zoukankan      html  css  js  c++  java
  • 《30天自制操作系统》11_day_学习笔记

    harib08a:
      鼠标的显示问题:我们可以看到,鼠标移到窗口最右侧之后就不能再移动了,而WIN中,鼠标是可以移动到最右边隐藏起来的。怎么办?把鼠标指针显示的范围扩宽就行!我们来修改一下HariMain来解决这个问题

    //HariMain节选
    if (mx > binfo->scrnx - 1) {    mx = binfo->scrnx - 1;    }//拓宽到右边界左边的一个像素
    if (my > binfo->scrny - 1) {    my = binfo->scrny - 1;    }//拓宽到下边界上方的一个像素

    harib08b:
      我们发现图层到了画面外,就会出现问题。这里我们一起来吧sheet_refreshsub()函数进行改进,让它不刷新画面以外的内容。
      方   法:在进行画面刷新之前,先判断是否已经超过了画面以外,超过画面外的部分不需要进行刷新操作(下面是关键部分的代码)

    //sheet.c中sheet_refreshsub()节选
    //判断,刷新像素的范围是否超出了画面
    if (bx0 < 0) { bx0 = 0; }
    if (by0 < 0) { by0 = 0; }
    if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }
    if (by1 > sht->bysize) { by1 = sht->bysize; }

    harib08c:
      我们发现在图层移动sheet_undown()中指定ctl太麻烦,于是在这一部分对此做了一些修改,目的就是不在sheet_undown()中指定ctl.
      1、struct SHEET中加入struct SHTCTL *ctl

    //在图层的结构体定义struct SHEET中,加入图层控制结构体指针struct SHTCTL *ctl
    struct SHEET {
        unsigned char *buf;
        int bxsize, bysize, vx0, vy0, col_inv, height, flags;
        struct SHTCTL *ctl; //图层控制结构体指针struct SHTCTL *ctl
    };

      2、对函数shtclt_init进行追加

    //追加内容: 将ctl初始化为图层控制结构体
    for (i = 0; i < MAX_SHEETS; i++) {
      ctl->sheets0[i].flags = 0; /* FLAG=0表示该图层未被使用 */
      ctl->sheets0[i].ctl = ctl; /* 指针ctl指向图层控制结构体 */
    }

      3、对函数sheet_undown进行修改

    //修改内容:将ctl初始化为图层控制结构体
    void sheet_updown(struct SHEET *sht, int height)
    {
        struct SHTCTL *ctl = sht->ctl;/* 指针ctl指向图层控制结构体 */
        int h, old = sht->height;    /* 记录设置前图层的高度 */
        //........
    }

      4、修改sheet_refresh()、sheet_slide()、sheet_free()

    //一、修改函数的参数
    //二、如果函数中使用了图层结构体SHEET,将结构体中指针ctl指向图层控制结构体
    void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1)
    {
        if (sht->height >= 0) { /* 此时正在显示,按照新图层的信息进行刷新 */
            sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1);/* 这里修改了对sheet_refreshsub调用 */
        }
        return;
    }
    void sheet_slide(struct SHEET *sht, int vx0, int vy0)
    {
        int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
        sht->vx0 = vx0;
        sht->vy0 = vy0;
        if (sht->height >= 0) { /* 此时正在显示,按照新图层的信息进行刷新 */
            sheet_refreshsub(sht->ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);/* 这里修改了对sheet_refreshsub调用 */
            sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize);/* 这里修改了对sheet_refreshsub调用 */
        }
        return;
    }
    void sheet_free(struct SHEET *sht)
    {
        if (sht->height >= 0) {
            sheet_updown(sht, -1); /* 这里修改了对sheet_updown的调用,隐藏该图层 */
        }
        sht->flags = 0;       /* 图层被释放,flag置0 */
        return;
    }

    harib08d:
      到这里,优化工作完成的差不多了,接下来继续对系统进行拓展;接下来我们尝试制作一下窗口。
      原   理:准备一张图层,在该图层的缓冲区描绘一个窗口的图像,接着把该缓冲区写到VRAM中,让界面刷新像素显示即可。

      1、窗口图层制作函数make_windows8()

    void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
    {
        static char closebtn[14][16] = { //窗口关闭按钮X的的数组
            "OOOOOOOOOOOOOOO@",
            "OQQQQQQQQQQQQQ$@",
            "OQQQQQQQQQQQQQ$@",
            "OQQQ@@QQQQ@@QQ$@",
            "OQQQQ@@QQ@@QQQ$@",
            "OQQQQQ@@@@QQQQ$@",
            "OQQQQQQ@@QQQQQ$@",
            "OQQQQQ@@@@QQQQ$@",
            "OQQQQ@@QQ@@QQQ$@",
            "OQQQ@@QQQQ@@QQ$@",
            "OQQQQQQQQQQQQQ$@",
            "OQQQQQQQQQQQQQ$@",
            "O$$$$$$$$$$$$$$@",
            "@@@@@@@@@@@@@@@@"
        };
        int x, y;
        char c;
        //窗口的颜色填充,(应该是前景)
        boxfill8(buf, xsize, COL8_C6C6C6, 0,         0,         xsize - 1, 0        );
        boxfill8(buf, xsize, COL8_FFFFFF, 1,         1,         xsize - 2, 1        );
        boxfill8(buf, xsize, COL8_C6C6C6, 0,         0,         0,         ysize - 1);
        boxfill8(buf, xsize, COL8_FFFFFF, 1,         1,         1,         ysize - 2);
        boxfill8(buf, xsize, COL8_848484, xsize - 2, 1,         xsize - 2, ysize - 2);
        boxfill8(buf, xsize, COL8_000000, xsize - 1, 0,         xsize - 1, ysize - 1);
        boxfill8(buf, xsize, COL8_C6C6C6, 2,         2,         xsize - 3, ysize - 3);
        boxfill8(buf, xsize, COL8_000084, 3,         3,         xsize - 4, 20       );
        boxfill8(buf, xsize, COL8_848484, 1,         ysize - 2, xsize - 2, ysize - 2);
        boxfill8(buf, xsize, COL8_000000, 0,         ysize - 1, xsize - 1, ysize - 1);
        putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);//显示窗口的额标题
        for (y = 0; y < 14; y++) {
            for (x = 0; x < 16; x++) {              //这里对关闭按钮的颜色进行设定
                c = closebtn[y][x];               //关闭按钮数组中不同的字符用特定的显示颜色来显示出来
                if (c == '@') {
                    c = COL8_000000;
                } else if (c == '$') {
                    c = COL8_848484;
                } else if (c == 'Q') {
                    c = COL8_C6C6C6;
                } else {
                    c = COL8_FFFFFF;
                }
                buf[(5 + y) * xsize + (xsize - 21 + x)] = c;//把这个颜色的值,给VRAM缓冲区
            }
        }
        return;
    }

      2、在HariMain中进行相应的修改

        //HariMain节选
        init_palette();                            //初始化调色板
        shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);
        sht_back  = sheet_alloc(shtctl);
        sht_mouse = sheet_alloc(shtctl);
        sht_win   = sheet_alloc(shtctl);                  //为窗口分配内存空间
        buf_back  = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);
        buf_win   = (unsigned char *) memman_alloc_4k(memman, 160 * 68);
        sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); /* 设置图层缓冲区,没有透明色 */
        sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);
        sheet_setbuf(sht_win, buf_win, 160, 68, -1);            /* 设置图层缓冲区,没有透明色 */
        init_screen8(buf_back, binfo->scrnx, binfo->scrny);       //初始化屏幕界面
        init_mouse_cursor8(buf_mouse, 99);                 //X按钮的功能键
        make_window8(buf_win, 160, 68, "window");             //制作窗口图层buf_win
        putfonts8_asc(buf_win, 160, 24, 28, COL8_000000, "Welcome to");//在图层buf_win中显示两串字符串
        putfonts8_asc(buf_win, 160, 24, 44, COL8_000000, "  Haribote-OS!");
        sheet_slide(sht_back, 0, 0);                    //图层上下滑动的函数,因为要让鼠标图层在窗口图层上方移动
        mx = (binfo->scrnx - 16) / 2;                     /* 这里把鼠标放在屏幕中间的位置 */
        my = (binfo->scrny - 28 - 16) / 2;
        sheet_slide(sht_mouse, mx, my);                  //设置鼠标图层的上下滑动
        sheet_slide(sht_win, 80, 72);                   //设置窗口图层sht_win的上下滑动
        sheet_updown(sht_back,  0);                    //设定背景图层的高度为0(在最底层)
        sheet_updown(sht_win,   1);                    //设置窗口图层的高度为1(在中间层)
        sheet_updown(sht_mouse, 2);                    //设置鼠标图层的高度为2(在最上层)
        sprintf(s, "(%3d, %3d)", mx, my);                 //在背景层打印出坐标的位置
        putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
        sprintf(s, "memory %dMB   free : %dKB",             //打印出内存使用情况
              memtotal / (1024 * 1024), memman_total(memman) / 1024);
        putfonts8_asc(buf_back, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
        sheet_refresh(sht_back, 0, 0, binfo->scrnx, 48);        //刷新屏幕显示

    harib08e:
        小实验:我们在上面一共有了3个图层:背景、窗口、鼠标。让我们抽一下风把这三个图层的高度改一下,看看会怎么样?我们把鼠标图层放在窗口下面,背景图层在最底下(上图运行成功,说明程序没问题)

    //修改HariMain中的图层高度设置
        sheet_updown(sht_back,  0);//背景最底层
        sheet_updown(sht_mouse, 1);//鼠标中间层
        sheet_updown(sht_win,   2);//窗口最高层

    harib08f:
        我们已经能顺利的制作出窗口了,现在我们来做个好玩的东西---计数器。在上一步,我们已经成功的绘制出了窗口,接下来只需要对窗口图层的内容做一些修改(这部分比较简单,直接上代码)

        //HariMain节选
        make_window8(buf_win, 160, 52, "counter");  //窗口标题:counter
        sheet_slide(sht_back, 0, 0);          //设置背景图层的上下滑动
        mx = (binfo->scrnx - 16) / 2;          /* 把鼠标放在屏幕中间 */
        my = (binfo->scrny - 28 - 16) / 2;
        sheet_slide(sht_mouse, mx, my);        //鼠标图层的上下滑动
        sheet_slide(sht_win, 80, 72);         //窗口图层的上下滑动
        sheet_updown(sht_back,  0);           //图层的高度
        sheet_updown(sht_win,   1);
        sheet_updown(sht_mouse, 2);
        sprintf(s, "(%3d, %3d)", mx, my);       //输出坐标位置
        putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
        sprintf(s, "memory %dMB   free : %dKB",
                memtotal / (1024 * 1024), memman_total(memman) / 1024);
        putfonts8_asc(buf_back, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
        sheet_refresh(sht_back, 0, 0, binfo->scrnx, 48);//刷新显示界面

    harib08g:
      我们make run一下,发现窗口中的内容在不断的闪烁。到这里位置,我们每次刷新显示都是从最低图层开始,一直向上把所有的图层都王VRAM中写一遍。但是很多图层的内容是没有变化的(比如背景图层);在这里我们做以下消除闪烁的优化处理:仅对refresh对象及以上的图层进行刷新就行了。
      1、修改完成刷新工作的sheet_refreshsub()函数:追加一个参数h0表示需要刷新的最低图层。这样在循环处加一个判断条件:for (h = h0; h <= ctl->top; h++)只对层数比h0高的图层进行刷新;
      2、修改相关的函数:上面我们对sheet_refreshsub()函数追加了h0参数,接下来我们把所有调用了sheet_refreshsub()的函数都修改一下。

        sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height);

    harib08h:
      上面我们已经消除了窗口刷新时的闪烁,接下来我们来消除鼠标的闪烁!

          

      1、map(地图)的定义和初始化:开辟一块和VRAM大小一样的内存--map;这块内存用来表示画面上的点是那个图层的像素,所以相当于是图层的地图。我们要理解作者的用意:map和VRAM设计成大小一样的,就是要把map当做一个中间的缓冲区域(相当于VRAM的一个缓存),这样可以对VRAM的操作进行加速。

    //bootpack.h和sheet.c节选
    //定  义:map定义在图层管理结构体中
    struct SHTCTL {
        unsigned char *vram, *map;
        int xsize, ysize, top;
        struct SHEET *sheets[MAX_SHEETS];
        struct SHEET sheets0[MAX_SHEETS];
    };
    //初始化:在图层管理初始化函数中做相应修改
    struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
    {
        //...函数节选:    ................
                           //开辟一个4KB的内存空间,map指向这个空间起始地址
        ctl->map = (unsigned char *) memman_alloc_4k(memman, xsize * ysize);
        if (ctl->map == 0) {       //空间开辟失败,报错前先释放
            memman_free_4k(memman, (int) ctl, sizeof (struct SHTCTL));
            goto err;
        }
        ctl->vram = vram;         //图层管理结构体其他变量的初始化
        ctl->xsize = xsize;
        ctl->ysize = ysize;
        ctl->top = -1;           /* 隐藏,表示现在没有任何图层 */
        for (i = 0; i < MAX_SHEETS; i++) {
            ctl->sheets0[i].flags = 0; /* 图层标识FLAG=0表示该图层没有被使用 */
            ctl->sheets0[i].ctl = ctl; /* 初始化图层ctl为图层管理结构体的地址 */
        }
    err:
        return ctl;
    }

      2、在map中写入图层号码:我们用当前图层的地址(sht)减去第一块图层的地址(ctl->sheets0)的偏移量来表示该图层的号码sid(sheet ID)
      图片P215的图片来一个;

    void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
    {
        int h, bx, by, vx, vy, bx0, by0, bx1, by1;
        unsigned char *buf, sid, *map = ctl->map;
        struct SHEET *sht;
        if (vx0 < 0) { vx0 = 0; }
        if (vy0 < 0) { vy0 = 0; }
        if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }
        if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }
        for (h = h0; h <= ctl->top; h++) {
            sht = ctl->sheets[h];
            //前图层的地址(sht)减去第一块图层的地址(ctl->sheets0)的偏移量
            sid = sht - ctl->sheets0; /* 将进行减法运算的地址作为图层号码使用 */
            buf = sht->buf;
            bx0 = vx0 - sht->vx0;
            by0 = vy0 - sht->vy0;
            bx1 = vx1 - sht->vx0;
            by1 = vy1 - sht->vy0;
            if (bx0 < 0) { bx0 = 0; }
            if (by0 < 0) { by0 = 0; }
            if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }
            if (by1 > sht->bysize) { by1 = sht->bysize; }
            for (by = by0; by < by1; by++) {
                vy = sht->vy0 + by;
                for (bx = bx0; bx < bx1; bx++) {
                    vx = sht->vx0 + bx;
                    if (buf[by * sht->bxsize + bx] != sht->col_inv) {
                        //在对需要刷新的部分进行刷新时,不再是写到VRAM中,而是将该像素的sid写到map中
                        map[vy * ctl->xsize + vx] = sid;
                    }
                }
            }
        }
        return;
    }

      3、对sheet_refreshsub进行改写,让它可以使用map

    void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
    {
        int h, bx, by, vx, vy, bx0, by0, bx1, by1;
        unsigned char *buf, *vram = ctl->vram, *map = ctl->map, sid;
        struct SHEET *sht;
        /* refresh刷新的范围超出了画面 */
        //.......................
        for (h = h0; h <= h1; h++) { //只对h0到h1的图层进行跟新操作
            sht = ctl->sheets[h];  //当前图层的地址
            buf = sht->buf;      //当前图层的缓冲区内容
            sid = sht - ctl->sheets0;//计算当前图层sid的值
            /* 倒推出刷新的范围(不懂看前一天的内容) */
            bx0 = vx0 - sht->vx0;
            by0 = vy0 - sht->vy0;
            bx1 = vx1 - sht->vx0;
            by1 = vy1 - sht->vy0;
            if (bx0 < 0) { bx0 = 0; }
            if (by0 < 0) { by0 = 0; }
            if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }
            if (by1 > sht->bysize) { by1 = sht->bysize; }
            for (by = by0; by < by1; by++) {
                vy = sht->vy0 + by;
                for (bx = bx0; bx < bx1; bx++) {
                    vx = sht->vx0 + bx;
                    //只有当前sid和map中该像素处sid相等,才将缓存写到VRAM中
                    if (map[vy * ctl->xsize + vx] == sid) {
                        vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
                    }
                }
            }
        }
        return;
    }

       4、修改调用了sheet_refreshsub()的3个函数:在函数sheet_refresh()sheet_slide()sheet-updown中都对函数sheet_refreshsub()有调用,我们找到这三个函数的相应位置,按照sheet_refreshsub()新的参数,重写一下调用.

  • 相关阅读:
    2016计算机专业考研:c++大学教程第二版目录(下)
    2016考研计算机考研备考书籍推荐
    2014年计算机考研真题重点选择题部分真题及解析
    2016计算机考研:数据结构常用算法精析
    2015考研计算机专业基础综合试题真题及答案(1)
    java串讲之String 如何定义字符串对象
    WindowsService
    sql 将datetime类型转化为指定格式的字符串
    C# 利用SMTP异步发送邮件
    Visual Studio
  • 原文地址:https://www.cnblogs.com/pengfeiz/p/5801717.html
Copyright © 2011-2022 走看看