本篇主要解决两个问题。一是扩充上一篇内存管理部分的功能,实现一种按4KB大小分配内存的函数;二是解决鼠标显示中鼠标覆盖任务栏的问题。
1. 以4KB大小为单位分配内存的函数
如果每次分配内存都需要按字节去分配,效率还是比较低的,可用内存也会变得越来越零碎。这里作者引入一种以4KB大小为单位进行内存分配的函数。
首先要根据应用程序所需的内存大小,进行4KB向上取整,也就是不足4KB的部分用4KB补足。相应的代码如下:
unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size)
{
unsigned int a;
size = (size + 0xfff) & 0xfffff000;
a = memman_alloc(man, size);
return a;
}
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size)
{
int i;
size = (size + 0xfff) & 0xfffff000;
i = memman_free(man, addr, size);
return i;
}
可以看出对于4KB(0x1000)的向上取整,采用的方法是(size + 0xfff) & 0xfffff000
用十进制来举例子就很简单,比如以10为单位向上舍入,123舍入后就是130,这个很好理解。
2. 图层的叠加处理
引入了以4KB为单位进行内存分配的函数之后,本篇的重点是完成叠加处理。从鼠标和任务栏的叠加扩展开去,扩展到多个不同图层的叠加显示。这部分确实比较复杂,如果没明白的话,还是需要认认真真地多看几遍。
2.1 图层概念的引入
为了讲解叠加处理,需要引入图层的概念。这里作者引入的图层概念就是一张图片,除了有图案的地方,背景色是透明的。而多个窗口叠加在一起,可能是多个大小不同的图层的叠放。最上面的小图层用来描绘鼠标,下面依次是叠加在一起的不同图层,而最下面则是桌面背景。另外还需要实现窗口的移动。
首先考虑描述一个图层信息的数据结构:
struct SHEET {
unsigned char *buf;
int bxsize, bysize, vx0, vy0, col_inv, height, flags;
};
这个结构体中,buf用来存放图层上的图案内容。bxsize,bysize用于描述图层的大小,vx0和vy0用来描述图层在整个显示画面中的位置,col_inv表示图层的透明色色号,height表示图层的高度,flags用于存放有关图层设置的信息。
这样一个图层就可以通过这个结构体进行描述了。
当然,只有一个图层,我们无法实现叠加处理,还需要一个管理多个图层信息的结构体:
#define MAX_SHEETS 256
struct SHTCTL {
unsigned char *vram;
int xsize, ysize, top;
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};
SHTCTL这个结构体用于管理图层。其中MAX_SHEETS是最大图层数,设置为256。结构体中,vram表示显存地址,xsize与ysize表示画面的大小,初始化时就从BOOTINFO中获取到并存起来。top用来表示最上面图层的高度。sheets0是一个结构体数组,用于保存图层的信息;而sheets是一个结构体指针数组。sheets0中存放的图层信息是乱序的,而通过sheets结构体指针数组将经过升序处理后的图层信息保存起来。
2.2 图层叠加处理
具备了这些数据结构之后,我们开始编写程序。首先需要对图层管理的数据结构进行初始化:
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{
struct SHTCTL *ctl;
int i;
ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));
if (ctl == 0) {
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; /* 图层标记为未使用 */
}
err:
return ctl;
}
这段程序的内容比较清楚。首先是通过内存分配函数给图层控制结构体分配足够的内存,通过sizeof(struct SHTCTL)来由编译器计算所需的内存大小。这个内存大小超过了4KB,所以用前面的内存分配函数来分配内存很合适。接下来将显存地址、画面大小等信息存放在结构体中。将top设置为-1,表示当前没有显示的图层;将sheets0数组中所有元素的flags成员置为0,表示当前图层都没有使用。
初始化之后,是分配空闲图层的函数:
#define SHEET_USE 1
struct SHEET *sheet_alloc(struct SHTCTL *ctl)
{
struct SHEET *sht;
int i;
for (i = 0; i < MAX_SHEETS; i++) {
if (ctl->sheets0[i].flags == 0) {
sht = &ctl->sheets0[i];
sht->flags = SHEET_USE; /* 将图层标记为使用 */
sht->height = -1; /* 表示当前图层隐藏 */
return sht;
}
}
return 0; /* 走到这里说明所有图层都处于使用状态,没有空闲图层 */
}
从图层管理结构体的sheets0数组中寻找未使用的图层,标记为使用,将height先设置为-1表示隐藏。
获取空闲图层之后,就可以根据实际情况对图层的参数进行设置,这也没什么难点。
void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv)
{
sht->buf = buf;
sht->bxsize = xsize;
sht