分享
 
 
 

MiniGUI体系结构—多窗口管理和控件类

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

1 引言

在任何一个足够复杂的 GUI 系统中,处理窗口之间的互相剪切是其首要解决的问题。因为多窗口系统首先要确保一个窗口中的绘制输出不会影响到另外一个窗口。为此,GUI 系统一般要利用 Z 序来管理窗口之间的互相剪切关系。根据窗口在 Z 序中所处的位置,GUI 系统要计算每个窗口受剪切的区域,即剪切域。通常,窗口的剪切域定义为互不相交的矩形集合。GUI 系统的底层图形引擎在进行输出时,要根据当前输出的剪切域进行输出的剪切操作。从而保证窗口的绘制输出不会互相影响。因为任何一个窗口的创建、销毁、隐藏、显示均有可能影响其他窗口的剪切域,所以首先要有一个高效的剪切域维护算法。本文将详细描述 MiniGUI 中的剪切域生成算法。

许多人对控件(或者部件)的概念已经相当熟悉了。控件可以理解为主窗口中的子窗口。这些子窗口的行为和主窗口一样,即能够接收键盘和鼠标等外部输入,也可以在自己的区域内进行输出??只是它们的所有活动被限制在主窗口中。MiniGUI 也支持子窗口,并且可以在子窗口中嵌套建立子窗口。我们将 MiniGUI 中的所有子窗口均称为控件。

在 Windows 或 X Window 中,系统会预先定义一些控件类,当利用某个控件类创建控件之后,所有属于这个控件类的控件均会具有相同的行为和显示。利用这些技术,可以确保一致的人机操作界面,而对程序员来讲,可以像搭积木一样地组建图形用户界面。MiniGUI 使用了控件类和控件的概念,并且可以方便地对已有控件进行重载,使得其有一些特殊效果。比如,需要建立一个只允许输入数字的编辑框时,就可以通过重载已有编辑框而实现,而不需要重新编写一个新的控件类。

在多语种环境中,输入法是一个必不可少的模块。输入法提供了将标准键盘输入翻译为适当语种的文字的能力。MiniGUI 中也包含有标准的中文简体输入法,包括全拼、五笔和智能拼音等等。本文最后将介绍 MiniGUI 中的输入法模块实现。

2 窗口 Z 序

Z 序实际定义了窗口之间的层叠顺序。说起“Z 序”这个名称,实际是相对屏幕坐标而言的。一般而言,屏幕上的所有窗口均有一个坐标系,即原点在左上角,X 轴水平向右,Y 轴垂直向下的坐标系。Z 序就是相对于一个假想的 Z 轴而言的,这个 Z 轴从屏幕外指向屏幕内。窗口在这个 Z 轴上的值,就确定了其 Z 序。Z 序值大的窗口,覆盖了 Z 序值小的窗口。

当然,在程序当中,Z 序一般表示为一个链表。越接近于链表头的节点,其 Z 序值就越大。在 MiniGUI 中,我们维护了两个 Z 序。其中一个 Z 序永远位于另一个 Z 序之上。这样,就可以创建始终位于其他窗口之上的窗口,比如输入法窗口。如果在建立窗口时,指定了 WS_EX_TOPMOST 扩展属性,就可以创建这样的主窗口。因为 Z 序的操作实际就是链表的操作,这里就不再赘述。

3 窗口剪切算法

有了窗口 Z 序,我们就可以计算每个窗口的剪切域。我们把因为窗口 Z 序而产生的剪切域称为“全局剪切域”,这是相对于窗口自身定义的剪切域而言的,我们把后者称为“局部剪切域”。窗口中的所有输出,首先要受到全局剪切域的影响,其次受到局部剪切域的影响。我们在这里重点讲解窗口的全局剪切域的生成和维护。

3.1 全局剪切域的生成和维护

在 MiniGUI 中,剪切域表示为若干互不相交的矩形之并集,这些矩形称为剪切矩形。最初,屏幕上没有任何窗口时,桌面的剪切域由一个矩形组成,即屏幕矩形;当屏幕上只有一个窗口时,该窗口的剪切域由一个矩形组成,该矩形即为窗口在屏幕上的矩形,而桌面的剪切域却可能是由多个矩形组成的。图 1 说明了只有一个窗口时的桌面的剪切域组成。从图中可以看出,此时桌面的剪切域由四个矩形组成,分别是 A、B、C 和 D。如果窗口在桌面的位置变化为图 2 所示,则桌面的剪切域将由两个矩形组成(A和B)。

图 1 由四个矩形组成的桌面剪切域

图 2 由两个矩形组成的桌面剪切域

读者很容易看出,在只有一个窗口的情况下,形成桌面剪切域的矩形最多只能有四个。

