Chinaunix首页 | 论坛 | 博客
  • 博客访问: 497094
  • 博文数量: 78
  • 博客积分: 5131
  • 博客等级: 大校
  • 技术积分: 1468
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-17 16:20
文章分类
文章存档

2012年(1)

2011年(29)

2010年(6)

2009年(24)

2008年(18)

我的朋友

分类: LINUX

2009-06-20 10:32:19

深入了解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]
阅读(1288) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~