This is documentation about the cursor handling, not about the cursor rendering!
Generally, a CursorRec gets allocated only once and used multiple times to save memory. Two functions are responsible for allocating cursors: AllocCursorARGB() and AllocGlyphCursor(). The matching Xlib functions would be XCreateCursor() and XCreateGlyphCursor().
The CursorRec contains a number of things, we will focus on the refcnt here. The refcnt is used to track how many instances of the cursor are used and to avoid freeing memory too early. The refcnt is increased when
- a client gets a reference to a cursor
- a window uses the cursor
- a pointer uses the cursor
- a pointer uses the cursor as part of a grab.
When you create a cursor, it will have a refcnt of 1 (the client has a reference to it after all). Each time you use Xlib's XDefineCursor() or XChangeWindowAttributes(), the window obtains a reference to the cursor and increases the refcnt. And each time the pointer passes into a window that has a cursor set, ChangeToCursor() will change the pointer's cursor and increase the refcnt. Using the cursor for a sprite does NOT change the refcnt!
The refcnt is decreased in FreeCursor(). When the refcnt hits 0, the memory for the cursor is freed. Make sure that when you call FreeCursor(), nothing in your codepath references the address anymore. The usual way to do this is something like
CursorPtr pOldCursor = device->spriteInfo->sprite->current; device->spriteInfo->sprite->current = NULL; FreeCursor(pOldCursor, 0);
The value of pOldCursor is undefined after FreeCursor().
FreeCursor() is called from several points. Each time the cursor leaves a window, CheckMotion() call PostNewCursor(), which may call ChangeToCursor(). When the client issues a FreeCursor request. Each time a window changes the cursor (ChangeWindowAttributes()). When a grab with a cursor is deactivated. And on CloseDownClient(), when all the resources are freed for the client. And quite a few more.
Animated cursors are part of the XRender extension. All they are is a list of standard cursors with a delay between them. The memory is a standard CursorRec, with an AnimCursorRec and several AnimCursElt attached to the end of the struct. The latter two contain the CursorRec that make up the animated cursor's frames. Animated cursors are identified with a special pattern in the CursorRecs bits field. See AnimCursorCreate().
So for an animated cursor with 3 frames, the memory looks something like this.
[ CursorRec ][ AnimCursorRec ][AnimCursElt][AnimCursElt][AnimCursElt] | | | [ CursorRec ] <------------------- | | [ CursorRec ] <--------------------------------- | [ CursorRec ] <----------------------------------------------
Each cursor that is used in an animated cursor has the refcnt increased, and likewise decreased via FreeCursor() when the animated cursor is deleted. If a window or pointer uses a animated cursor, they use the animated cursor struct, never directly any of the cursors that make up the frames.
X uses a BlockHandler to do stuff when nothing else needs to be done. Such as redrawing mouse cursors. The animated cursor code adds itself to the block handler and AnimCurScreenBlockHandler() is called regularly.
Display of an animated cursor uses set of static variables in animcur.c. The currently displayed animated cursor is saved, and each time the block handler is called, it checks
- whether the current cursor is an animated cursor.
- if so, if the timeout for the next frame has passed already
- if so, display the next frame
- if so, save the timeout for the next frame in the static struct
- update wakeup handler to wake up for next timeout.