minigui代码分析

一、minigui运行模式

1.线程模式:MiniGui-Threads 

定义:_MGRM_THREADS 
行在MiniGui-Threads上的程序可以在不同的线程中建立多个窗口,但所有的窗口在一个进程或地址空间中运行,传统意义上的嵌入式操作系统。

2.进程模式:MiniGui-Processes 

定义:_MGRM_Processes或者定义_LITE_VERSION 

MiniGui-Processes上每个程序是单独的进程,每个进程也可以建立多个窗口,并且实现了多进程窗口系统。适用于具有完整UNIX特性的嵌入式式系统。

3.独立应用模式:MiniGui-Standalone

定义:_MGRM_STANDALONE 或者定义_LITE_VERSION和_STAND_ALONE 

通过独立任务的方式运行,既不需要多进程支持也不需要多线程支持。

二、数据结构

1.CreateMainWindow函数参数:PMAINWINCREATE  pCreateInfo

结构体MAINWINCREATE 定义了被创建的窗口的位置、标题、类型等基本参数。实际上包含了创建窗口的UI风格和窗口处理函数两方面的内容。

PMAINWINCREATE为指向该结构体的指针。

typedef struct _MAINWINCREATE
{
    DWORD dwStyle;                                      //窗口风格
    DWORD dwExStyle;                                    //窗口的附加风格
    const char* spCaption;                              //窗口的标题
    HMENU hMenu;                                        //附加在窗口上的菜单句柄
    HCURSOR hCursor;                                    //在窗口中所使用的鼠标光标句柄
    HICON hIcon;                                        //程序的图标句柄
    HWND  hHosting;                                     //窗口消息队列的托管窗口
    int (*MainWindowProc)(HWND, int, WPARAM, LPARAM);   //该窗口的消息处理函数指针,回调函数
    int lx, ty, rx, by;                                 //窗口相对屏幕的绝对坐标,以象素点表示
    int iBkColor;                                       //窗口背景颜色
    DWORD dwAddData;                                    //附带给窗口的一个32 位值
    DWORD dwReserved;                                   //预留,没有用到
}MAINWINCREATE;
typedef MAINWINCREATE* PMAINWINCREATE;

2.MAINWIN结构体:主窗口的详细信息由该结构体给出
typedef struct _MAINWIN
{
    /* These fields are similiar with CONTROL struct. */
    unsigned char DataType;     // 数据类型,表示是否是一个窗口(主窗口或者控件窗口),对于该结构和CONTROL结构,都必须是TYPE_HWND
    unsigned char WinType;      // 判断是否是主窗口,对于该结构,必须是TYPE_MAINWIN
    unsigned short Flags;       // special runtime flags, such EraseBkGnd flags

    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.
    
    int iBkColor;       // the background color.
    HMENU hMenu;        // handle of menu.
    HACCEL hAccel;      // handle of accelerator table.
    HCURSOR hCursor;    // handle of cursor.
    HICON hIcon;        // handle of icon.
    HMENU hSysMenu;     // handle of system menu.
    PLOGFONT pLogFont;  // pointer to logical font.

    char* spCaption;    // the caption of main window.
    int   id;           // the identifier of main window.

    LFSCROLLBARINFO vscroll; // the vertical scroll bar information.
    LFSCROLLBARINFO hscroll; // the horizital scroll bar information.

    /** the window renderer */
    WINDOW_ELEMENT_RENDERER* we_rdr;

    HDC   privCDC;      // the private client DC.
    INVRGN InvRgn;      // 这个主窗口的无效区域,在处理MSG_PAINT消息时很重要
    PGCRINFO pGCRInfo;  // pointer to global clip region info struct.

    // the Z order node.
    int idx_znode;

    PCARETINFO pCaretInfo; // pointer to system caret info struct.

    DWORD dwAddData;    // the additional data.
    DWORD dwAddData2;   // the second addtional data.

    int (*MainWindowProc)(HWND, int, WPARAM, LPARAM); // the address of main window procedure.

    struct _MAINWIN* pMainWin; // the main window that contains this window. for main window, always be itself.

    HWND hParent;       // the parent of this window.   for main window, always be HWND_DESKTOP.
    
    /* Child windows.*/
    HWND hFirstChild;    // the handle of first child window.
    HWND hActiveChild;  // the currently active child window.
    HWND hOldUnderPointer;  // the old child window under pointer.
    HWND hPrimitive;    // the premitive child of mouse event.

    NOTIFPROC NotifProc;    // the notification callback procedure.

    /* window element data. */
    struct _wnd_element_data* wed;
    
    /* Main Window hosting. The following members are only implemented for main window.*/
    struct _MAINWIN* pHosting; // the hosting main window.
    struct _MAINWIN* pFirstHosted; // the first hosted main window.
    struct _MAINWIN* pNextHosted;  // the next hosted main window.

    PMSGQUEUE pMessages;  // the message queue.

    GCRINFO GCRInfo; // the global clip region info struct. put here to avoid invoking malloc function.

#ifdef _MGRM_THREADS
    pthread_t th;        // the thread which creates this main window.
#endif
	//the controls as main
	HWND hFirstChildAsMainWin;

    HDC   secondaryDC;                // the secondary window dc.
    ON_UPDATE_SECONDARYDC update_secdc; // the callback of secondary window dc
    RECT  update_rc;
} MAINWIN;
3.MSGQUEUE消息队列

