第一次在Linux环境下编程,使用minigui做个界面显示位置和速度曲线。Windows下写过一些C程序,minigui学习了两个礼拜,研究了点Emacs,边写边学习。
在104系统上运行,与底层FPGA通信,将其采集到的数据读入并显示。FPGA此处不谈,记录下minigui,以helloworld为例。
/**********************************************************************************/
#include <stdio.h>
#include <minigui/common.h> //包括minigui常用的宏以及数据类型的定义
#include <minigui/minigui.h> //包含全局的和通用的接口函数以及某些杂项函数的定义
#include <minigui/gdi.h> //包含minigui绘图函数的接口定义
#include <minigui/window.h> //包含窗口有关的宏、数据类型、数据接口定义以及函数接口声明
//#include <minigui/control.h> //包含libminigui中所有内建控件的接口定义
/*程序入口*/
int MiniGUIMain (int argc, const char* argv[]) //argc:命令行参数个数 argv参数字符串数组指针
{
MSG Msg; //window.h中
HWND hMainWnd;
MAINWINCREATE CreateInfo; //描述一个主窗口的属性
//const char* old_renderer;
#ifdef _MGRM_PROCESSES
JoinLayer(NAME_DEF_LAYER , "helloworld" , 0 , 0); //MiniGUI-Processes模式下加入层(客户端)
#endif
CreateInfo.dwStyle = WS_VISIBLE | WS_BORDER | WS_CAPTION; //设置主窗口风格:可见|有边框|有标题栏
CreateInfo.dwExStyle = WS_EX_NONE; //扩展风格:无
CreateInfo.spCaption = "HelloWorld"; //标题
CreateInfo.hMenu = 0; //主菜单:无
CreateInfo.hCursor = GetSystemCursor(0); //设置主窗口的光标为系统缺省光标
CreateInfo.hIcon = 0; //图标:无
CreateInfo.MainWindowProc = HelloWinProc; //设置主窗口的窗口函数,所有发往该窗口的消息由该函数处理
CreateInfo.lx = 0; //屏幕上的位置(0,0)、(320,240)
CreateInfo.ty = 0;
CreateInfo.rx = 320;
CreateInfo.by = 240;
CreateInfo.iBkColor = COLOR_lightwhite; //背景色
CreateInfo.dwAddData = 0; //附加数据:无
//在窗口过程中,可以使用GetWindowAdditionalData函数获取该指针,从而获得所需要传递的参数。
CreateInfo.hHosting = HWND_DESKTOP; //设置主窗口的托管窗口为桌面窗口
//MiniGUI-Threads 中每个线程创建的第一个主窗口,其托管窗口必须是桌面,即 HWND_DESKTOP,
//该线程的其他窗口,必须由属于同一线程的已有主窗口作为托管窗口。
//系统在托管窗口为 HWND_DESKTOP 时创建新的消息队列,
//而在指定非桌面的窗口作为托管窗口时,使用该托管窗口的消息队列,
//也就是说,同一线程中的所有主窗口应该使用同一个消息队列。
//在调用 MiniGUIMain 之前,MiniGUI 启动自己的桌面窗口(Desktop)。
//old_renderer = SetDefaultWindowElementRender("classic");
hMainWnd = CreateMainWindow (&CreateInfo); //创建一个主窗口,返回值为所创建主窗口的句柄
if (hMainWnd == HWND_INVALID)
return -1;
ShowWindow(hMainWnd, SW_SHOWNORMAL); //显示:参数1:所要显示的窗口句柄 参数2:显示窗口的方式(显示/隐藏)
while (GetMessage(&Msg, hMainWnd)) { //参数1:要获取消息的主窗口的句柄 参数2:指向MSG结构的指针
//GetMessage函数将用从消息队列中取出的消息来填充该消息结构的各个域
TranslateMessage(&Msg); //把击键消息转换为MSG_CHAR消息,然后直接发送到窗口过程函数
DispatchMessage(&Msg); //把消息发往该消息的目标窗口的窗口过程,让其处理。
//处理完消息后,应用程序的窗口函数将返回到DispatchMessage函数,再返回到应用程序代码,
//应用程序又从下一个GetMessage函数调用开始消息循环。
}
//SetDefaultWindowElementRenderer(old_renderer);
MainWindowThreadCleanup (hMainWnd); //清除主窗口所使用消息队列系统资源
return 0;
}
/*窗口函数*/
//窗口过程是一个特定类型的函数,用来接收和处理所有发送到该窗口的消息。
//每个控件类有一个窗口过程,属于同一控件类的所有控件共用同一个窗口过程来处理消息。
static int HelloWinProc(HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
//窗口过称函数,参数与MSG结构的前4个域相同 //由minigui调用,是一个回调函数
{
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); //在消息队列中投入一个MSG_QUIT消息,
//当GetMessage函数取出MSG_QUIT消息时将返回0.,最终导致程序退出消息循环
return 0;
}
return DefaultMainWinProc(hWnd, message, wParam, lParam); //默认处理
}
/**********************************************************************************/
以上是完整的helloworld程序,可以直接用。
接下来是对话框,有模态对话框和非模态对话框之分。
模态对话框就是显示之后,用户不能再切换到其他主窗口进行工作的对话框,而只能在关闭之后,才能使用其他的主窗口。MiniGUI 中,使用 DialogBoxIndirectParam 函数建立的对话框就是模态对话框。实际上,该对话框首先根据模板建立对话框,然后禁止其托管主窗口,并在主窗口的 MSG_CREATE 消息中创建控件,之后发送 MSG_INITDIALOG 消息给回调函数,最终建立一个新的消息循环,并进入该消息循环,直到程序调用 EndDialog 函数为止。
非模态对话框在利用对话框模板中的数据建立主窗口之后,会立即返回。使用 CreateMainWindowIndirect函数建立普通的主窗口。
对话框模板
在 MiniGUI 中,用两个结构来表示对话框模板(<minigui/window.h>) ,如下所示:
typedef struct
{
char* class_name; // control class
DWORD dwStyle; // control style
int x, y, w, h; // control position in dialog
int id; // control identifier
const char* caption; // control caption
DWORD dwAddData; // additional data
DWORD dwExStyle; // control extended style
} CTRLDATA; //用来定义控件
typedef CTRLDATA* PCTRLDATA;
typedef struct
{
DWORD dwStyle; // dialog box style
DWORD dwExStyle; // dialog box extended style
int x, y, w, h; // dialog box position
const char* caption; // dialog box caption
HICON hIcon; // dialog box icon
HMENU hMenu; // dialog box menu
int controlnr; // number of controls
PCTRLDATA controls; // poiter to control array
DWORD dwAddData; // addtional data, must be zero
} DLGTEMPLATE; //用来定义对话框本身
typedef DLGTEMPLATE* PDLGTEMPLATE;
在程序中,应该首先利用 CTRLDATA 定义对话框中所有的控件,并用数组表示。控件在该数组中的顺序,也就是对话框中用户按TAB键时的控件切换顺序。然后定义对话框,指定对话框中的控件数目,并指定 DLGTEMPLATE 结构中的 controls 指针指向定义控件的数组。
例如:
static CTRLDATA ButtonCtrls[BUTTONNUM];
static DLGTEMPLATE ButtonPanel = {
WS_VISIBLE | WS_THINFRAME,
WS_EX_NONE, 0,0,0,0,
"buttonlist",
0,0,
BUTTONNUM, ButtonCtrls,
0
};
ButtonPanel.controls = ButtonCtrls;
定义完对话框模板数据后,需要定义对话框的回调函数。
对话框操作函数 (不一定仅在对话框中可用)
函数名称 用途 备注
DestroyAllControls 销毁所有的子窗口
GetDlgCtrlID 根据控件句柄获取控件标识符
GetDlgItem 根据控件标识符获取控件句柄
GetDlgItemInt 获取控件文本并转换为整数值
SetDlgItemInt 根据整数值设置控件文本
GetDlgItemText 获取子控件文本 功能同 GetWindowText
GetDlgItemText2 获取子控件文本 根据文本长度自动分配内存,应用程序负责释放该内存
SetDlgItemText 设置子控件文本 功能同 SetWindowText
GetNextDlgGroupItem 获取下一个同组子控件 用于遍历同组控件,参阅 WS_GROUP 风格
GetNextDlgTabItem 获取下一个“TAB 键停止”子控件 用于TAB键游历控件,参阅WS_TABSTOP风格
SendDlgItemMessage 向子控件发送消息 功能同 SendMessage
CheckDlgButton 设置检查框子控件的选中状态
CheckRadioButton 设置单选按钮子控件的选中状态
IsDlgButtonChecked 检查子按钮是否选中
GetDlgDefPushButton 获取当前默认子按钮
通用窗口操作函数
函数名称 用途 备注
UpdateWindow 立即更新某个窗口
ShowWindow 显示或隐藏某个窗口
IsWindowVisible 判断某个窗口是否可见 控件和主窗口均可用
EnableWindow 使能或禁止某个窗口
IsWindowEnabled 判断某个窗口是否可用
GetClientRect 获取窗口客户区矩形
GetWindowRect 获取窗口矩形 屏幕坐标系中的窗口尺寸
GetWindowBkColor 获取窗口背景色
SetWindowBkColor 设置窗口背景色
GetWindowFont 获取窗口默认字体
SetWindowFont 设置窗口默认字体
GetWindowCursor 获取窗口光标
SetWindowCursor 设置窗口光标
GetWindowStyle 获取窗口风格
GetWindowExStyle 获取窗口扩展风格
GetFocusChild 获取拥有输入焦点的子窗口
SetFocusChild 设置焦点子窗口
GetWindowCallbackProc 获取窗口过程函数
SetWindowCallbackProc 设置窗口过程函数
GetWindowAdditionalData 获取窗口附加数据一
SetWindowAdditionalData 设置窗口附加数据一
GetWindowAdditionalData2 获取窗口附加数据二
SetWindowAdditionalData2 设置窗口附加数据二
对话框和控件在内部已使用附加数据二,保留附加数据一给应用程序使用
GetWindowCaption 获取窗口标题 通常用于主窗口
SetWindowCaption 设置窗口标题 通常用于主窗口
InvalidateRect 使窗口的给定矩形区域无效 将引发窗口重绘
GetUpdateRect 获取窗口当前的无效区域外包矩形
ClientToScreen 将窗口客户区坐标转换为屏幕坐标
ScreenToClient 将屏幕坐标转换为客户区坐标
WindowToScreen 将窗口坐标转换为屏幕坐标
ScreenToWindow 将屏幕坐标转换为窗口坐标
IsMainWindow 判断给定窗口是否为主窗口
IsControl 判断给定窗口是否为控件
IsDialog 判断给定窗口是否为对话框
GetParent 获取窗口的父窗口句柄 主窗口的父窗口永远为HWND_DESKTOP
GetMainWindowHandle 返回包含某个窗口的主窗口句柄
GetNextChild 获取下一个子窗口 用于遍历某个窗口的所有子窗口
GetNextMainWindow 获取下一个主窗口句柄 用于遍历所有主窗口
GetHosting 获取某个主窗口的托管窗口
GetFirstHosted 获取某个主窗口的第一个被托管窗口
GetNextHosted 获取下一个被托管窗口 用于遍历某个主窗口的所有被托管窗口
GetActiveWindow 获取当前活动主窗口
SetActiveWindow 设置当前活动主窗口
GetCapture 获取当前捕获鼠标的窗口
SetCapture 捕获鼠标
ReleaseCapture 释放鼠标
MoveWindow 移动窗口或改变窗口大小
ScrollWindow 滚动窗口客户区的内容
事件钩子
通常情况下,键盘事件和鼠标事件以其正常的途径从底层设备传递到最终的应用程序窗口过程中进行处理。MiniGUI 提供了一种机制,使得我们可以在这些事件转换成相应的消息并传递到具体的窗口之前截获这些事件,然后有两种选择:让事件继续沿着正常的路径传递;或者打断事件的传递。这种机制就是钩子机制。
钩子其实是一个回调函数,如果应用程序注册有钩子,系统就会在传递消息的中途调用这个回调函数,然后根据该回调函数的返回值来判断是否继续传递消息。
MiniGUI-Threads 和 MiniGUI-Standalone 模式下定义的钩子回调函数的原型如下所示:
typedef int (* MSGHOOK)(void* context, HWND dst_wnd, int msg, WPARAM wparam, LPARAM lparam);
其中,context 是注册钩子时传入的一个上下文信息,可以是一个指针;dst_wnd 是该消息的目标主窗口;msg 是消息标识符;wparam 和 lparam 是消息的两个参数。钩子函数的返回值决定了系统是否继续传递事件:返回 HOOK_GOON 将继续传递事件;返回 HOOK_STOP 将停止事件的继续传递。
注册键盘和鼠标事件的钩子函数:
MSGHOOK GUIAPI RegisterKeyMsgHook (void* context, MSGHOOK hook);
MSGHOOK GUIAPI RegisterMouseMsgHook (void* context, MSGHOOK hook);
调用这两个函数时,只需传入上下文信息以及钩子回调函数的指针即可。成功时会返回先前注册的钩子函数指针。如果想注销先前注册的钩子函数,只需为 hook 参数传入 NULL :RegisterKeyMsgHook (0, old_hook);
窗口重绘
一般来说,在以下情况下,MiniGUI程序的窗口过程会接收到一个MSG_PAINT消息:
1、用户移动窗口或显示窗口时,MiniGUI向先前被隐藏的窗口发送MSG_PAINT消息;
2、程序使用InvalidateRect函数来更新窗口的无效区域,这将产生一个MSG_PAINT消息;
3、程序调用UpdateWindow函数来重绘窗口;
4、覆盖程序窗口的对话框或消息框被消除;
5、下拉或弹出菜单被消除。
基本绘图函数
void GUIAPI SetPixel (HDC hdc, int x, int y, gal_pixel c);
void GUIAPI SetPixelRGB (HDC hdc, int x, int y, int r, int g, int b);
gal_pixel GUIAPI GetPixel (HDC hdc, int x, int y);
void GUIAPI GetPixelRGB (HDC hdc, int x, int y, int* r, int* g, int* b);
gal_pixel GUIAPI RGB2Pixel (HDC hdc, int r, int g, int b);
void GUIAPI LineTo (HDC hdc, int x, int y);
void GUIAPI MoveTo (HDC hdc, int x, int y);
void GUIAPI Circle (HDC hdc, int x, int y, int r);
void GUIAPI Rectangle (HDC hdc, int x0, int y0, int x1, int y1);
菜单
定时器
在 MiniGUI 中,应用程序可以调用 SetTimer 函数创建定时器。SetTimer 的第三个参数用来指定定时器的间隔,默认以 10 毫秒为单位。
当创建的定时器到期时,创建定时器时指定的窗口就会收到 MSG_TIMER 消息,并传递到期的定时器标识号。
在不需要定时器时,应用程序可以调用 KillTimer 函数删除定时器。
应用程序还可以调用 ResetTimer 函数重新设定定时器的间隔。
IsTimerInstalled 函数用于检查一个定时器是否被安装到指定的窗口上。
HaveFreeTimer 用于检测系统中是否还有可用的定时器资源。
其他一些说明
1、设备字体
2、文本输出
3、颜色
4、输入框限制
SetWindowCallbackProc(hwndedit, RestrictedEditBox);