Comment 15 for bug 1024482

Revision history for this message
Daniel Gnoutcheff (gnoutchd) wrote :

OK, I think I've tracked down the cause of this bug.

Thanks to MPX (multi-pointer X), it turns out that each X window (including the root window) may have *multiple* cursor icon settings. Traditionally, under the core X11 protocol, a window could only define a "standard cursor", which can be set by the XDefineCursor(3) function. However, XInput2 introduces the possibility of having multiple mouse pointers on the screen, and it allows windows to define a "device cursor" for each pointer individually. This can be done though XInput's XIDefineCursor(3) function. Crucially, if a given window has both a standard cursor and a device cursor defined, then the device cursor will override the standard cursor, at least for the particular "master pointer device" that the device cursor was defined for.

Here is the relevant extract from the XInput2 protocol specification, which is shipped as /usr/share/doc/x11proto-input-dev/XI2proto.html in the x11proto-input-dev package:
> Whenever device enters a window W, the cursor shape is selected in the
> following order:
> - if the current window has a device cursor C(d) defined for device,
> display this cursor C(d).
> - otherwise, if the current window has a cursor C(w) defined in the core
> protocol’s window attributes, display cursor C(w).
> - repeat on parent window until a cursor has been found.

On a typical system with a single master pointer device, this means that once a device pointer is set, the standard cursor setting loses its effect. So, if something sets a device pointer on the root window, then any program that that tries to change the cursor icon by setting the root window's standard cursor (which includes xsetroot, XFCE, and GNOME) is sabotaged.

And the final piece: if XInput support is available, GTK3's implementation of gdk_window_set_cursor will use XIDefineCursor. Since both unity-greeter and lightdm-gtk-greeter use gdk_window_set_cursor, that means both greeters set a device cursor on the root window. From that point on, xsetroot and friends are SOL.

The behavior of the xserver is set in the protocol specification, so we can't "fix" this problem there. I think the best solution is to get unity-greeter and lightdm-gtk-greeter to use XDefineCursor instead of XIDefineCursor. This might be done by modifying gdk_window_set_cursor() or by modifying the greeters to use XDefineCursor directly instead of deferring to gdk. swiftgeek's approach might work too, but I think we can do something simpler than that.

In the meantime, working around this bug is somewhat of a pain because (1) XIUndefineCursor doesn't seem to work correctly on the root window (probably an xserver bug, I'll follow up on that later) and (2) the xserver currently prohibits removal of the initial "Virtual core pointer" master pointer device, which is the pointer device that our greeters are setting a device cursor for. So, once the device cursor is set on the root window, I don't think there's any way to get the standard cursor setting to work again.

A workaround is to (re)set the device cursor instead. My attached xiset.c will set the device cursor on the root window to whatever the default XCursor theme is. (I originally wrote it while trying to understand the behavior of gdk_window_set_cursor().) I set my desired Xcursor theme in ~/.Xdefaults and have my DE run this program at login. It does the job for now. I've also heard of an "xicursorset" tool floating around, which might do the job as well.