注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

miniGUI笔记  

2016-05-26 21:43:58|  分类: 计算机图形学 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、编译环境
由于Xorg的代码过于庞大,很难看到一个图形系统的内部实现,所以从国产的开源软件miniGUI来入手。在软件的官方网站下载了最新的软件包MiniGUI Core Lib (V3.0.12 )。由于现在大部分的系统都已经是64位,所以自然在fedora core的64位环境下编译,但是有很多问题,可以认为这个版本是不支持64环境编译的。我自己简单调整了一些编译和运行时的问题,最终使用xvfb在系统上启动了扫雷程序,不过其它的例子程序很多都无法启动。记得主要的问题是里面HANDLE类型都是使用的int,在64位环境下,int不足以表示一个指针,所以在函数传递过程中HANDLE无法还原为指针;还有对大小端识别造成的问题等。使用-m32选项来编译的时候也会有其它的问题,通常的64位系统很少再包含32位的头文件和lib库,所以这样通过这种方式希望在64位环境下编译启动也不推荐,所以看代码的时候还是使用了sourceforge上的1.6版本和32位的redhat6.6 企业版,这种组合编译调试比较简单,而且1.6的版本和软件作者的介绍文章比较吻合,代码也更加简单直观,接近系统的核心。
下面是编译中遇到的一些问题,在安装虚拟机系统之后,最好把安装盘放在虚拟机的光驱中,这样需要软件的时候可以随时从光盘中安装(当然联网安装也可以)。通过下面步骤将IOS安装盘挂在到虚拟机系统中
在 设置 -->>硬件-->>(cd/dvd SATA)选项卡 “设备状态” 选中 “已连接” “启动时连接”  
qvfb configure的错误
configure: error: C++ preprocessor "/lib/cpp" fails sanity check
安装/media/RHEL-6.6 Server.i386/Packages/gcc-c++-4.4.7-11.el6.i686.rpm,

checking for X... configure: error: Can't find X includes. Please check your installation and add the correct paths!
同样方法,安装/media/RHEL-6.6 Server.i386/Packages/qt3-devel-3.3.8b-30.el6.i686.rpm,然后执行./configure --with-qt-dir=/usr/lib/qt-3.3/
 minigui的安装
tar xvf libminigui-1.6.10.tar.gz
 cd libminigui-1.6.10
./configure
make
make install

tar xvf mg-samples-1.6.10.tar.gz
cd mg-samples-1.6.10
./configure
make && make install

tar xvf minigui-res-1.6.10.tar.gz
cd minigui-res-1.6.10
make install

tsecer@harry: which qvfb
/usr/local/bin/qvfb

tsecer@harry:  ./mywins 
./mywins: error while loading shared libraries: libmgext-1.6.so.10: cannot open shared object file: No such file or directory
tsecer@harry:  ll /usr/local/lib/lib
libmgext-1.6.so.10        libminigui-1.6.so.10      libvcongui-1.6.so.10
libmgext-1.6.so.10.0.0    libminigui-1.6.so.10.0.0  libvcongui-1.6.so.10.0.0
……
tsecer@harry:  echo /usr/local/lib >  /etc/ld.so.conf.d/minigui.conf 
tsecer@harry:  ldconfig
tsecer@harry:  ./mywins 
NEWGAL: Video mode smaller than requested.
NEWGAL: Set video mode failure.
InitGUI: Can not initialize graphics engine!
tsecer@harry:  
在启动的qvfb==>>File==>>Configure==>>640*480 "VGA"
点击 OK 
tsecer@harry:  cat /usr/local/etc/MiniGUI.cfg 
# MiniGUI Ver 2.0.3/1.6.9
# This configuration file is for classic window style.
#
# Copyright (C) 2002~2007 Feynman Software
# Copyright (C) 1998~2002 Wei Yongming.
……
[qvfb]
defaultmode=640x480-16bpp
display=0
这个配置和qvfb启动时的输出一致

