Chinaunix首页 | 论坛 | 博客
  • 博客访问: 974404
  • 博文数量: 192
  • 博客积分: 3070
  • 博客等级: 中校
  • 技术积分: 1861
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-27 23:44
个人简介

Start Linux Leave Linux a while Back to Linux

文章分类

全部博文(192)

文章存档

2023年(18)

2022年(11)

2021年(8)

2020年(14)

2019年(7)

2018年(13)

2017年(16)

2016年(4)

2012年(2)

2011年(13)

2010年(26)

2009年(13)

2008年(27)

2007年(20)

我的朋友

分类: LINUX

2008-09-05 22:54:12

在文件系统 /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();
  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 的按键部分就讲完,有兴趣可以理清一下细节的东西.

 

 

阅读(2094) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~