一、什么是事件驱动编程:
MinGUI是一个图形用户界面支持系统。通常的GUI编程概念均适用于MiniGUI编程。
二、焦点和光标:
焦点和光标的概念用于管理输入设备和输入事件的传送。鼠标光标是一个绘制在屏幕上的小位图,指示当前的鼠标位置,应用程序可以绘制哪一个位图以及是否显示光标。应用程序还可以捕捉光标并获取光标事件。
三、MINIGUI的三种运行模式
① MiniGUI-Threads,运行在MiniGUI-Threads上的程序可以在不同的线程中建立多个窗口,但是所有窗口在一个进程或者地址空间中运行。
②MiniGUI-Process。MiniGUI-Process上的每个程序是独立的进程,每个进程也可以建立多个窗口,这种模式适用于具有完整UNIX特性的嵌入式操作系统。
③MiniGUI-Standalone。这种模式下,MiniGUI可以以独立进程的方式运行,不需要多线程,这种模式适合功能单一的场合
MiniGUI-Standalone模式适应面最广,几乎支持所有操作系统。
四、MiniGUI-Lite
Lite模式下,的MiniGui使用嵌入式的linux的进程机制,使得MiniGui更稳定,能充分利用类似地址控件保护的高级性能。我们可以在MiniGui-Lite模式下运行多个MiniGui客户进程,当其中一个进程不正常终止,其他进程将不受影响。
MiniGui-Lite模式下,客户端创建的窗口不是一个全局对象。
五、helloworld程序
#include <minigui/common.h>
#include <minigui/minigui.h>
#include <minigui/gdi.h>
#include <minigui/window.h>
static LRESULT HelloWinProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
switch (message) {
case MSG_PAINT:
hdc = BeginPaint (hWnd);
TextOut (hdc, 60, 60, "Hello, world!");
EndPaint (hWnd, hdc);
return 0;
case MSG_CLOSE:
DestroyMainWindow (hWnd);
PostQuitMessage (hWnd);
return 0;
}
return DefaultMainWinProc (hWnd, message, wParam, lParam);
}
int MiniGUIMain (int argc, const char* argv[])
{
MSG Msg;
HWND hMainWnd;
MAINWINCREATE CreateInfo;
#ifdef _MGRM_PROCESSES
JoinLayer (NAME_DEF_LAYER , "helloworld" , 0 , 0);
#endif
CreateInfo.dwStyle = WS_VISIBLE | WS_BORDER | WS_CAPTION;
CreateInfo.dwExStyle = WS_EX_NONE;
CreateInfo.spCaption = "Hello, world!";
CreateInfo.hMenu = 0;
CreateInfo.hCursor = GetSystemCursor (0);
CreateInfo.hIcon = 0;
CreateInfo.MainWindowProc = HelloWinProc;
CreateInfo.lx = 0;
CreateInfo.ty = 0;
CreateInfo.rx = 240;
CreateInfo.by = 180;
CreateInfo.iBkColor = COLOR_lightwhite;
CreateInfo.dwAddData = 0;
CreateInfo.hHosting = HWND_DESKTOP;
hMainWnd = CreateMainWindow (&CreateInfo);
if (hMainWnd == HWND_INVALID)
return -1;
ShowWindow (hMainWnd, SW_SHOWNORMAL);
while (GetMessage (&Msg, hMainWnd)) {
TranslateMessage (&Msg);
DispatchMessage (&Msg);
}
MainWindowThreadCleanup (hMainWnd);
return 0;
}
#ifndef _MGRM_PROCESSES
#include <minigui/dti.c>
#endif
程序创建一个大小为 320x240 像素的应用程序窗口,并显示“Hello world!” 在窗口客户区的中心。
所有MiniGUI都必须包含的四个头文件M<minigui/common.h>
,<minigui/minigui.h>
,<minigui/gdi.h>
,和<minigui/window.h>、<control.h>
common.h
: 包括 MiniGUI 中常用的宏和数据类型的定义。minigui.h
:包括全局和通用接口函数和一些杂项函数的定义。gdi.h
:包括MiniGUI图形功能的接口定义。window.h
: 包括宏、数据类型、数据结构的定义,这些都与窗口和函数接口的声明有关。
使用预定义控件的 MiniGUI 应用程序必须包含另一个头文件 -- minigui/control.h
:
control.h
:包括minigui库中所有内置控件的接口定义。
C 程序的入口是主程序,而 MiniGUI 程序的入口是MiniGUIMain
,程序原型如下:
int MiniGUIMain ( int argc, const char * argv[]);
该函数是main
ANSI C 的(入口函数)封装的宏。所以每个MiniGUI 应用程序(无论是服务器功能mginit
还是客户端应用程序)的入口 都是该MiniGUIMain
函数。和的参数 argc
与C程序中函数的argv
含义相同main
,分别是命令行参数的个数和指向参数的字符串数组指针。
在 MiniGUI-Processes 中加入一个层
# ifdef _MGRM_PROCESSES
JoinLayer (NAME_DEF_LAYER, " helloworld " , 0 , 0 );
#ENDIF
JoinLayer
是 MiniGUI-Processes 的一个特殊功能,因此包含在_MGRM_PROCESSES
. 在 MiniGUI-Processes 运行模式下,每个 MiniGUI 客户端程序在调用其他 MiniGUI 函数之前,必须先调用该函数加入一个层(或创建一个新层)。
如果程序是 MiniGUI-Processes 的服务器,你应该调用 ServerStartup
:
if (!ServerStartup ( 0 , 0 , 0 )) {
fprintf (stderr, "无法启动 MiniGUI-Processes 的服务器: mginit. \n " );
返回 1 ;
}
注意MiniGUI 为三种运行模式定义了不同的宏:
- MiniGUI 线程:
_MGRM_THREADS
; - MiniGUI-Processes:
_MGRM_PROCESSES
和_LITE_VERSION
; - MiniGUI-Standalone:
_MGRM_STANDALONE
,_LITE_VERSION
, 和_STAND_ALONE
.
创建和显示主窗口
hMainWnd = CreateMainWindow (&CreateInfo);
每个 MiniGUI 应用程序的初始用户界面通常是一个主窗口;您可以通过调用CreateMainWindow
函数创建一个主窗口。CreateMainWindow
函数的参数是一个指向MAINWINCREATE
结构体的指针,CreatInfo
在这个例子中就是这个结构体,返回值是创建的主窗口的句柄。MAINWINCREAT
结构体描述了主窗口的属性,在CreatInfo
创建主窗口之前需要设置其属性。
CreateInfo.dwStyle = WS_VISIBLE | WS_BORDER | WS_CAPTION;
上面的语句设置了主窗口的样式,这里的窗口设置为初始可见,并有边框和标题栏。
CreateInfo.dwExStyle = WS_EX_NONE;
以上语句设置了主窗口的扩展样式,窗口没有扩展样式。
CreateInfo.spCaption = "Hello, world!" ;
该语句将主窗口的标题设置为“Hello, world!”。
CreateInfo.hMenu = 0 ;
该语句设置主窗口的主菜单,窗口没有主菜单。
CreateInfo.hCursor = GetSystemCursor ( 0 );
该语句设置主窗口的光标,该窗口的光标是默认的系统光标。
CreateInfo.hIcon = 0 ;
该语句设置主窗口的图标,窗口没有图标。
CreateInfo.MainWindowProc = HelloWinProc;
该语句将主窗口的窗口过程函数设置为 HelloWinProc
,所有发送到该窗口的消息都由该函数处理。
创建信息.lx = 0 ;
创建信息.ty = 0 ;
创建信息.rx = 320 ;
CreateInfo.by = 240 ;
以上语句设置了主窗口在屏幕上的位置和大小,左上角和右下角分别位于(0, 0)和(320, 240)。
CreateInfo.iBkColor = PIXEL_lightwhite;
该语句将主窗口的背景色设置为白色,PIXEL_lightwhite
是 MiniGUI 预定义的像素值。
CreateInfo.dwAddData = 0 ;
该语句设置主窗口的附加数据,窗口没有附加数据。
CreateInfo.hHosting = HWND_DESKTOP;
此语句将主窗口的托管窗口设置为桌面窗口。
ShowWindow (hMainWnd, SW_SHOWNORMAL);
ShowWindow
创建主窗口后,需要调用函数将创建的窗口显示在屏幕上。
的第一个参数hMainWnd
是要显示的窗口的句柄,
第二个参数指定显示窗口的动作(显示或隐藏)。SW_SHOWNORMAL
表示显示主窗口并将其设置为最顶部的窗口。
进入消息循环
当ShowWindow
函数被调用时,主窗口将显示在屏幕上。和其他 GUI 一样,是时候进入消息循环了。MiniGUI 为每个 MiniGUI 程序维护一个消息队列。事件发生后,MiniGUI 将事件转化为消息,并将消息放入目标窗口的消息队列中。那么应用程序的任务就是执行下面的消息循环代码,不断地从消息队列中获取消息并进行处理。
while (GetMessage (&Msg, hMainWnd)) {
TranslateMessage (&Msg);
DispatchMessage (&Msg);
}
Msg
变量类型为 MSG 结构体,定义 minigui/window.h
如下:
/**
* 消息结构。
* \sa GetMessage, PostMessage, msgs
*/
typedef struct _MSG
{
/** 接收此消息的窗口句柄。*/
HWND hwnd;
UINT message; /** 消息标识符 */
WPARAM wParam; /** 消息的第一个参数(具有指针精度的无符号整数)。 */
LPARAM lParam; /** 消息的第二个参数(具有指针精度的无符号整数) */
DWORD time; /**时间*/
#ifdef _MGRM_THREADS
void* pAdd; /** 附加数据*/
#endif
} MSG;
typedef MSG* PMSG;
GetMessage
函数从应用程序的消息队列中获取一条消息。
GetMessage (&Msg, hMainWnd);
此函数的第二个参数是主窗口的句柄,第一个参数是指向 MSG 结构的指针。GetMessage
函数用从消息队列中得到的消息填充 MSG 结构的字段,其中包括:
hwnd
: 消息发送到的窗口句柄。该值是相同hMainWnd
的helloworld.c
程序。message
: 消息标识符。这是用于标识消息的整数。每条消息都有一个对应的预定义标识符,这些标识符定义在minigui/window.h
并带有MSG_
前缀。wParam
:第一个消息参数,不同的消息含义和取值不同。lParam
: 第二个消息参数,其含义和取值取决于消息。- time:消息放入消息队列的时间(tick count)。
只要从消息队列中得到的消息不是MSG_QUIT
, GetMessage
则返回一个非0值,消息循环将继续。MSG_QUIT
message 消息使GetMessage
返回为零,并导致消息循环终止。
TranslateMessage (&Msg);
TranslateMessage
函数将击键消息转换为MSG_CHAR
消息,然后将消息发送给窗口过程函数。
DispatchMessage (&Msg);
DispatchMessage
函数最终将消息发送到目标窗口的窗口过程,并让它处理消息。
在这个例子中,窗口过程是HelloWinProc
。也就是说,MiniGUI 在函数中调用主窗口的窗口过程函数(回调函数)DispatchMessage
来处理发送到这个主窗口的消息。处理完消息后,应用程序的窗口过程函数返回DispatchMessage函数中
,而DispatchMessage
function最终返回应用程序代码,应用程序通过调用下一个GetMessage
函数开始新的消息循环。
窗口过程函数
窗口过程函数是 MiniGUI 程序的主体。一个应用程序的大部分工作实际上发生在窗口过程函数中,因为GUI程序的主要任务是接收和处理窗口接收到的各种消息。
在helloworld.c
程序中,窗口过程是名为HelloWinProc的函数,窗口过程函数可以由程序员任意命名,CreateMainWindow
函数根据MAINWINCREATE
结构中指定的窗口过程创建主窗口。
窗口过程函数定义如下:
static LRESULT HelloWinProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
窗口过程函数的四个参数与 MSG 结构的前四个域相同,第一个参数hWnd
是接收消息的窗口句柄,与CreateMainWindow
函数的返回值相同, 表示接收消息的具体窗口。第二个参数与MSG结构的message字段相同,是一个整数值,表示接收到的消息。最后两个参数都是消息参数(两个具有指针精度的整数),提供与消息相关的特殊信息。
窗口过程函数通常不是由程序直接调用,而是由MiniGUI 调用,即回调函数。
窗口过程函数不予处理的消息应该传给DefaultMainWinProc函数进行缺省处理,从DefaultMainWinProc返回的值必须由窗口过程返回。
屏幕输出
程序在响应MSG_PAINT
消息时执行屏幕输出。应用程序首先通过调用BeginPaint
函数获取设备上下文句柄 ,并使用它来调用GDI函数来执行绘图操作。这里,程序使用TextOut
文本输出函数来显示“Hello, world!” 字符串。绘制完成后,应用程序调用EndPaint
函数释放设备上下文句柄。
我们将在本指南的第三部分详细介绍 MiniGUI 图形设备接口。
退出程序
当用户单击窗口右上角的关闭按钮时,窗口过程函数将收到一条MSG_CLOSE
消息。helloworld
程序在收到MSG_CLOSE
消息时调用DestroyMainWindow函数
销毁主窗口,并且调用PostQuitMessage
函数将MSG_QUIT投入到
到消息队列中。GetMessage
函数在接收到MSG_QUIT
消息时会返回0 ,最终导致程序退出消息循环。
程序最后调用MainWindowThreadCleanup
清理主窗口使用的消息队列等系统资源,并最终由MiniGUIMain
finally返回。
编译、链接和运行
可以在命令行中输入以下命令进行编译 helloworld.c
,并链接生成执行文件helloworld
:
$ gcc -o helloworld helloworld.c -lpthread -lminigui_ths -ljpeg -lpng -lz
如果将 MiniGUI 配置为 MiniGUI-Processes,则需要以下编译选项:
$ gcc -o helloworld helloworld.c -lminigui_procs -ljpeg -lpng -lz
-o
告诉 编译器 ( gcc
) 要生成的目标文件名,这里是helloworld
; -l选项
指定生成helloworld
时需要链接的一个库,这里连接的是minigui
库和pthread
库,如果 MiniGUI 配置为 MiniGUI-Threads时。还需要链接pthread库。 pthread
是提供 POSIX 兼容线程支持的函数库,编译 MiniGUI-Threads 程序时必须链接这个函数库。我们所编译的程序只使用MiniGUI核心库minigui
中的函数,没有使用MiniGUI其他库提供的组件(如libmgext或libvcongui),因此只需要链接minigui库
或链其他要链接的函数库如jpeg、png、z 等都是MiniGUI 内部依赖的函数库(这里假设您在配置MiniGUI 时已经开启了JPEG 和PNG 图像支持)。
如果将 MiniGUI 配置为 MiniGUI-Processes,则在运行helloworld
程序之前,必须先确保已经启动了MiniGUI的服务器端程序mginit。例如,你可以在 启动MED的mginit
程序,然后进入helloworld
文件所在目录,在命令行中输入./helloworld
即可启动helloworld
程序。
$ ./helloworld