tsecer@harry: qvfb
Using display 0
二、一个最简单的问题,鼠标点击事件由谁处理
然后启动自带例子中的mg-samples-1.6.10\src\mywins.c例子,单单这个界面,就有很多令人感兴趣的东西,例如鼠标放在窗口的header地方可以拖动窗口,窗口可以互相剪切(例如ProcessWin Example和menu按钮界面之间的剪切)。不过这些作为入门,看起来还是有点复杂,所以我们就只关注下一个更基础的、从实现上来看不是很直观(也就是需要做特殊处理)的现象,就是其中的Menu按钮弹出的对话框是优先级最高的,它会拦截鼠标点击操作,比方说,当Menu对话框弹出之后,鼠标在主窗口可见区域内点击的时候并不会被主窗口显示,这一点对于我们习惯了windows的实现的人来说觉得这个可能比较稀松平常。但是对于我这样的入门者来看,这个的确还是有点神奇的。因为只管上说,顶层Menu窗口的图形并没有覆盖到鼠标点击区域,当我点击鼠标的时候,此时鼠标所在的坐标是属于主窗口的,直观上说(我们只考虑图形的覆盖区域的话),这个事件应该由主窗口来处理,被逻辑上位于前端的Menu对话框处理毕竟是需要有一些处理。直观的想法是当一个鼠标事件过来之后,首先要判断当前zorder上最上层的窗口,如果存在这样一个窗口,继续判断这个点击是否落在来顶层窗口之中,如果有则给窗口发送消息,如果没有则丢弃掉这个消息,我们就先看下这个如何实现。
minigui1.6\source\libminigui-1.6.10\src\kernel\init.c
static void ParseEvent (PLWEVENT lwe)
{
……
        Msg.wParam = me->status;
        Msg.lParam = MAKELONG (me->x, me->y);

        switch (me->event) {
        case ME_MOVED:
            Msg.message = MSG_MOUSEMOVE;
            break;
        case ME_LEFTDOWN:
            Msg.message = MSG_LBUTTONDOWN;
            break;
……
        QueueDeskMessage (&Msg);
    }
}
消息首先是发给了桌面线程。
三、对于左键点击的消息
这个地方的处理逻辑在这里,消息首先被派发到了mainwindows中,此时虽然是弹出窗口Menu遮挡在了最前面,但是通过GetMainWindowPtrUnderPoint并没有找到这个子窗口,而是首先找到了不会相应这个消息的主窗口
(gdb) bt
#0  MouseMessageHandler (message=1, flags=4096, x=157, y=12)
    at desktop-comm.c:1853
#1  0x00237c06 in DesktopWinProc (hWnd=3076384, message=1, wParam=4096, 
    lParam=786589) at desktop-comm.c:2952
