目錄
簡介
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();
}