Xorg Handler簡介

簡介

Xorg Handler用於處理各種類型的回調函數,Xorg一共支持四種類型的Handler:

  • BlockHandler: 在阻塞之前調用;
  • WakeupHandler: 在喚醒之後調用;
  • InputHandler: 用於鍵盤鼠標等輸入設備;
  • GeneralHandler: 用於其它類型的設備;

其中BlockHandler和WakeupHandler屬於同一種類型,它們的回調函數不需要關聯設備文件句柄,並且一旦註冊上就會無條件執行。

InputHandler和GeneralHandler屬於另一種類型,它們的回調函數需要關聯到某一個設備句柄(例如鍵盤設備文件),當設備句柄有數據到達時,可以馬上喚醒阻塞中的Xorg,並且只有在設備句柄可讀/可寫時,對應的回調函數纔會執行。

註冊BlockHandler和WakeupHandler

RegisterBlockAndWakeupHandlers

BlockHandler和WakeupHandler通過調用RegisterBlockAndWakeupHandlers函數註冊,該函數同時註冊兩個回調函數,通過往static數組handlers增加一個成員實現:

Bool
RegisterBlockAndWakeupHandlers(BlockHandlerProcPtr blockHandler,
                               WakeupHandlerProcPtr wakeupHandler,
                               void *blockData)
{
    BlockHandlerPtr new;

    if (numHandlers >= sizeHandlers) {
        new = (BlockHandlerPtr) realloc(handlers, (numHandlers + 1) *
                                        sizeof(BlockHandlerRec));
        if (!new)
            return FALSE;
        handlers = new;
        sizeHandlers = numHandlers + 1;
    }
    handlers[numHandlers].BlockHandler = blockHandler;
    handlers[numHandlers].WakeupHandler = wakeupHandler;
    handlers[numHandlers].blockData = blockData;
    handlers[numHandlers].deleted = FALSE;
    numHandlers = numHandlers + 1;
    return TRUE;
}

註冊到Screen

除了調用上面的函數註冊到handlers,也可以通過直接修改指針註冊到Screen,典型代碼如下:

/*
 * Setup points to the block and wakeup handlers.  Pass a pointer
 * to the current screen as pWakeupdata.
 */
pScreen->BlockHandler = winBlockHandler;
pScreen->WakeupHandler = winWakeupHandler;

如果使用直接修改指針的方式註冊,需要在修改之前保存原始指針,退出時進行還原。這兩種註冊方式使用場景也有差別,第二種註冊的回調函數與對應的Screen綁定,第一種註冊方式沒有綁定。

註冊InputHandler

InputHandler通過調用xf86AddInputHandler函數註冊,該函數在InputHandlers鏈表中增加一個成員。

static void *
addInputHandler(int fd, InputHandlerProc proc, void *data)
{
    IHPtr ih;

    if (fd < 0 || !proc)
        return NULL;

    ih = calloc(sizeof(*ih), 1);
    if (!ih)
        return NULL;

    ih->fd = fd;
    ih->ihproc = proc;
    ih->data = data;
    ih->enabled = TRUE;

    ih->next = InputHandlers;
    InputHandlers = ih;

    return ih;
}

void *
xf86AddInputHandler(int fd, InputHandlerProc proc, void *data)
{
    IHPtr ih = addInputHandler(fd, proc, data);

    if (ih) {
        AddEnabledDevice(fd);
        ih->is_input = TRUE;
    }
    return ih;
}

註冊GeneralHandler

GeneralHandler通過調用xf86AddGeneralHandler函數註冊。該函數與xf86AddInputHandler一樣,內部也是通過調用addInputHandler實現,不同的是InputHandler被標記爲輸入設備,除此之外沒有其它差別。

void *
xf86AddGeneralHandler(int fd, InputHandlerProc proc, void *data)
{
    IHPtr ih = addInputHandler(fd, proc, data);

    if (ih)
        AddGeneralSocket(fd);
    return ih;
}

Handler的執行