#2  0x0023858e in DesktopMain (data=0xbff3dfa0) at desktop-ths.c:165
#3  0x008ceb39 in start_thread () from /lib/libpthread.so.0
#4  0x00713c1e in clone () from /lib/libc.so.6
(gdb) p pCtrlPtrIn->spCaption
$3 = 0xb776e008 "Examples for mywindow interfaces"
(gdb) 
1839            if (_mgs_button_down_main_window) {
(gdb) 
1848            else if (pUnderPointer) {
(gdb) 
1849                if (__mg_ptmi)
(gdb) 
1852                if (pUnderPointer->dwStyle & WS_DISABLED) {
(gdb) 
1853                    Ping ();
(gdb) p/x pUnderPointer->dwStyle
$5 = 0x2cc00000
(gdb) 
#define WS_CAPTION          0x20000000L
#define WS_DISABLED         0x04000000L
#define WS_VISIBLE          0x08000000L
#define WS_BORDER           0x00400000L
#define WS_DLGFRAME         0x00800000L
这说明,当鼠标点击之后,窗口系统的确是精确的找到了这个主窗口,并将消息发送给了这个窗口,不过由于这个窗口是处于禁止状态,也就是pUnderPointer->dwStyle中的WS_DISABLED状态被置位,这样我们就会听到“噔”的一声,有可能当前的主窗口还会调皮的抖动一下。
四、窗口什么时候被置位WS_DISABLED
static int WindowMessageHandler(int message, PMAINWIN pWin, LPARAM lParam)
{
……
     case MSG_ENABLEMAINWIN:
            iRet = !(pWin->dwStyle & WS_DISABLED);

            if ( (!(pWin->dwStyle & WS_DISABLED) && !lParam)
                    || ((pWin->dwStyle & WS_DISABLED) && lParam) ) {
                if (lParam)
                    pWin->dwStyle &= ~WS_DISABLED;
                else
                    pWin->dwStyle |=  WS_DISABLED;
……
}

在子窗口创建的时候,直接通过EnableWindow (hOwner, FALSE)函数将父窗口设置为禁用状态
(gdb) bt
#0  EnableWindow (hWnd=152419480, fEnable=0) at window.c:3838
#1  0x0021737f in DialogBoxIndirectParam (pDlgTemplate=0xbff3dd60, 
    hOwner=152419480, DlgProc=0x9f5f54 <WinMenuBoxProc>, lParam=3220430156)
    at dialog.c:163
#2  0x009f669f in myWinMenu (hParentWnd=152419480, title=0x80493a8 "Menu", 
    label=0x804984f "Items", width=80, listboxheight=200, items=0x804ada0, 
    listItem=0x804ae58, buttons=0x804adc0) at mywindows.c:753
#3  0x08048d93 in my_notif_proc (hwnd=152727152, id=109, nc=0, add_data=0)
    at mywins.c:388
#4  0x001df1f3 in NotifyParentEx (hwnd=152727152, id=109, code=0, add_data=0)
    at ctrlmisc.c:32
#5  0x001e3aa6 in ButtonCtrlProc (hWnd=152727152, uMsg=2, wParam=1024, 
    lParam=14745703) at button.c:1466
#6  0x0023b718 in DispatchMessage (pMsg=0xbff3df60) at message.c:930
#7  0x0021744e in DialogBoxIndirectParam (pDlgTemplate=0x804ab20, 
    hOwner=3076384, DlgProc=0x8048de8 <DialogBoxProc2>, lParam=0)
    at dialog.c:181
#8  0x0804920f in MiniGUIAppMain (argc=1, argv=0xbff3e0c4) at mywins.c:508
#9  0x080491b4 in main (args=1, argv=0xbff3e0c4) at mywins.c:497
(gdb) 

五、子窗口关闭之后新窗口的显示
1、设置主窗口使能状态的调用流程
(gdb) bt
#0  EnableWindow (hWnd=152419480, fEnable=1) at window.c:3838
#1  0x0021751a in EndDialog (hDlg=152727968, endCode=211) at dialog.c:209
#2  0x009f609a in WinMenuBoxProc (hDlg=152727968, message=288, wParam=211, 
    lParam=152729728) at mywindows.c:610
#3  0x0023b718 in DispatchMessage (pMsg=0xbff3dca0) at message.c:930
#4  0x0021744e in DialogBoxIndirectParam (pDlgTemplate=0xbff3dd60, 
    hOwner=152419480, DlgProc=0x9f5f54 <WinMenuBoxProc>, lParam=3220430156)
    at dialog.c:181
#5  0x009f669f in myWinMenu (hParentWnd=152419480, title=0x80493a8 "Menu", 
    label=0x804984f "Items", width=80, listboxheight=200, items=0x804ada0, 
    listItem=0x804ae58, buttons=0x804adc0) at mywindows.c:753
#6  0x08048d93 in my_notif_proc (hwnd=152727152, id=109, nc=0, add_data=0)
    at mywins.c:388
#7  0x001df1f3 in NotifyParentEx (hwnd=152727152, id=109, code=0, add_data=0)
    at ctrlmisc.c:32
