OpenPandora: Using custom cursors in fullscreen X11 SDL windows without touchscreen drifting/grabbing issues.

OpenPandora: Using custom cursors in fullscreen X11 SDL windows without touchscreen drifting/grabbing issues.

Note: This is is quick guide aimed at developers/porters looking to fix issues with custom SDL cursors (Cursors made up using an SDL_Surface) and the OpenPandora touchscreen, if your not using SDL then the chances are your not seeing the issue.

Normally I would not put together quick posts with small code snippets (I tend to direct people to code and tell them to work it out Winking smile) but as several people pointed out to me recently ScummVM for the OpenPandora works around a ‘feature’ in the SDL build on the device that causes relative screen coordinates to be returned to the event stack when you hit the screen edges with the touchscreen. In essence this is a code fix for the ‘cursor drift’ bug people report.

Nubs, mice, in fact any relative input device are fine, it is absolute input devices like the touchscreen that ‘drift’ and end up offset making some types of application unusable with the touchscreen (ScummVM was one of them).

As the same issue seems to cause problems several other applications on the OpenPandora I thought I would do a quick howto with a work around and give a little background on the issue.

Symptoms:

After calling SDL_ShowCursor(SDL_DISABLE); and instantiating the SDL_Surface that contains whatever you want to blit as your custom cursor you will find that touchscreen presses on the sides of the touchscreen will cause the pointer to drift.

This leads to future touchscreen presses being offset from the cursor position. Relative devices (i.e. Mice/Nubs etc.) are no effected and carry on updating the cursor as normal.

What is actually happening is that SDL is reverting to returning relative X and Y’s at the screen edges even when your using something that only works with absolute values and is asking for/checking the absolute values (i.e. a touchscreen Winking smile).

Work Around:

The work around for the issue is very simple (well in fact there are several work around’s, I’ll just highlight the one I choose to use in ScummVM).

To fix the issue you need to get SDL to behave the same way with the SDL Cursor hidden as it does with it shown. The simplest way to do this is not hide the SDL Cursor, ok that’s not very helpful as it will give you a bog standard SDL Cursor printed over the top of your nice fancy cursor. What if you could hide the SDL Cursor without disabling it? Winking smile

Yep, the fix really is as simple as that, just leave the SDL Cursor enabled but use SDL_CreateCursor to create an empty transparent cursor. This way all you will see is your nice custom cursor but SDL internally will behave using the logic path for SDL_ShowCursor(SDL_ENABLE);

Other options may include playing with calls to SDL_MoveCursor() or playing with the status of the SDL WM Grab features. I’ll leave the reader to work them out.

Sample Workaround Code:

static SDL_Cursor *hiddenCursor;

void someSDLinit() {
    …
    …
    uint8_t hiddenCursorData = 0;
    …
    … (Init SDL here)
    …
    hiddenCursor = SDL_CreateCursor(&hiddenCursorData, &hiddenCursorData, 8, 1, 0, 0);
    …
    /* On the OpenPandora we need to work around an SDL assumption that
       returns relative mouse coordinates when you get to the screen
       edges using the touchscreen and a custom surface for the cursor.
       The workaround is to set a blank SDL cursor and NOT disable it
       (Hackish I know).

       The root issues lies in the Windows Manager GRAB code in SDL.
       That is why the issue is not seen on framebuffer devices like the
       GP2X (there is no X window manager ;)).
    */

    SDL_ShowCursor(SDL_ENABLE);
    SDL_SetCursor(hiddenCursor);
    …}

void someSDLdeinit() {
    …

    SDL_FreeCursor(hiddenCursor);
    …
    …
}

Why does it happen?

The problem stems from the way SDL handles Window Manager grabbing at the edges of the surface when full screen.

To quote a comment from part of the SDL source that illustrates the point:

/* Determine whether the mouse should be in relative mode or not.
   This function is called when the input grab state or cursor
   visibility state changes.
   If the cursor is not visible, and the input is grabbed, the
   driver can place the mouse in relative mode, which may result
   in higher accuracy sampling of the pointer motion.
*/

There is some debate about this representing a bug in SDL or ‘expected behaviour’ when you have hidden the cursor. You would never see the issue with a relative input device only absolute devices.

This could be patched at the SDL level and in fact that maybe desirable (I am personally not in favour of patching this in SDL just for the OpenPandora, there needs to be a more generic fix). But either way, the work around above will work regardless of any SDL patching and continue to work.

I hope this quick fix is valuable to people, it may provide some quick fixes to applications that exhibit the problem Smile.

Regards,

John