Handler執行流程如下圖所示,在WaitForSomething函數中先調用BlockHandler執行所有block類型的handler,然後執行select等待輸入事件,select阻塞結束後執行WakeupHandler執行所有wakeup類型的handler。InputHandler和GeneralHandler由xf86Wakeup函數執行,該函數在Xorg初始化時會被註冊爲一個WakeupHandler。

執行流程圖

BlockHandler函數

void
BlockHandler(void *pTimeout, void *pReadmask)
{
    int i, j;

    ++inHandler;
    for (i = 0; i < screenInfo.numScreens; i++)
        (*screenInfo.screens[i]->BlockHandler) (screenInfo.screens[i],
                                                pTimeout, pReadmask);
    for (i = 0; i < screenInfo.numGPUScreens; i++)
        (*screenInfo.gpuscreens[i]->BlockHandler) (screenInfo.gpuscreens[i],
                                                   pTimeout, pReadmask);
    for (i = 0; i < numHandlers; i++)
        if (!handlers[i].deleted)
            (*handlers[i].BlockHandler) (handlers[i].blockData,
                                         pTimeout, pReadmask);
    if (handlerDeleted) {
        for (i = 0; i < numHandlers;)
            if (handlers[i].deleted) {
                for (j = i; j < numHandlers - 1; j++)
                    handlers[j] = handlers[j + 1];
                numHandlers--;
            }
            else
                i++;
        handlerDeleted = FALSE;
    }
    --inHandler;
}

WakeupHandler函數

void
WakeupHandler(int result, void *pReadmask)
{
    int i, j;

    ++inHandler;
    for (i = numHandlers - 1; i >= 0; i--)
        if (!handlers[i].deleted)
            (*handlers[i].WakeupHandler) (handlers[i].blockData,
                                          result, pReadmask);
    for (i = 0; i < screenInfo.numScreens; i++)
        (*screenInfo.screens[i]->WakeupHandler) (screenInfo.screens[i],
                                                 result, pReadmask);
    for (i = 0; i < screenInfo.numGPUScreens; i++)
        (*screenInfo.gpuscreens[i]->WakeupHandler) (screenInfo.gpuscreens[i],
                                                    result, pReadmask);
    if (handlerDeleted) {
        for (i = 0; i < numHandlers;)
            if (handlers[i].deleted) {
                for (j = i; j < numHandlers - 1; j++)
                    handlers[j] = handlers[j + 1];
                numHandlers--;
            }
            else
                i++;
        handlerDeleted = FALSE;
    }
    --inHandler;
}

xf86Wakeup函數

Xorg在初始化時調用RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr) NoopDDA, xf86Wakeup, NULL);註冊xf86Wakeup。

void
xf86Wakeup(void *blockData, int err, void *pReadmask)
{
    fd_set *LastSelectMask = (fd_set *) pReadmask;
    fd_set devicesWithInput;
    InputInfoPtr pInfo;

    if (err >= 0) {

        XFD_ANDSET(&devicesWithInput, LastSelectMask, &EnabledDevices);
        if (XFD_ANYSET(&devicesWithInput)) {
            pInfo = xf86InputDevs;
            while (pInfo) {
                if (pInfo->read_input && pInfo->fd >= 0 &&
                    (FD_ISSET(pInfo->fd, &devicesWithInput) != 0)) {
                    OsBlockSIGIO();

                    /*
                     * Remove the descriptior from the set because more than one
                     * device may share the same file descriptor.
                     */
                    FD_CLR(pInfo->fd, &devicesWithInput);

                    pInfo->read_input(pInfo);
                    OsReleaseSIGIO();
                }
                pInfo = pInfo->next;
            }
        }
    }

    if (err >= 0) {             /* we don't want the handlers called if select() */
        IHPtr ih, ih_tmp;       /* returned with an error condition, do we?      */

        nt_list_for_each_entry_safe(ih, ih_tmp, InputHandlers, next) {
            if (ih->enabled && ih->fd >= 0 && ih->ihproc &&
                (FD_ISSET(ih->fd, ((fd_set *) pReadmask)) != 0)) {
                ih->ihproc(ih->fd, ih->data);
            }
        }
    }

    if (xf86VTSwitchPending())
        xf86VTSwitch();
}

公衆號二維碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章