#8  0x001e3aa6 in ButtonCtrlProc (hWnd=152727152, uMsg=2, wParam=1024, 
    lParam=15073379) at button.c:1466
#9  0x0023b718 in DispatchMessage (pMsg=0xbff3df60) at message.c:930
#10 0x0021744e in DialogBoxIndirectParam (pDlgTemplate=0x804ab20, 
    hOwner=3076384, DlgProc=0x8048de8 <DialogBoxProc2>, lParam=0)
    at dialog.c:181
#11 0x0804920f in MiniGUIAppMain (argc=1, argv=0xbff3e0c4) at mywins.c:508
#12 0x080491b4 in main (args=1, argv=0xbff3e0c4) at mywins.c:497
2、显示层面的处理
当管理状态设置后,需要对显示层面的界面也要做些特殊处理,在EndDialog函数中,和EnableWindow几乎工整对仗的ShowWindow将会处理图形剪切及刷新逻辑。
EndDialog===>>ShowWindow===>>WindowMessageHandler===>>dskMoveToTopMost===>>dskUpdateGCRInfoOnShowNewMainWin
// When show a main window, all main windows which are covered by
// this showing main window will be updated.
// 
// Window functions which lead to calling this function:
// ShowWindow: show an invisible main window with a SW_SHOW parameter.
//
// this is a normal main window.
static void dskUpdateGCRInfoOnShowMainWin (MAINWIN* pWin)
{
    RECT rcWin;
    
    dskGetWindowRectInScreen (pWin, &rcWin);

    start_clip_window (pWin);
    reset_window (pWin, &rcWin);

    if (pWin->dwExStyle & WS_EX_TOPMOST) {
        clip_by_all_above_this (&sg_TopMostWinZOrder, pWin);
    }
    else {
//可怜剃头者,人亦剃其头。遮挡在自己Z序之下的窗口,同时被在自己前面的窗口剪切。
        clip_by_windows (&sg_TopMostWinZOrder, pWin);
        clip_by_all_above_this (&sg_MainWinZOrder, pWin);
    }

    end_clip_window (pWin);

    if (pWin->dwExStyle & WS_EX_TOPMOST) {
        clip_windows_under_this (&sg_TopMostWinZOrder, pWin, &rcWin);
        clip_windows (&sg_MainWinZOrder, &rcWin);
    }
    else {
        clip_windows_under_this (&sg_MainWinZOrder, pWin, &rcWin);
    }

    clip_desktop (&rcWin);
}

static void clip_by_all_above_this (ZORDERINFO* zorder, PMAINWIN pWin)
{
    PZORDERNODE pNode;
    PGCRINFO pGCRInfo = pWin->pGCRInfo;
    PMAINWIN pTemp;
    RECT rcTemp;
    
    pNode = zorder->pTopMost;
    while (pNode)
    {
        if (pNode->hWnd == (HWND)pWin)
            break;
            
        pTemp = (PMAINWIN)(pNode->hWnd);
        if (pTemp->dwStyle & WS_VISIBLE) {
            dskGetWindowRectInScreen (pTemp, &rcTemp);
            SubtractClipRect (&pGCRInfo->crgn, &rcTemp);
        }

        pNode = pNode->pNext;
    }
}
// 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;

    if (pWin) {
        while (pNode && pNode->hWnd != (HWND)pWin)
            pNode = pNode->pNext;
        pNode = pNode->pNext;
    }

    while (pNode)
    {
        if (((PMAINWIN)(pNode->hWnd))->dwStyle & WS_VISIBLE) {
            pGCRInfo = ((PMAINWIN)(pNode->hWnd))->pGCRInfo;
        
#ifndef _LITE_VERSION
            pthread_mutex_lock (&pGCRInfo->lock);
#endif
            SubtractClipRect (&pGCRInfo->crgn, rcWin);
            pGCRInfo->age ++;
#ifndef _LITE_VERSION
            pthread_mutex_unlock (&pGCRInfo->lock);
#endif
        }

        pNode = pNode->pNext;
    }
}
这两个典型的函数名看起来也非常工整,名字本身也显示了两个函数名字本身也表明了函数的内容,窗口剪切所有在自己Z序下面的窗口,同时被在自己Z序之上的窗口剪切,这里的Z序在miniGUI作者的文章中有详细说明。这布局图还引申出来一个效果,用这张图可以言简意赅的表达这个意思 ,中间的图形会被更高Zorder的窗口剪切,同时也剪切自己后面的窗口。
miniGUI笔记 - Tsecer - Tsecer的回音岛
 
