深入了解microwindows(一)---按键篇
在文件系统 /usr/etc/nanoX.local 的脚本中有如下一条语句:
./ebrmain/ebrmain
这语句是 microwindows 的入口, ebrmain.c 经编译之后链接 生成 ebrmain 文件, 最终生成 nanoX.img 文件(AP程序).
所以首先进入 ebrmain.c 文件中的 main() 函数.
这个函数不是很长,但涉及的东西比较多,我们一段一段来分析.
int main()
{
GR_EVENT event;
if (GrOpen() 0) {
fprintf (stderr, "GrOpen failed");
return 0;
}
CurFrameState.wid = GrNewWindowEx(GR_WM_PROPS_APPWINDOW, NULL,GR_ROOT_WINDOW_ID, 0, 0, ScreenWidth, ScreenHeight, WHITE);
GrSelectEvents(CurFrameState.wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE | GR_EVENT_MASK_KEY_DOWN);
GrMapWindow(CurFrameState.wid);
CurFrameState.gc = GrNewGC();
[color=#0000cc] while (1) {
[/color] GrCheckNextEvent(&event);
event_handler (&event);
usleep(50000);
}
//GrMainLoop(event_handler);
int ret;
//waiting for the pthread_main exit
ret = pthread_join(pthread_main_id , NULL);
if (ret != 0) {
fprintf(stderr, "Error waiting for to join. Error %i.\n", ret);
return 0;
}
GrDestroyFont(CurFrameState.font);
GrDestroyWindow(CurFrameState.wid);
GrDestroyGC(CurFrameState.gc);
GrClose();
return 0;
}
GrOpen() 在 ./src/nanox/srvmain.c 文件中定义.
int GrOpen(void)
{
SERVER_LOCK();
escape_quits = 1;
/* Client calls this routine once. We
* init everything here
*/
if (connectcount = 0) {
if(GsInitialize() 0) {
SERVER_UNLOCK();
return -1;
}
GsAcceptClientFd(999);
curclient = root_client;
}
SERVER_UNLOCK();
return 1;
}
GsInitialize() 函数用于bind 按键和图形,
/*
* Initialize the graphics and mouse devices at startup.
* Returns nonzero with a message printed if the initialization failed.
*/
int GsInitialize(void)
{
GR_WINDOW *wp; /* root window */
PSD psd;
GR_CURSOR_ID cid;
static const MWIMAGEBITS cursorbits[16] = {
0xe000, 0x9800, 0x8600, 0x4180,
0x4060, 0x2018, 0x2004, 0x107c,
0x1020, 0x0910, 0x0988, 0x0544,
0x0522, 0x0211, 0x000a, 0x0004
};
static const MWIMAGEBITS cursormask[16] = {
0xe000, 0xf800, 0xfe00, 0x7f80,
0x7fe0, 0x3ff8, 0x3ffc, 0x1ffc,
0x1fe0, 0x0ff0, 0x0ff8, 0x077c,
0x073e, 0x021f, 0x000e, 0x0004
};
/* If needed, initialize the server mutex. */
SERVER_LOCK_INIT();
setbuf(stdout, NULL);
setbuf(stderr, NULL);
wp = (GR_WINDOW *) malloc(sizeof(GR_WINDOW));
if (wp == NULL) {
EPRINTF("Cannot allocate root window\n");
return -1;
}
startTicks = GsGetTickCount();
#ifndef MW_NOSIGNALS
/* catch terminate signal to restore tty state*/
signal(SIGTERM, (void *)GsTerminate);
#endif
#if MW_FEATURE_TIMERS
screensaver_delay = 0;
#endif
screensaver_active = GR_FALSE;
selection_owner.wid = 0;
selection_owner.typelist = NULL;
#if !NONETWORK
#ifndef MW_NOSIGNALS
/* ignore pipe signal, sent when clients exit*/
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
#endif
if (GsOpenSocket() 0) {
EPRINTF("Cannot bind to named socket\n");
free(wp);
return -1;
}
#endif
EPRINTF("initialise keyboard\n");
if ((keyb_fd = GdOpenKeyboard()) == -1) {
EPRINTF("Cannot initialise keyboard\n");
/*GsCloseSocket();*/
free(wp);
return -1;
}
if ((psd = GdOpenScreen()) == NULL) {
EPRINTF("Cannot initialise screen\n");
/*GsCloseSocket();*/
GdCloseKeyboard();
free(wp);
return -1;
}
GdSetPortraitMode(psd, portraitmode);
if ((mouse_fd = GdOpenMouse()) == -1) {
EPRINTF("Cannot initialise mouse\n");
/*GsCloseSocket();*/
GdCloseScreen(psd);
GdCloseKeyboard();
free(wp);
return -1;
}
/*
* Create std font.
*/
stdfont = GdCreateFont(psd, MWFONT_SYSTEM_VAR, 0, NULL);
/*
* Initialize the root window.
*/
wp->psd = psd;
wp->id = GR_ROOT_WINDOW_ID;
wp->parent = NULL; /* changed: was = NULL*/
wp->owner = NULL;
wp->children = NULL;
wp->siblings = NULL;
wp->next = NULL;
wp->x = 0;
wp->y = 0;
wp->width = psd->xvirtres;
wp->height = psd->yvirtres;
wp->bordersize = 0;
wp->background = BLACK;
wp->bordercolor = wp->background;
wp->nopropmask = 0;
wp->bgpixmap = NULL;
wp->bgpixmapflags = GR_BACKGROUND_TILE;
wp->eventclients = NULL;
wp->cursorid = 0;
wp->mapped = GR_TRUE;
wp->realized = GR_TRUE;
wp->output = GR_TRUE;
wp->props = 0;
wp->title = NULL;
wp->clipregion = NULL;
listpp = NULL;
listwp = wp;
rootwp = wp;
focuswp = wp;
mousewp = wp;
focusfixed = GR_FALSE;
/*
* Initialize and position the default cursor.
*/
curcursor = NULL;
cursorx = -1;
cursory = -1;
GdShowCursor(psd);
GrMoveCursor(psd->xvirtres / 2, psd->yvirtres / 2);
cid = GrNewCursor(16, 16, 0, 0, WHITE, BLACK, (MWIMAGEBITS *)cursorbits,
(MWIMAGEBITS *)cursormask);
GrSetWindowCursor(GR_ROOT_WINDOW_ID, cid);
stdcursor = GsFindCursor(cid);
psd->FillRect(psd, 0, 0, psd->xvirtres-1, psd->yvirtres-1,
GdFindColor(psd, wp->background));
/*
* Tell the mouse driver some things.
*/
curbuttons = 0;
GdRestrictMouse(0, 0, psd->xvirtres - 1, psd->yvirtres - 1);
GdMoveMouse(psd->xvirtres / 2, psd->yvirtres / 2);
/* Force root window screen paint*/
GsRedrawScreen();
/*
* Force the cursor to appear on the screen at startup.
* (not required with above GsRedrawScreen)
GdHideCursor(psd);
GdShowCursor(psd);
*/
/*
* All done.
*/
connectcount = 0;
return 0;
}
GdOpenKeyboard() 函数在 src/engine/devkbd.c 文件中定义
/**
* Open the keyboard.
*
* @return Fd (>0) on success, -1 on error, -2 if no keyboard.
*/
int GdOpenKeyboard(void)
{
int fd;
if ((fd = kbddev.Open(&kbddev)) == -1)
return -1;
/* possible -2 return means no kbd*/
return fd;
}
kbddev 的定义 src/drivers/kbd_tty.c 文件中, 该文件有对各成员函数的具体实现的代码.
KBDDEVICE kbddev = {
TTY_Open,
TTY_Close,
TTY_GetModifierInfo,
TTY_Read,
#if _MINIX
TTY_Poll
#else
NULL
#endif
};
如TTY_Open() 的实现如下:
/*
* Open the keyboard.
* This is real simple, we just use a special file handle
* that allows non-blocking I/O, and put the terminal into
* character mode.
*/
static int
TTY_Open(KBDDEVICE *pkd)
{
char *kbd;
kbd="/dev/button";
fd = open(kbd, O_RDONLY | O_NONBLOCK);
return fd;
}
其他成员函数也类似.
那kbddev 这个结构体己经初始化了那其他文件如何调用呢?
在kbd_tty.c 文件中,
#include "device.h" ---> extern KBDDEVICE kbddev;
只要包括了 #include "device.h" 这个头文件 就可以调用 kbddev中的成员函数,回到 srvmain.c 文件,其包含了
#include "serv.h" ---> #include "device.h"
所以GdOpenKeyboard() 中kbddev.open() 之后, 以后就可以进行 read 操作了(读键值). 至此,按键初始化完成.
回到 main() 函数, GrOpen() 之后还要进行窗口的初始化,先跳过这几个函数,
while (1) {
GrCheckNextEvent(&event);
event_handler (&event);
usleep(50000);
}
while 循环中 不断地检查有无有新的事件(GrCheckNextEvent),然后进行事件的处理(event_handler),后面加一个 sleep 防止CPU始终处于 100%利用率中.
现在分析GrCheckNextEvent()和event_handler() 这两个函数.
GrCheckNextEvent() 在 src/nanox/srvfunc.c 文件中定义,
/*
* Return the next event from the event queue if one is ready.
* If one is not ready, then the type GR_EVENT_TYPE_NONE is returned.
* If it is an error event, then a user-specified routine is called
* if it was defined, otherwise we clean up and exit.
*/
void GrCheckNextEvent(GR_EVENT *ep)
{
SERVER_LOCK();
CheckNextEvent(ep, GR_TRUE);
SERVER_UNLOCK();
}
如果有新的事件产生,GrCheckNextEvent会从事件队列中取出,反之,返回 GR_EVNET_TYPE_NONE.
GrCheckNextEvent->CheckNextEvent
CheckNextEvent 的实现代码:
static void CheckNextEvent(GR_EVENT *ep, GR_BOOL doCheckEvent)
{
GR_EVENT_LIST * elp;
/* Copy first event if any*/
if(!GrPeekEvent(ep))
return;
/* Get first event again*/
elp = curclient->eventhead;
/* if GrCheckEvent, turn timeouts into no event*/
if (doCheckEvent && elp->event.type == GR_EVENT_TYPE_TIMEOUT)
ep->type = GR_EVENT_TYPE_NONE;
/* Remove first event from queue*/
curclient->eventhead = elp->next;
if (curclient->eventtail == elp)
curclient->eventtail = NULL;
elp->next = eventfree;
eventfree = elp;
}
GrCheckNextEvent->CheckNextEvent->GrPeekEvent.
/*
* Peek at the event queue for the current client to see if there are any
* outstanding events. Returns the event at the head of the queue, or
* else a null event type. The event is still left in the queue, however.
*/
int GrPeekEvent(GR_EVENT *ep)
{
GR_EVENT_LIST * elp;
SERVER_LOCK();
elp = curclient->eventhead;
/* if no events on queue, force select() event check*/
if (elp == NULL) {
GsSelect(-1L); /* poll*/
elp = curclient->eventhead;
}
if(elp == NULL) {
ep->type = GR_EVENT_TYPE_NONE;
SERVER_UNLOCK();
return 0;
}
/* copy event out*/
*ep = elp->event;
SERVER_UNLOCK();
return 1;
}
GrCheckNextEvent->CheckNextEvent->GrPeekEvent->GsSelect.
GsSelect() 函数在 src/nanox/srvmain.c 文件中定义,用于检测 I/O端口的状态是否有变化.
void GsSelect(GR_TIMEOUT timeout)
{
int mouseevents = 0;
int keybdevents = 0;
GR_TIMEOUT waittime = 0;
GR_EVENT_GENERAL *gp;
/* input gathering loop */
while (1)
{
/* If keyboard data present, service it */
while (kbddev.Poll() > 0)
{
GsCheckKeyboardEvent();
if (keybdevents++ > MAX_KEYBDEVENTS)
{
/* don't handle too many events at one shot */
break;
}
}
/* did we process any input yet? */
if ((mouseevents > 0) || (keybdevents > 0))
{
/* yep -- return without sleeping */
return;
}
/* give up time-slice & sleep for a bit */
GdSleep(POLLTIME);
waittime += POLLTIME;
/* have we timed out? */
if (waittime >= timeout)
{
/* special case: polling when timeout == 0 -- don't send timeout event */
if (timeout != 0)
{
/* Timeout has occured.
** Currently return a timeout event regardless of whether client
** has selected for it.
*/
if ((gp = (GR_EVENT_GENERAL *)GsAllocEvent(curclient)) != NULL)
{
gp->type = GR_EVENT_TYPE_TIMEOUT;
}
}
return;
}
}
}
GsSelect函数还有对 触摸屏(scrdev)和鼠标(mousedev)事件的处理,这里没有用到,把它去掉了,只对按键事件进行分析,触摸屏和鼠标等输入设备的实现应该也跟按键事件差不多的.
GrCheckNextEvent->CheckNextEvent->GrPeekEvent->GsSelect->GsCheckKeyboardEvent.
GsCheckKeyboardEvent() 函数在 src/nanox/srvevent.c 文件中定义,
/*
* Update keyboard status and issue events on it if necessary.
* This function doesn't block, but is normally only called when
* there is known to be some data waiting to be read from the keyboard.
*/
GR_BOOL GsCheckKeyboardEvent(void)
{
MWKEY mwkey; /* latest character */
MWKEYMOD modifiers; /* latest modifiers */
MWSCANCODE scancode;
int keystatus; /* latest keyboard status */
/* Read the latest keyboard status: */
keystatus = GdReadKeyboard(&mwkey, &modifiers, &scancode);
if(keystatus 0) {
if(keystatus == -2) /* special case return code*/
GsTerminate();
GsError(GR_ERROR_KEYBOARD_ERROR, 0);
return FALSE;
} else if(keystatus) { /* Deliver events as appropriate: */
switch (mwkey) {
case MWKEY_QUIT:
GsTerminate();
/* no return*/
case MWKEY_REDRAW:
GsRedrawScreen();
break;
case MWKEY_PRINT:
if (keystatus == 1)
GdCaptureScreen("screen.bmp");
break;
}
GsDeliverKeyboardEvent(0,
(keystatus==1?
GR_EVENT_TYPE_KEY_DOWN: GR_EVENT_TYPE_KEY_UP),
mwkey, modifiers, scancode);
return TRUE;
}
return FALSE;
}
GrCheckNextEvent->CheckNextEvent->GrPeekEvent->GsSelect->GsCheckKeyboardEvent->GdReadKeyboard
GdReadKeyboard() 在 src/engine/devkbd.c 文件中定义,读键值.
/**
* This reads one keystroke from the keyboard, and the current state of
* the mode keys (ALT, SHIFT, CTRL). Returns -1 on error, 0 if no data
* is ready, 1 if keypress, 2 if keyrelease.
* This is a non-blocking call. Returns -2 if ESC pressed.
*
* @param buf Recieves the key.
* @param modifiers Recieves the state of the modifiers (shift etc).
* @param scancode Recieves the scancode of the key.
* @return -1 on error, 0 if no data is ready,
* 1 if keypress, 2 if keyrelease.
*/
int
GdReadKeyboard(MWKEY *buf, MWKEYMOD *modifiers, MWSCANCODE *scancode)
{
int result = kbddev.Read(buf, modifiers, scancode);
return result;
}
这里说一下这几个参数的作用:
buf: 读回来的键值.
modifiers: 如果你的键盘有用到组合键就需要这个参数,比如,你按 alt+F4 关闭窗口,modifiers 这个参数就保存了 alt 的键值.
scancode: 实际的键值,从 kernel中传递过来的.
GrCheckNextEvent->CheckNextEvent->GrPeekEvent->GsSelect->GsCheckKeyboardEvent->GdReadKeyboard->kbddev.Read
kbddev.Read 在 src/drivers/kbd_tty.c 文件中定义,这里简单的说一下Read 方法
/*
* This reads one keystroke from the keyboard, and the current state of
* the modifier keys (ALT, SHIFT, etc). Returns -1 on error, 0 if no data
* is ready, 1 on a keypress, and 2 on keyrelease.
* This is a non-blocking call.
*/
static int TTY_Read(MWKEY *kbuf, MWKEYMOD *modifiers, MWSCANCODE *scancode)
{
unsigned long keyvalue;
static unsigned long prekey=0;
MWKEY mwkey=0;
read(fd, &keyvalue, 4);
if(keyvalue==0x07)
mwkey=Top_Key; // 自己一个键值
else if(keyvalue==0x08)
mwkey=Bottom_Key;
else if(keyvalue==0x09)
mwkey=Left_Key;
else if(keyvalue==0x0A)
mwkey=Right_Key;
else if(keyvalue==0x06)
mwkey=Enter_Key;
*kbuf = mwkey; /* keypress*/
*modifiers = 0; /* no modifiers*/
*scancode = 0; /* no scancode*/
return 1; /* key press */
}
}
至此,读键值的流程大概有一定的了解了,下面讲 按键处理的程序的部分.
回到 main() 函数, event_handler 就是按键处理函数. 这部分比较简单,下面是一个简单的按键处理函数的例子.
void event_handler(GR_EVENT *event)
{
int ret;
switch (event->type) {
case GR_EVENT_TYPE_EXPOSURE:
/* mw 初始化完之后会接收到这个事件, 在这里可以进行一些
* 初始化的动作, 如显示主窗口,创建主线程等等
*/
printf("Initialize nanoX finish!\n");
break;
case GR_EVENT_TYPE_CLOSE_REQ:
GrDestroyFont(CurFrameState.font);
GrDestroyWindow(CurFrameState.wid);
GrDestroyGC(CurFrameState.gc);
GrClose();
exit(0);
break;
case GR_EVENT_TYPE_KEY_DOWN:
switch(event->keystroke.ch) {
case Top_Key:
printf("Got Top_Key\n");
// handler event
break;
case Botton_Key:
printf("Got Botton_Key\n");
// handler event
break;
}
break;
default:
break;
}
}
这就是一个简单的按键处理的函数.
microwindows 的按键部分就讲完,有兴趣可以理清一下细节的东西.
[b]本文来自ChinaUnix博客,如果查看原文请点:[/b][url]http://blog.chinaunix.net/u1/42456/showart_1166034.html[/url]
阅读(1244) | 评论(0) | 转发(0) |