summaryrefslogtreecommitdiff
path: root/Development/Documentation/CursorHandling.mdwn
blob: 54b8779d2f9d3b348e20ca42a71ac5c8ff44ecaf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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

1. a client gets a reference to a cursor
1. a window uses the cursor
1. a pointer uses the cursor
1. 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
[[!format txt """
  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

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 _CursorRec_s _bits_ field. See _AnimCursorCreate()_.

So for an animated cursor with 3 frames, the memory looks something like this.
[[!format txt """
[     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.

---
[[CategoryServerInternals]]