说到这里,其实已经可以看到这里的实现和我刚开始直观上的想法有区别,在鼠标点击的时候,它并不是首先判断zorder,让zorder上靠前的首先获得处理机会,而是按照常规的逻辑来判断鼠标点击位置下可见图形的
六、子控件和主窗口之间的关系
1、创建方法的不同
以主窗口的创建为例,我们常见的例子是通过接口DialogBoxIndirectParam来创建,这个函数中调用的就是CreateMainWindow
HWND GUIAPI CreateMainWindow (PMAINWINCREATE pCreateInfo)
{

相反的,以控件为例,我们可以看下项目中自带的例子mg-samples-1.6.10\src\combobox.c为例,它在创建子控件的时候是通过传递的    DlgMyDate.controls = CtrlMyDate;指针列表,还是以作者的

(gdb) bt
#0  PreDefDialogProc (hWnd=134532288, message=96, wParam=0, lParam=3221222092)
    at dialog.c:236
#1  0x08048b7c in MyDateBoxProc (hDlg=134532288, message=96, wParam=0, 
    lParam=3221222092) at combobox.c:267
#2  0x0019138b in SendMessage (hWnd=134532288, iMsg=96, wParam=0, 
    lParam=3221222092) at message.c:792
#3  0x0017da72 in CreateMainWindow (pCreateInfo=0xbffff2cc) at window.c:3625
#4  0x0016d325 in DialogBoxIndirectParam (pDlgTemplate=0x804a380, 
    hOwner=2380064, DlgProc=0x804891b <MyDateBoxProc>, lParam=0)
    at dialog.c:155