struct _MSGQUEUE
{
    DWORD dwState;              // message queue states

#ifdef _MGRM_THREADS
    pthread_mutex_t lock;       // lock
    sem_t wait;                 // the semaphore for wait message
    sem_t sync_msg;             // the semaphore for sync message
#endif

    PQMSG  pFirstNotifyMsg;     // head of the notify message queue
    PQMSG  pLastNotifyMsg;      // tail of the notify message queue

#ifdef _MGRM_THREADS
    PSYNCMSG pFirstSyncMsg;     // head of the sync message queue
    PSYNCMSG pLastSyncMsg;      // tail of the sync message queue
#else
    IDLEHANDLER OnIdle;         // Idle handler
#endif

#ifdef _MGRM_THREADS
    PMAINWIN pRootMainWin;      // The root main window of this message queue.
#endif

    MSG* msg;                   /* post message buffer */
    int len;                    /* buffer len */
    int readpos, writepos;      /* positions for reading and writing */

    int FirstTimerSlot;         /* the first timer slot to be checked */
    DWORD TimerMask;            /* timer slots mask */

    int loop_depth;             /* message loop depth, for dialog boxes. */
};

三、CreateMainWindow函数流程

1.init_desktop_win 

static void init_desktop_win (void)
{
    static MAINWIN sg_desktop_win;
    PMAINWIN pDesktopWin;

    LICENSE_SET_MESSAGE_OFFSET();

    pDesktopWin = &sg_desktop_win; //作为默认的Desktop

    pDesktopWin->pMessages         = __mg_dsk_msg_queue;  // 自己的消息队列,其他窗口发送的SendMessage将消息压入该队列[luther.gliethttp]
    pDesktopWin->MainWindowProc    = DesktopWinProc;      // 桌面默认回调处理函数

    pDesktopWin->DataType          = TYPE_HWND;
    pDesktopWin->WinType           = TYPE_ROOTWIN;
#ifdef _MGRM_THREADS
    pDesktopWin->th                = __mg_desktop;
#endif

    pDesktopWin->pLogFont          = GetSystemFont (SYSLOGFONT_WCHAR_DEF);
    pDesktopWin->spCaption         = "THE DESKTOP WINDOW";

    pDesktopWin->pGCRInfo          = &sg_ScrGCRInfo;
    pDesktopWin->idx_znode         = 0;

    pDesktopWin->pMainWin          = pDesktopWin;
    pDesktopWin->we_rdr            = __mg_def_renderer;

    __mg_hwnd_desktop = (HWND)pDesktopWin;  // 登记到desktop的全局量中
    __mg_dsk_win  = pDesktopWin;
}

2.main函数 
#define main_entry main

