Hands-on Projects for the Linux Graphics Subsystem
|
The notion of the 'event' is central to the mechanism of the X Window System. X Client. At Events we read:
From a programmer's point of view, an event reports:
* Something that your program needs to know about, such as user input or information available from other clients. |
We have met for instance MapNotify at section 3.2.2.13. This is event type is described in MapNotify.
The example in this link describes a client that processes Expose events, KeyPress events and mouse button events. In the next text we will discuss the mouse events a by combining the section 3.2.5.1 and the section 5.1.4. Events are posted by the mouse and are processed by the X Server, which sends notification back to the clients. This is the second circle we examine after the X Client request and X Server request dispatching.
As seen in section 5.1.4 upon the pointer movement a MotionNotify event is prepared. The event is next queued via xf86PostButtonEvent(). This input event is processed then by ProcessInputEvents(), which as seen in section 3.2.5.1 resolves to a ProcessPointerEvent() call. This is implemented as:
void #ifdef XKB CoreProcessPointerEvent (register xEvent *xE, register DeviceIntPtr mouse, int count) #else ProcessPointerEvent (register xEvent *xE, register DeviceIntPtr mouse, int count) #endif { register GrabPtr grab = mouse->grab; Bool deactivateGrab = FALSE; register ButtonClassPtr butc = mouse->button; #ifdef XKB XkbSrvInfoPtr xkbi= inputInfo.keyboard->key->xkbInfo; #endif #ifdef XEVIE if(xevieFlag && clients[xevieClientIndex] && !xeviegrabState && (xevieMask & xevieFilters[xE->u.u.type])) { if(xevieEventSent) xevieEventSent = 0; else { xeviemouse = mouse; WriteToClient(clients[xevieClientIndex], sizeof(xEvent), (char *)xE); return; } } #endif if (!syncEvents.playingEvents) NoticeTime(xE) XE_KBPTR.state = (butc->state | ( #ifdef XKB (noXkbExtension ? inputInfo.keyboard->key->state : xkbi->state.grab_mods) #else inputInfo.keyboard->key->state #endif )); { NoticeTime(xE); if (DeviceEventCallback) { DeviceEventInfoRec eventinfo; /* see comment in EnqueueEvents regarding the next three lines */ if (xE->u.u.type == MotionNotify) XE_KBPTR.root = WindowTable[sprite.hotPhys.pScreen->myNum]->drawable.id; eventinfo.events = xE; eventinfo.count = count; CallCallbacks(&DeviceEventCallback, (pointer)&eventinfo); } } if (xE->u.u.type != MotionNotify) { register int key; register BYTE *kptr; int bit; XE_KBPTR.rootX = sprite.hot.x; XE_KBPTR.rootY = sprite.hot.y; key = xE->u.u.detail; kptr = &butc->down[key >> 3]; bit = 1 << (key & 7); switch (xE->u.u.type) { case ButtonPress: mouse->valuator->motionHintWindow = NullWindow; if (!(*kptr & bit)) butc->buttonsDown++; butc->motionMask = ButtonMotionMask; *kptr |= bit; #if !defined(XFree86Server) || !defined(XINPUT) xE->u.u.detail = butc->map[key]; #endif if (xE->u.u.detail == 0) return; if (xE->u.u.detail <= 5) butc->state |= (Button1Mask >> 1) << xE->u.u.detail; filters[MotionNotify] = Motion_Filter(butc); if (!grab) if (CheckDeviceGrabs(mouse, xE, 0, count)) return; break; case ButtonRelease: mouse->valuator->motionHintWindow = NullWindow; if (*kptr & bit) --butc->buttonsDown; if (!butc->buttonsDown) butc->motionMask = 0; *kptr &= ~bit; #if !defined(XFree86Server) || !defined(XINPUT) xE->u.u.detail = butc->map[key]; #endif if (xE->u.u.detail == 0) return; if (xE->u.u.detail <= 5) butc->state &= ~((Button1Mask >> 1) << xE->u.u.detail); filters[MotionNotify] = Motion_Filter(butc); if (!butc->state && mouse->fromPassiveGrab) deactivateGrab = TRUE; break; default: FatalError("bogus pointer event from ddx"); } } else if (!CheckMotion(xE)) return; if (grab) DeliverGrabbedEvent(xE, mouse, deactivateGrab, count); else DeliverDeviceEvents(sprite.win, xE, NullGrab, NullWindow, mouse, count); if (deactivateGrab) (*mouse->DeactivateGrab)(mouse); } |
CheckMotion() checks the mouse motion by comparing the rootX and rootY position the mouse posted (as seen in section 5.1.4) with the x and y coordination of the sprite:
if ((sprite.hotPhys.x != XE_KBPTR.rootX) || (sprite.hotPhys.y != XE_KBPTR.rootY)) { (*sprite.hotPhys.pScreen->SetCursorPosition)(If there is motion the sprite position is updated. miPointerSetCursorPosition() is used as the pScreen->SetCursorPosition routine which calls miPointerUpdate(), this miSpriteMoveCursor(), this miSpriteSetCursor(), this miDCMoveCursor(), the latter finally composes the screen with the cursor on the right position. The events are then delivered to the clients either by DeliverDeviceEvents() or DeliverGrabbedEvent() (it resolves to DeliverDeviceEvents). DeliverDeviceEvents() calls DeliverEventsToWindow(), which calls TryClientEvents() to send the event to the client that is the window owner. This follows a WriteEventsToClient() and then a WriteToClient() call.
|
CheckMotion is implemented as:
static Bool CheckMotion(xEvent *xE) { WindowPtr prevSpriteWin = sprite.win; #ifdef PANORAMIX if(!noPanoramiXExtension) return XineramaCheckMotion(xE); #endif if (xE && !syncEvents.playingEvents) { if (sprite.hot.pScreen != sprite.hotPhys.pScreen) { sprite.hot.pScreen = sprite.hotPhys.pScreen; ROOT = WindowTable[sprite.hot.pScreen->myNum]; } sprite.hot.x = XE_KBPTR.rootX; sprite.hot.y = XE_KBPTR.rootY; if (sprite.hot.x < sprite.physLimits.x1) sprite.hot.x = sprite.physLimits.x1; else if (sprite.hot.x >= sprite.physLimits.x2) sprite.hot.x = sprite.physLimits.x2 - 1; if (sprite.hot.y < sprite.physLimits.y1) sprite.hot.y = sprite.physLimits.y1; else if (sprite.hot.y >= sprite.physLimits.y2) sprite.hot.y = sprite.physLimits.y2 - 1; #ifdef SHAPE if (sprite.hotShape) ConfineToShape(sprite.hotShape, &sprite.hot.x, &sprite.hot.y); #endif #ifdef XEVIE xeviehot.x = sprite.hot.x; xeviehot.y = sprite.hot.y; #endif sprite.hotPhys = sprite.hot; if ((sprite.hotPhys.x != XE_KBPTR.rootX) || (sprite.hotPhys.y != XE_KBPTR.rootY)) { (*sprite.hotPhys.pScreen->SetCursorPosition)( sprite.hotPhys.pScreen, sprite.hotPhys.x, sprite.hotPhys.y, FALSE); } XE_KBPTR.rootX = sprite.hot.x; XE_KBPTR.rootY = sprite.hot.y; } |
sprite is the cursor sprite, defined in the same file as:
static struct { CursorPtr current; BoxRec hotLimits; /* logical constraints of hot spot */ Bool confined; /* confined to screen */ #if defined(SHAPE) || defined(PANORAMIX) RegionPtr hotShape; /* additional logical shape constraint */ #endif BoxRec physLimits; /* physical constraints of hot spot */ WindowPtr win; /* window of logical position */ HotSpot hot; /* logical pointer position */ HotSpot hotPhys; /* physical pointer position */ #ifdef PANORAMIX ScreenPtr screen; /* all others are in Screen 0 coordinates */ RegionRec Reg1; /* Region 1 for confining motion */ RegionRec Reg2; /* Region 2 for confining virtual motion */ WindowPtr windows[MAXSCREENS]; WindowPtr confineWin; /* confine window */ #endif } sprite; /* info about the cursor sprite */ |
Also the following are also defined:
/* * The window trace information is used to avoid having to compute all the * windows between the root and the current pointer window each time a button * or key goes down. The grabs on each of those windows must be checked. */ static WindowPtr *spriteTrace = (WindowPtr *)NULL; #define ROOT spriteTrace[0] #define XE_KBPTR (xE->u.keyButtonPointer) |
Where is *sprite.hotPhys.pScreen->SetCursorPosition function found?
miDCInitialize() calls miSpriteInitialize() as:
screenFuncs were previously obtained from xf86GetPointerScreenFuncs(), as a miDCInitialize() argument. It returns xf86PointerScreenFuncs. Next miSpriteInitialize() calls miPointerInitialize() as:
miPointerInitialize() is implemented as:
miPointerInitialize() sets SetCursorPosition to miPointerSetCursorPosition().
|
miPointerSetCursorPosition() is implemented as:
static Bool miPointerSetCursorPosition(pScreen, x, y, generateEvent) ScreenPtr pScreen; int x, y; Bool generateEvent; { SetupScreen (pScreen); GenerateEvent = generateEvent; /* device dependent - must pend signal and call miPointerWarpCursor */ (*pScreenPriv->screenFuncs->WarpCursor) (pScreen, x, y); if (!generateEvent) miPointerUpdate(); return TRUE; } |
miPointerUpdate() is implemented as:
/* * miPointerUpdate * * Syncronize the sprite with the cursor - called from ProcessInputEvents */ void miPointerUpdate () { ScreenPtr pScreen; miPointerScreenPtr pScreenPriv; CursorPtr pCursor; int x, y, devx, devy; pScreen = miPointer.pScreen; x = miPointer.x; y = miPointer.y; devx = miPointer.devx; devy = miPointer.devy; if (!pScreen) return; pScreenPriv = GetScreenPrivate (pScreen); /* * if the cursor has switched screens, disable the sprite * on the old screen */ if (pScreen != miPointer.pSpriteScreen) { if (miPointer.pSpriteScreen) { miPointerScreenPtr pOldPriv; pOldPriv = GetScreenPrivate (miPointer.pSpriteScreen); if (miPointer.pCursor) { (*pOldPriv->spriteFuncs->SetCursor) (miPointer.pSpriteScreen, NullCursor, 0, 0); } (*pOldPriv->screenFuncs->CrossScreen) (miPointer.pSpriteScreen, FALSE); } (*pScreenPriv->screenFuncs->CrossScreen) (pScreen, TRUE); (*pScreenPriv->spriteFuncs->SetCursor) (pScreen, miPointer.pCursor, x, y); miPointer.devx = x; miPointer.devy = y; miPointer.pSpriteCursor = miPointer.pCursor; miPointer.pSpriteScreen = pScreen; } /* * if the cursor has changed, display the new one */ else if (miPointer.pCursor != miPointer.pSpriteCursor) { pCursor = miPointer.pCursor; if (pCursor->bits->emptyMask && !pScreenPriv->showTransparent) pCursor = NullCursor; (*pScreenPriv->spriteFuncs->SetCursor) (pScreen, pCursor, x, y); miPointer.devx = x; miPointer.devy = y; miPointer.pSpriteCursor = miPointer.pCursor; } else if (x != devx || y != devy) { miPointer.devx = x; miPointer.devy = y; if(!miPointer.pCursor->bits->emptyMask) (*pScreenPriv->spriteFuncs->MoveCursor) (pScreen, x, y); } } |
At miPointerInitialize as second argument (spriteFuncs) it was passed miSpritePointerFuncs, defined as:
_X_EXPORT miPointerSpriteFuncRec miSpritePointerFuncs = { miSpriteRealizeCursor, miSpriteUnrealizeCursor, miSpriteSetCursor, miSpriteMoveCursor, }; |
MoveCursor becomes therefore miSpriteMoveCursor(), implemented as:
static void miSpriteMoveCursor (pScreen, x, y) ScreenPtr pScreen; int x, y; { miSpriteScreenPtr pScreenPriv; pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; miSpriteSetCursor (pScreen, pScreenPriv->pCursor, x, y); } |
pScreenPriv, a pointer to the screen privates is obtained and then its cursor field is passed as argument to miSpriteSetCursor().
miSpriteSetCursor() is implemented as:
static void miSpriteSetCursor (pScreen, pCursor, x, y) ScreenPtr pScreen; CursorPtr pCursor; int x; int y; { miSpriteScreenPtr pScreenPriv; pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr; if (!pCursor) { pScreenPriv->shouldBeUp = FALSE; if (pScreenPriv->isUp) miSpriteRemoveCursor (pScreen); pScreenPriv->pCursor = 0; return; } pScreenPriv->shouldBeUp = TRUE; if (pScreenPriv->x == x && pScreenPriv->y == y && pScreenPriv->pCursor == pCursor && !pScreenPriv->checkPixels) { return; } pScreenPriv->x = x; pScreenPriv->y = y; pScreenPriv->pCacheWin = NullWindow; if (pScreenPriv->checkPixels || pScreenPriv->pCursor != pCursor) { pScreenPriv->pCursor = pCursor; miSpriteFindColors (pScreen); } if (pScreenPriv->isUp) { int sx, sy; /* * check to see if the old saved region * encloses the new sprite, in which case we use * the flicker-free MoveCursor primitive. */ sx = pScreenPriv->x - (int)pCursor->bits->xhot; sy = pScreenPriv->y - (int)pCursor->bits->yhot; if (sx + (int) pCursor->bits->width >= pScreenPriv->saved.x1 && sx < pScreenPriv->saved.x2 && sy + (int) pCursor->bits->height >= pScreenPriv->saved.y1 && sy < pScreenPriv->saved.y2 && (int) pCursor->bits->width + (2 * SPRITE_PAD) == pScreenPriv->saved.x2 - pScreenPriv->saved.x1 && (int) pCursor->bits->height + (2 * SPRITE_PAD) == pScreenPriv->saved.y2 - pScreenPriv->saved.y1 ) { DamageDrawInternal (pScreen, TRUE); miSpriteIsUpFALSE (pScreen, pScreenPriv); if (!(sx >= pScreenPriv->saved.x1 && sx + (int)pCursor->bits->width < pScreenPriv->saved.x2 && sy >= pScreenPriv->saved.y1 && sy + (int)pCursor->bits->height < pScreenPriv->saved.y2)) { int oldx1, oldy1, dx, dy; oldx1 = pScreenPriv->saved.x1; oldy1 = pScreenPriv->saved.y1; dx = oldx1 - (sx - SPRITE_PAD); dy = oldy1 - (sy - SPRITE_PAD); pScreenPriv->saved.x1 -= dx; pScreenPriv->saved.y1 -= dy; pScreenPriv->saved.x2 -= dx; pScreenPriv->saved.y2 -= dy; (void) (*pScreenPriv->funcs->ChangeSave) (pScreen, pScreenPriv->saved.x1, pScreenPriv->saved.y1, pScreenPriv->saved.x2 - pScreenPriv->saved.x1, pScreenPriv->saved.y2 - pScreenPriv->saved.y1, dx, dy); } (void) (*pScreenPriv->funcs->MoveCursor) (pScreen, pCursor, pScreenPriv->saved.x1, pScreenPriv->saved.y1, pScreenPriv->saved.x2 - pScreenPriv->saved.x1, pScreenPriv->saved.y2 - pScreenPriv->saved.y1, sx - pScreenPriv->saved.x1, sy - pScreenPriv->saved.y1, pScreenPriv->colors[SOURCE_COLOR].pixel, pScreenPriv->colors[MASK_COLOR].pixel); miSpriteIsUpTRUE (pScreen, pScreenPriv); DamageDrawInternal (pScreen, FALSE); } else { SPRITE_DEBUG (("SetCursor remove\n")); miSpriteRemoveCursor (pScreen); } } if (!pScreenPriv->isUp && pScreenPriv->pCursor) { SPRITE_DEBUG (("SetCursor restore\n")); miSpriteRestoreCursor (pScreen); } } |
Which is the pScreenPriv->funcs->MoveCursor function?
pScreenPriv is of type miSpriteScreenPtr: CursorPtr pCursor; int x; /* cursor hotspot */ int y; BoxRec saved; /* saved area from the screen */ Bool isUp; /* cursor in frame buffer */ Bool shouldBeUp; /* cursor should be displayed */ WindowPtr pCacheWin; /* window the cursor last seen in */ Bool isInCacheWin; Bool checkPixels; /* check colormap collision */ xColorItem colors[2]; ColormapPtr pInstalledMap; ColormapPtr pColormap; VisualPtr pVisual; miSpriteCursorFuncPtr funcs; DamagePtr pDamage; /* damage tracking structure */ } miSpriteScreenRec, *miSpriteScreenPtr;The pScreenPriv funcs field is set in miSpriteInitialize() as: pScreenPriv->funcs = cursorFuncs;where cursorFuncs is the second argument of miSpriteInitialize() prototype: miSpriteInitialize (pScreen, cursorFuncs, screenFuncs)miSpriteInitialize() was called as: if (!miSpriteInitialize (pScreen, &miDCFuncs, screenFuncs))Therefore the cursorFuncs is miDCFuncs, defined as: static miSpriteCursorFuncRec miDCFuncs = { miDCRealizeCursor, miDCUnrealizeCursor, miDCPutUpCursor, miDCSaveUnderCursor, miDCRestoreUnderCursor, miDCMoveCursor, miDCChangeSave, };miDCMoveCursor is implemented in midispcur.c:
miSpriteCursorFuncRec is defined in misprite.h as: typedef struct { Bool (*RealizeCursor)( ScreenPtr /*pScreen*/, CursorPtr /*pCursor*/ ); Bool (*UnrealizeCursor)( ScreenPtr /*pScreen*/, CursorPtr /*pCursor*/ ); Bool (*PutUpCursor)( ScreenPtr /*pScreen*/, CursorPtr /*pCursor*/, int /*x*/, int /*y*/, unsigned long /*source*/, unsigned long /*mask*/ ); Bool (*SaveUnderCursor)( ScreenPtr /*pScreen*/, int /*x*/, int /*y*/, int /*w*/, int /*h*/ ); Bool (*RestoreUnderCursor)( ScreenPtr /*pScreen*/, int /*x*/, int /*y*/, int /*w*/, int /*h*/ ); Bool (*MoveCursor)( ScreenPtr /*pScreen*/, CursorPtr /*pCursor*/, int /*x*/, int /*y*/, int /*w*/, int /*h*/, int /*dx*/, int /*dy*/, unsigned long /*source*/, unsigned long /*mask*/ ); Bool (*ChangeSave)( ScreenPtr /*pScreen*/, int /*x*/, int /*y*/, int /*w*/, int /*h*/, int /*dx*/, int /*dy*/ ); } miSpriteCursorFuncRec, *miSpriteCursorFuncPtr; |
After the software cursor initialization the hardware cursor initialization takes place in MGAScreenInit(): /* Initialize HW cursor layer. Must follow software cursor initialization*/ if (pMga->HWCursor) { if(!MGAHWCursorInit(pScreen)) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Hardware cursor initialization failed\n"); } |
MGAHWCursorInit() calls xf86InitCursor() as:
return(xf86InitCursor(pScreen, infoPtr));xf86InitCursor() is implemented as:
Bool xf86InitCursor( ScreenPtr pScreen, xf86CursorInfoPtr infoPtr ) { ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; xf86CursorScreenPtr ScreenPriv; miPointerScreenPtr PointPriv; if (xf86CursorGeneration != serverGeneration) { if ((xf86CursorScreenIndex = AllocateScreenPrivateIndex()) < 0) return FALSE; xf86CursorGeneration = serverGeneration; } if (!xf86InitHardwareCursor(pScreen, infoPtr)) return FALSE; ScreenPriv = xcalloc(1, sizeof(xf86CursorScreenRec)); if (!ScreenPriv) return FALSE; pScreen->devPrivates[xf86CursorScreenIndex].ptr = ScreenPriv; ScreenPriv->SWCursor = TRUE; ScreenPriv->isUp = FALSE; ScreenPriv->CurrentCursor = NULL; ScreenPriv->CursorInfoPtr = infoPtr; ScreenPriv->PalettedCursor = FALSE; ScreenPriv->pInstalledMap = NULL; ScreenPriv->CloseScreen = pScreen->CloseScreen; pScreen->CloseScreen = xf86CursorCloseScreen; ScreenPriv->QueryBestSize = pScreen->QueryBestSize; pScreen->QueryBestSize = xf86CursorQueryBestSize; ScreenPriv->RecolorCursor = pScreen->RecolorCursor; pScreen->RecolorCursor = xf86CursorRecolorCursor; if ((infoPtr->pScrn->bitsPerPixel == 8) && !(infoPtr->Flags & HARDWARE_CURSOR_TRUECOLOR_AT_8BPP)) { ScreenPriv->InstallColormap = pScreen->InstallColormap; pScreen->InstallColormap = xf86CursorInstallColormap; ScreenPriv->PalettedCursor = TRUE; } PointPriv = pScreen->devPrivates[miPointerScreenIndex].ptr; ScreenPriv->showTransparent = PointPriv->showTransparent; if (infoPtr->Flags & HARDWARE_CURSOR_SHOW_TRANSPARENT) PointPriv->showTransparent = TRUE; else PointPriv->showTransparent = FALSE; ScreenPriv->spriteFuncs = PointPriv->spriteFuncs; PointPriv->spriteFuncs = &xf86CursorSpriteFuncs; ScreenPriv->SwitchMode = pScrn->SwitchMode; ScreenPriv->EnterVT = pScrn->EnterVT; ScreenPriv->LeaveVT = pScrn->LeaveVT; ScreenPriv->SetDGAMode = pScrn->SetDGAMode; ScreenPriv->ForceHWCursorCount = 0; ScreenPriv->HWCursorForced = FALSE; if (pScrn->SwitchMode) pScrn->SwitchMode = xf86CursorSwitchMode; pScrn->EnterVT = xf86CursorEnterVT; pScrn->LeaveVT = xf86CursorLeaveVT; pScrn->SetDGAMode = xf86CursorSetDGAMode; return TRUE; } |
In xf86InitCursor() PointPriv->spriteFuncs is set to the xf86CursorSpriteFuncs address:
PointPriv->spriteFuncs = &xf86CursorSpriteFuncs;where xf86CursorSpriteFuncs is defined as:
static miPointerSpriteFuncRec xf86CursorSpriteFuncs = { xf86CursorRealizeCursor, xf86CursorUnrealizeCursor, xf86CursorSetCursor, xf86CursorMoveCursor };Notice that PointPriv is of type miPointerScreenPtr, which is defined as:
typedef struct { miPointerSpriteFuncPtr spriteFuncs; /* sprite-specific methods */ miPointerScreenFuncPtr screenFuncs; /* screen-specific methods */ CloseScreenProcPtr CloseScreen; Bool waitForUpdate; /* don't move cursor in SIGIO */ Bool showTransparent; /* show empty cursors */ } miPointerScreenRec, *miPointerScreenPtr;and miPointerScreenFuncPtr is defined as:
typedef struct _miPointerScreenFuncRec { Bool (*CursorOffScreen)( ScreenPtr* /* ppScr */, int* /* px */, int* /* py */ ); void (*CrossScreen)( ScreenPtr /* pScr */, int /* entering */ ); void (*WarpCursor)( ScreenPtr /* pScr */, int /* x */, int /* y */ ); void (*EnqueueEvent)( xEventPtr /* event */ ); void (*NewEventScreen)( ScreenPtr /* pScr */, Bool /* fromDIX */ ); } miPointerScreenFuncRec, *miPointerScreenFuncPtr;
REFERENCES
Events
The Keyboard and Pointer
Events
For the Hardware cursors - Software cursors:
Strategies for Porting the X v11 Sample Server
Cursor Notes
Definition of the Porting Layer
X Server Device Developer's Guide