#5  0x08048c09 in MiniGUIAppMain (argc=1, argv=0xbffff414) at combobox.c:278
#6  0x08048bbe in main (args=1, argv=0xbffff414) at combobox.c:270
(gdb) 
2、对于事件的响应
当鼠标点击或者移动的时候,这个窗口管理系统的处理逻辑将如何派发和处理这个事件呢?或者说一个更为直观的问题:在判断鼠标这个事件的时候,这里的控件是否可以作为窗口系统可见的基础单位?最简单的方法还是“调试一下,你就知道”。当我在Meeting Plan对话框的OK 按钮上滑动鼠标的时候,在函数GetMainWindowPtrUnderPoint判断返回的对话框的控件为主窗口,而不是我们这里看到的按钮控件的句柄。这一点也不用多打调用栈,在这个地方简单的打印一个窗口的标题就可以确认这个控件是什么内容。
(gdb) p pTemp->spCaption
$4 = 0xb7fe2008 "Meeting  Plan"
(gdb) list
1637            mg_altdown = 0;
1638            if (mg_modal == 1) {
1639                mg_modal = 0;
1640                return 0;
1641            }
1642        }
1643    }
1644
1645    if (mg_altdown) {
1646        if (message == MSG_KEYDOWN) {
(gdb) 
3、事件的再次派发
由于事件不是直接发送给控件,而是发送给了控件“寄生”的宿主主控件,所以此时的消息需要由主控件的处理函数再次派发,这个派发过程以combox中的例子来说明,就是在PreDefDialogProc--->>>PreDefMainWinProc--->>>DefaultMouseMsgHandler--->>>--->>>
    else {
        pUnderPointer = wndMouseInWhichControl (pWin, x, y, &UndHitCode);
        if (pUnderPointer && (pUnderPointer->dwStyle & WS_DISABLED))
            pUnderPointer = NULL;

        if (pUnderPointer) {
            cx = x - pUnderPointer->cl;
            cy = y - pUnderPointer->ct;
        }
    }
……
    switch (message) {
        case MSG_MOUSEMOVE:
……
        case MSG_LBUTTONDOWN:
        case MSG_RBUTTONDOWN:
            if (pUnderPointer) {
                if (pUnderPointer->dwStyle & WS_DISABLED) {
                    Ping ();
                    break;
                }
……
                else if (__mgs_captured_ctrl == NULL) {
                    PostMessage((HWND)pUnderPointer, message, 
                        flags, MAKELONG(cx, cy));
                }
mainwindow的主要作用就是遍历所有的控件,判断出这个事件是在哪个控件所在范围内(代码中这个地方的逻辑看起来比较复杂,但是说我们关系的主要逻辑就这么多了,其它的暂时不用关系),然后把这个事件顺手转发给这个控件,然后调用控件自定义的处理函数。
4、控件的注册
libminigui-1.6.10\src\control\button.c
BOOL RegisterButtonControl (void)
{
……
    WndClass.spClassName = CTRL_BUTTON;
    WndClass.dwStyle     = WS_NONE;
    WndClass.dwExStyle   = WS_EX_NONE;
    WndClass.hCursor     = GetSystemCursor (IDC_ARROW);
    WndClass.iBkColor    = GetWindowElementColor (BKC_CONTROL_DEF); 
    WndClass.WinProc     = ButtonCtrlProc;

5、控件的使用
HWND GUIAPI CreateWindowEx (const char* spClassName, const char* spCaption,
                  DWORD dwStyle, DWORD dwExStyle, int id, 
                  int x, int y, int w, int h,
                  HWND hParentWnd, DWORD dwAddData)
{
……
    cci = (PCTRLCLASSINFO)SendMessage (HWND_DESKTOP, 
                MSG_GETCTRLCLASSINFO, 0, (LPARAM)spClassName);
                
    if (!cci) return HWND_INVALID;
……
    pNewCtrl->ControlProc = cci->ControlProc;
6、控件处理函数的调用
static int ButtonCtrlProc (HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam)
{
……
        case MSG_LBUTTONDOWN:
            if (GetCapture () == hWnd)
                break;
            SetCapture (hWnd);
                
            BUTTON_STATUS(pCtrl) |= BST_PUSHED;
            BUTTON_STATUS(pCtrl) |= BST_FOCUS;

            if (dwStyle & BS_NOTIFY)
                NotifyParent (hWnd, pCtrl->id, BN_PUSHED);

            InvalidateRect (hWnd, NULL, TRUE);
        break;
从这个流程可以看到一个好处:就是控件的处理函数我们不需要关系,这个判断都是由通用的控件处理函数ButtonCtrlProc来完成,它读取出自己的ID,然后向父对话框发送一个BN_PUSHED事件,这样的情况下,用户可以只关心自己定义的对话框处理函数中的逻辑,只是处理逻辑上自定义的控件ID的BN_PUSHED消息,这样非常的通用,相当于对底层屏蔽了剪切层的图形相关的代码,用户可以只关心对话框的业务逻辑。
7、mainwindow、dialog、control的区别
这个大致在网上搜索了下,没有详细的针对miniGUI的说明,但是有一个和Qt相关的类似问题"Difference between Dialog and widget and QMainWindow",上面的回答是
Re: ifference between Dialog and widget and QMainWindow

Widget: A rectangular region on the screen for display and user interaction. They include buttons, sliders, views, dialogs, windows, etc. All widgets will display something on the screen, and many will also accept user input from the keyboard or mouse. The word "widget" comes from Unix, on Windows they are called "controls".

Window: A "top level" widget. A window is the top of a parent/child hiearchy, and are usually displayed with a titlebar and border. The underlying windowing system (Windows, KDE, GNOME, etc) will provide policies for the windows, such as titlebar/border style, placement, focus, etc.

Dialog: A special kind of window, usually temporary. They may or may not have a different titlebar appearance. They are presented to the user for the purpose of notification or gathering input, and typically have OK, Cancel, etc., buttons on the bottom or right.

The key here is that all of these are widgets, windows are the top-level widgets, and dialogs are a special kind of window.

其实控件和对话框的区别是非常明显的,例如,通过一个控件是依赖于对话框的,它和对话框的相对位置固定,比方说我们通常不能在不移动对话框的情况下移动一个控件,例如一个对话框上的按钮。而这里的解释是对话框通常是临时的、用户通知用户一个事件、手机用户输入,让用户选择“确定/取消”等。在miniGUI中,控件通常是通过创建窗口时传入的controls数组来创建子控件:
int GUIAPI PreDefDialogProc (HWND hWnd, int message, 
                WPARAM wParam, LPARAM lParam)
{
    HWND hCurFocus;

    switch (message) {
    case MSG_CREATE:
    {
        int i;
        PCTRLDATA pCtrlData;
        HWND hCtrl;
            
        PDLGTEMPLATE pDlgTmpl 
                    = (PDLGTEMPLATE)(((PMAINWINCREATE)lParam)->dwReserved);
            
        for (i = 0; i < pDlgTmpl->controlnr; i++) {
            pCtrlData = pDlgTmpl->controls + i;
            if (pCtrlData->class_name) {
                hCtrl = CreateWindowEx (pCtrlData->class_name,
                              pCtrlData->caption,
                              pCtrlData->dwStyle | WS_CHILD,
                              pCtrlData->dwExStyle,
                              pCtrlData->id,
                              pCtrlData->x,
                              pCtrlData->y,
                              pCtrlData->w,
                              pCtrlData->h,
                              hWnd,
                              pCtrlData->dwAddData);
            }
            else
                break;
                              
            if (hCtrl == HWND_INVALID) {
                dlgDestroyAllControls (hWnd);
                return -1;
            }
        }

        return 0;
    }
七、想到的其它问题
从系统实现上来看,这里其实有很多所谓的面向对象思想的萌芽,在没有面向对象思想产生之前,如何让接口做的更加通用?这里的关键就是通过消息发送机制来实现,也就是这里通用的SendMessage,这个接口作为所有模块和逻辑的通讯原语,也就是以这个接口为基础,实现了整个系统的耦合基础。
不同的控件/窗口之间具有相同的初始内存布局,
// this struct is an internal struct
typedef struct _MAINWIN
{
    /*
     * These fields are similiar with CONTROL struct.
     */
    short DataType;     // the data type.
    short WinType;      // the window type.

    int left, top;      // the position and size of main window.
    int right, bottom;

    int cl, ct;         // the position and size of client area.
    int cr, cb;

    DWORD dwStyle;      // the styles of main window.
    DWORD dwExStyle;    // the extended styles of main window.
……
控件结构
typedef struct tagCONTROL
{
    /*
     * These fields are similiar with MAINWIN struct.
     */
    short DataType;         // the data type
    short WinType;          // the window type

    int left, top;          // the position of control in main window's
    int right, bottom;      // client area.

    int cl, ct;             // the positio of control client in main window's
    int cr, cb;             // client area.

    DWORD dwStyle;          // the styles of child window.
    DWORD dwExStyle;        // the extended styles of child window.
多态的实现主要通过让子控件先处理,这样如果子控件处理之后不再需要父控件处理可以直接返回。这个其实就和很多技术的发展一样,很多革命性的技术出现之前,已经有很多替代的方法来解决这种问题,通常解决问题的方法不止一个,也不是非要某种技术不可,只是这种技术可能更加便捷。

  评论这张
 
阅读(143)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017