#define MiniGUIMain \
MiniGUIAppMain (int args, const char* argv[]); \
int main_entry (int args, const char* argv[]) \
{ \
    int iRet = 0; \
    if (InitGUI (args, argv) != 0) { \
        return 1; \
    } \
    iRet = MiniGUIAppMain (args, argv); \
    TerminateGUI (iRet); \
    return iRet; \
} \
int MiniGUIAppMain

3.CreateMainWindow函数流程

HWND GUIAPI CreateMainWindowEx (PMAINWINCREATE pCreateInfo, 
        const char* werdr_name, const WINDOW_ELEMENT_ATTR* we_attrs,
        const char* window_name, const char* layer_name)
{
    // 指针
    PMAINWIN pWin;

    if (pCreateInfo == NULL) {
        return HWND_INVALID;
    }

    if (!(pWin = calloc(1, sizeof(MAINWIN)))) { //分配结构体内存
        return HWND_INVALID;
    }

#ifdef _MGRM_THREADS //这是重要部分,用于找到消息队列
    if (pCreateInfo->hHosting == HWND_DESKTOP || pCreateInfo->hHosting == 0) {
        /*如果托管窗口为桌面窗口或者没有托管窗口,为新建的主窗口创建线程信息和消息队列,获取本线程关联的消息队列结构体。
         *若获取失败说明该窗口是最顶层的主窗口,目前还有对应的消息队列,则为其创建一个消息队列结构体*/
        if ((pWin->pMessages = GetMsgQueueThisThread ()) == NULL) { //试图获取本线程关联的消息队列结构体
            if (!(pWin->pMessages = mg_InitMsgQueueThisThread ()) ) { //试图去创建一个消息队列结构体
                free (pWin);

                return HWND_INVALID;
            }
            pWin->pMessages->pRootMainWin = pWin;//如果创建消息队列结构体成功,则设置当前窗口为根窗口
        }
        else {
            /* Already have a top level main window, in case of user have set 
               a wrong hosting window */
            pWin->pHosting = pWin->pMessages->pRootMainWin;//设置托管口
        }
    }
    else {
        pWin->pMessages = GetMsgQueueThisThread (); //直接获取,这种情况下,是可以肯定消息队列已经存在
        if (pWin->pMessages != kernel_GetMsgQueue (pCreateInfo->hHosting) || //该函数的调用者必须和hosting的消息队列所在线程一致。这很重要
                pWin->pMessages == NULL) {
            free (pWin);

            return HWND_INVALID;
        }
    }

    if (pWin->pHosting == NULL) //如果当前窗口的托管主窗口为空,利用传递的函数参数获得托管主窗口信息
        pWin->pHosting = gui_GetMainWindowPtrOfControl (pCreateInfo->hHosting);
    /* leave the pHosting is NULL for the first window of this thread. */
#else
    pWin->pHosting = gui_GetMainWindowPtrOfControl (pCreateInfo->hHosting); //运行模式为非MiniGui-Threads,获得托管窗口的句柄
    if (pWin->pHosting == NULL)
        pWin->pHosting = __mg_dsk_win; //托管窗口句柄为空,设置托管窗口为默认桌面窗口

    pWin->pMessages = __mg_dsk_msg_queue; //将消息队列设置为默认桌面消息队列
#endif

    pWin->pMainWin      = pWin; //以下部分在初始化结构体成员,可以忽略
    pWin->hParent       = 0;    //当前窗口的父窗口不存在
    pWin->pFirstHosted  = NULL; //第一个托管主窗口
    pWin->pNextHosted   = NULL; //下一个托管主窗口
    pWin->DataType      = TYPE_HWND;      //数据类型
    pWin->WinType       = TYPE_MAINWIN;   //窗口类型

#ifdef _MGRM_THREADS
    pWin->th            = pthread_self(); //创建主窗口的线程
#endif

    pWin->hFirstChild   = 0;    //第一个子窗口的句柄为0,即不存在
    pWin->hActiveChild  = 0;    //当前活动的子窗口的句柄为0,即不存在
    pWin->hOldUnderPointer = 0; //旧的子窗口
    pWin->hPrimitive    = 0;

    pWin->NotifProc     = NULL;

    pWin->dwStyle       = pCreateInfo->dwStyle;
    pWin->dwExStyle     = pCreateInfo->dwExStyle;

#ifdef _MGHAVE_MENU
    pWin->hMenu         = pCreateInfo->hMenu;
#else
    pWin->hMenu         = 0;
#endif
    pWin->hCursor       = pCreateInfo->hCursor;
    pWin->hIcon         = pCreateInfo->hIcon;

#ifdef _MGHAVE_MENU
    if ((pWin->dwStyle & WS_CAPTION) && (pWin->dwStyle & WS_SYSMENU)) //如果当前窗口包含标题且包含系统菜单,则创建系统菜单
        pWin->hSysMenu= CreateSystemMenu ((HWND)pWin, pWin->dwStyle);
    else
#endif
        pWin->hSysMenu = 0; //否则系统菜单项为0

    pWin->spCaption    = FixStrAlloc (strlen (pCreateInfo->spCaption));//分配空间存放标题
    if (pCreateInfo->spCaption [0]) //如果函数参数结构体的标题字符串的第一个字符非空
        strcpy (pWin->spCaption, pCreateInfo->spCaption); //复制标题到结构体的标题项

    pWin->MainWindowProc = pCreateInfo->MainWindowProc; //消息处理函数
    pWin->iBkColor    = pCreateInfo->iBkColor;

    pWin->pCaretInfo = NULL; //指向系统插入字符信息结构体

    pWin->dwAddData   = pCreateInfo->dwAddData;
    pWin->dwAddData2  = 0;
    pWin->secondaryDC = 0;

    /* Scroll bar */ //下面是初始化滚动条相关的内容
    if (pWin->dwStyle & WS_VSCROLL) { //垂直方向的滚动条
        pWin->vscroll.minPos = 0;
        pWin->vscroll.maxPos = 100;
        pWin->vscroll.curPos = 0;
        pWin->vscroll.pageStep = 101;
        pWin->vscroll.barStart = 0;
        pWin->vscroll.barLen = 10;
        pWin->vscroll.status = SBS_NORMAL;
    }
    else
        pWin->vscroll.status = SBS_HIDE | SBS_DISABLED;

    if (pWin->dwStyle & WS_HSCROLL) { //水平方向的滚动条
        pWin->hscroll.minPos = 0;
        pWin->hscroll.maxPos = 100;
        pWin->hscroll.curPos = 0;
        pWin->hscroll.pageStep = 101;
        pWin->hscroll.barStart = 0;
        pWin->hscroll.barLen = 10;
        pWin->hscroll.status = SBS_NORMAL;
    }
    else
        pWin->hscroll.status = SBS_HIDE | SBS_DISABLED;

    /** perfer to use parent renderer */ //初始化渲染器相关的内容,这时可以忽略这一部分
    if (pWin->dwExStyle & WS_EX_USEPARENTRDR) {
        if (((PMAINWIN)pCreateInfo->hHosting)->we_rdr) {
            pWin->we_rdr = ((PMAINWIN)pCreateInfo->hHosting)->we_rdr;
            ++pWin->we_rdr->refcount;
        }
        else {
            return HWND_INVALID;
        }
    }
    else {
        /** set window renderer */
        set_window_renderer (pWin, werdr_name);
    }

    /** set window element data */
    while (we_attrs && we_attrs->we_attr_id != -1) {
        // append_window_element_data (pWin, 
        //       we_attrs->we_attr_id, we_attrs->we_attr);
        DWORD _old;
        set_window_element_data ((HWND)pWin, 
                we_attrs->we_attr_id, we_attrs->we_attr, &_old);
        ++we_attrs;
    }

    /** prefer to parent font */
    if (pWin->dwExStyle & WS_EX_USEPARENTFONT)
        pWin->pLogFont = __mg_dsk_win->pLogFont;
    else {
        pWin->pLogFont = GetSystemFont (SYSLOGFONT_WCHAR_DEF);
    }

    if (SendMessage ((HWND)pWin, MSG_NCCREATE, 0, (LPARAM)pCreateInfo)) // MSG_NCCREATE表示窗口已经创建但是还没有向系统注册
        goto err;

    /** reset menu size */
    ResetMenuSize ((HWND)pWin);

#ifdef __TARGET_FMSOFT__
    pCreateInfo->lx += __mg_mainwin_offset_x;
    pCreateInfo->rx += __mg_mainwin_offset_x;
    pCreateInfo->ty += __mg_mainwin_offset_y;
    pCreateInfo->by += __mg_mainwin_offset_y;
#endif

    SendMessage ((HWND)pWin, MSG_SIZECHANGING, //开始发生一些消息,让窗口进行一些工作
            (WPARAM)&pCreateInfo->lx, (LPARAM)&pWin->left);
    SendMessage ((HWND)pWin, MSG_CHANGESIZE, (WPARAM)&pWin->left, 0);

    pWin->pGCRInfo = &pWin->GCRInfo;

    if (SendMessage (HWND_DESKTOP, MSG_ADDNEWMAINWIN, (WPARAM) pWin, 0) < 0)//这个很重要:把主窗口发送给Desktop窗口托管,进行管理。
        goto err;

    /* 
     * We should add the new main window in system and then
     * SendMessage MSG_CREATE for application to create
     * child windows.
     */
    if (SendMessage ((HWND)pWin, MSG_CREATE, 0, (LPARAM)pCreateInfo)) { //发送一个MSG_CREATE消息,告知应用程序窗口已经创建成功
        SendMessage(HWND_DESKTOP, MSG_REMOVEMAINWIN, (WPARAM)pWin, 0);
        goto err;
    }
#ifndef _MGRM_PROCESSES
    screensaver_create();
#endif
    return (HWND)pWin;

err:
#ifdef _MGRM_THREADS
    if (pWin->pMessages && pWin->pHosting == NULL) {
        mg_FreeMsgQueueThisThread ();
    }
#endif

    if (pWin->secondaryDC) DeleteSecondaryDC ((HWND)pWin);
    free (pWin);

    return HWND_INVALID;
}

