在文件系统 /usr/etc/nanoX.local 的脚本中有如下一条语句:
这语句是 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(); while (1) { 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 的按键部分就讲完,有兴趣可以理清一下细节的东西.
阅读(2087) | 评论(0) | 转发(0) |