此时,如果有一个新的窗口出现,则新的窗口将同时剪切旧的窗口和桌面(图 3。窗口的剪切矩形用空心矩形表示,而桌面的剪切矩形用实心矩形表示)。而这时,桌面和旧窗口的剪切域将多出一些矩形,这些矩形应该是原有剪切域中的每个矩形受到新窗口矩形影响之后生成的剪切矩形。同样,原有剪切域中的每个矩形只能最多只能派生出4个新剪切域,而某些矩形根本不会受到新窗口矩形的影响。

图 3 有新窗口被创建时,桌面和旧窗口的剪切域

这样,我们可以将某个窗口全局剪切域归纳为原有剪切域中排除(Exclude)某个矩形而生成的:

窗口的全局剪切域初始化为窗口矩形。

当窗口之上有其他窗口覆盖时,则该窗口的全局剪切域为排除新窗口矩形之后的剪切域。

沿 Z 序迭代第 2 步,直到最顶层窗口。

清单 1 中的代码是在显示一个新窗口时,MiniGUI 处理被该窗口所覆盖的其他所有窗口的代码。这段代码调用了剪切域维护接口中的 SubtractClipRect 函数计算新的剪切域。

清单 1

显示新窗口时计算被新窗口覆盖的窗口的全局剪切域

// clip all windows under this window.

static void clip_windows_under_this (ZORDERINFO* zorder, PMAINWIN pWin, RECT* rcWin)

{

PZORDERNODE pNode;

PGCRINFO pGCRInfo;

pNode = zorder-pTopMost;

while (pNode-hWnd != (HWND)pWin)

pNode = pNode-pNext;

pNode = pNode-pNext;

while (pNode)

{

if (((PMAINWIN)(pNode-hWnd))-dwStyle & WS_VISIBLE) {

pGCRInfo = ((PMAINWIN)(pNode-hWnd))-pGCRInfo;

pthread_mutex_lock (&pGCRInfo-lock);

SubtractClipRect (&pGCRInfo-crgn, rcWin);

pGCRInfo-age ++;

pthread_mutex_unlock (&pGCRInfo-lock);

}

pNode = pNode-pNext;

}

}

与排除矩形相反的操作是包含(Include)某个矩形到剪切域中。这个操作用于隐藏或者销毁某个窗口时。当一个窗口被隐藏或销毁时,该窗口之下的所有窗口将受到影响,此时,要将被隐藏或销毁窗口的矩形包含到这些受影响窗口的全局剪切域中。为此,MiniGUI 的剪切域维护接口中有一个函数专用于该类操作(IncludeClipRect)。为确保剪切域中矩形互不相交,该函数首先计算与每个剪切矩形的相交矩形,然后将自己添加到该剪切域中。

但是,在某些情况下,我们必须重新计算所有窗口的全局剪切域,比如在移动某个窗口时。

3.2 剪切矩形的私有堆

显然,在剪切域非常复杂,或者窗口非常多时,需要大量的矩形来表示每个窗口的全局剪切域。而在 C 程序中,如果频繁使用 malloc 和 free 申请和释放每个剪切矩形,将带来许多问题。第一,malloc 和 free 是非常耗时的操作;第二,频繁的 malloc 和 free 将导致 C 程序堆的碎片化,从而可能导致将来的内存分配失败。为了避免频繁使用 malloc 和 free,MiniGUI 在初始化时,建立了一个私有的堆。我们可以直接从这个堆中分配剪切矩形,而不需要从进程的全局堆中分配剪切矩形。这个私有堆实际是由一些空闲待用的剪切矩形组成的。每次分配时返回该链表的头节点,而在释放时放进该链表的尾节点。如果该链表为空,则利用 malloc 从进程的全局堆中分配剪切矩形。清单 2 说明了这个私有堆的初始化和操作。

清单 2

从剪切矩形私有堆中分配和释放剪切矩形

PCLIPRECT GUIAPI ClipRectAlloc(PFREECLIPRECTLIST pList)

{

PCLIPRECT pRect;

#ifndef _LITE_VERSION

pthread_mutex_lock (&pList-lock);

#endif

if (pList-head) {

pRect = pList-head;

pList-head = pRect-next;

}

else {

if (pList-free size) {

pRect = pList-heap + pList-free;

pRect-fromheap = TRUE;

pList-free ++;

}

else {

pRect = malloc (sizeof(CLIPRECT));

if (pRect == NULL)

fprintf (stderr, "GDI error: alloc clip rect failure!\n");

else

pRect-fromheap = FALSE;

}

}

#ifndef _LITE_VERSION

pthread_mutex_unlock (&pList-lock);

#endif

return pRect;

}

void GUIAPI FreeClipRect(PFREECLIPRECTLIST pList, CLIPRECT* pRect)

{

#ifndef _LITE_VERSION

pthread_mutex_lock (&pList-lock);

#endif

pRect-next = NULL;

if (pList-head) {

pList-tail-next = (PCLIPRECT)pRect;

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有