1)判断传入的参数pCreateInfo是否为空

        Case NULL:若参数为空,返回HWND_INVALID  

        Case NOT NULL:若参数不为空,继续执行2 
2)为PMAINWIN类型的pWin分配内存空间,并判断pWin是否为空 

        Case NULL:分配空间失败,返回HWND_INVALID 

        Case NOT NULL:分配空间成功,继续执行3 
3)是否定义_MGRM_THREADS:

        3.a定义了_MGRM_THREADS,代表minigui的运行模式为MiniGui-Threads

        设置pWin的成员pWin->pMessages和pWin->pHosting 

        3.b没有定义_MGRM_THREADS,代表minigui的运行模式为非MiniGui-Threads  

        设置pWin的成员pWin->pMessages和pWin->pHosting 
4)设置pWin的成员。

5)初始化渲染器相关的内容。

6)SendMessage ((HWND)pWin,MSG_NCCREATE, 0, (LPARAM)pCreateInfo)

        表示该窗口已经创建但是还没有向系统进行注册,当收到这种类型的消息时可以对自己创建的对象进行初始化,但不能创建子窗口,也不能进行绘图。如果函数返回值为非零值,创建的窗口将被销毁。

7)SendMessage ((HWND)pWin, MSG_SIZECHANGING,(WPARAM)&pCreateInfo->lx, (LPARAM)&pWin->left);

        指示了将要被更改的窗口的大小,当窗口大小将要发生改变时,该消息会发送给窗口。如果你想要控制窗口改变后的实际位置和大小(窗口改变可能是MoveWindow或者其他函数引起的),你需要使用MSG_SIZECHANGING作为SendMessage函数的第二个参数,并且通过第二个参数返回位置和大小信息。

9)SendMessage ((HWND)pWin,MSG_CHANGESIZE, (WPARAM)&pWin->left, 0);

        确定改变后的窗口大小。

9)SendMessage (HWND_DESKTOP,MSG_ADDNEWMAINWIN, (WPARAM) pWin, 0);

        把主窗口发送给Desktop窗口托管,进行管理,并绘制窗口。

10)SendMessage ((HWND)pWin,MSG_CREATE, 0, (LPARAM)pCreateInfo);

        发送一个MSG_CREATE消息,告知应用程序窗口已经创建成功。


你可能感兴趣的:(minigui代码分析)