1.线程模式:MiniGui-Threads
定义:_MGRM_THREADS2.进程模式: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:若参数不为空,继续执行2Case NULL:分配空间失败,返回HWND_INVALID
Case NOT NULL:分配空间成功,继续执行33.a定义了_MGRM_THREADS,代表minigui的运行模式为MiniGui-Threads
设置pWin的成员pWin->pMessages和pWin->pHosting3.b没有定义_MGRM_THREADS,代表minigui的运行模式为非MiniGui-Threads
设置pWin的成员pWin->pMessages和pWin->pHosting5)初始化渲染器相关的内容。
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消息,告知应用程序窗口已经创建成功。