debugging with SDL_CaptureMouse

Hi

I have been debugging an app the uses SDL_CaptureMouse.
If a breakpoint is hit while SDL_CaptureMouse is active, the mouse is not released and any interaction with the debugger gui or the rest of the desktop is extremely hard.

I am running on Ubuntu 20.10, GNOME, X11.
I see that this functions eventually calls X11_XGrabPointer.

I think I can explain this behaviour “easily”, but I wonder what people have done so far to debug this type of applications.

Could / should gdb tell X11 that the app is being debugged and so the mouse pointer released, and re-acquired later?

Is there another explanation / solution?

I think most people, myself included, use remote debugging in cases like this. Most often that means ssh and command line lldb/gdb, but you can run a debug server and connect to it from GUI tools as well.

I think if you think on it you will see why expecting gdb or any other debugger to do anything about this is impractical and would likely cause difficult to debug scenarios involving the location of the mouse.

I was just gonna write an answer, but turns out, I already did =)
See: How to release the mouse when debugging in gdb

The mentioned python script (which is not accessible anymore) looked like:

#!/usr/bin/python
# release mouse when breaking into debugger for SDL2 programs
# by KittyCat, some comments + gdb.write() messages by Daniel Gibson
# put this next to the executable you wanna debug and name it
# <YourExecutableName>-gdb.py
# Make sure that it's in a subdir of an allowed GDB auto-loading safe-path,
# see https://sourceware.org/gdb/onlinedocs/gdb/Auto_002dloading-safe-path.html
# e.g. by executing in a terminal:
# $ echo "add-auto-load-safe-path /path/to/project" >> ~/.gdbinit

def release_mouse (event):
        gdb.write("GDB/SDL2: Releasing mouse\n")
        gdb.execute("call SDL_SetRelativeMouseMode(0)")
        
        # TODO: if you use SDL_SetWindowGrab(), you may have to write
        #       function with no arguments to call it with your SDL_Window*
        #       handle and call this as well, like:
        #gdb.execute("call DEBUG_UngrabMouse()")
        
        # TODO: the following doesn't seem to work, maybe it does for you.
        #       either way, it needs xdotool installed.
        #gdb.execute("exec xdotool key XF86Ungrab")
        
 
gdb.events.stop.connect(release_mouse)

gdb.write("GDB/SDL2: installed release mouse for SDL2\n")

By the way, nowadays for the SDL_SetWindowGrab() case
gdb.execute("call SDL_SetWindowGrab(SDL_GetGrabbedWindow(), 0)")
might work, but this is completely untested.

I investigated that commented out xdotool key XF86Ungrab thing a bit, and it turns out that it doesn’t work because it’s disabled by default, because apparently it allows bypassing screensavers/screenlockers (and as far as I know X11 fixed this by disabling it by default instead of providing an API for screensavers to disable it while they’re locked…)

Anyway, it might work to, when htting a breakpoing, enable XF86Ungrab, send that key event with xdotool, disable it again. Would probably look like:

#!/usr/bin/python
def release_mouse (event):
    # allow XF86Ungrab
    gdb.execute("setxkbmap -option grab:break_actions")
    # send that key to ungrab the cursor
    gdb.execute("exec xdotool key XF86Ungrab") 
    # this should reset xkbmap options to default (disallow XF86Ungrab again)
    gdb.execute("setxkbmap -option")

gdb.events.stop.connect(release_mouse)
gdb.write("GDB/X11: installed release mouse for X11\n")

Note that you still need xdotool installed and allow GDB auto-loading etc.

Also note that I didn’t test this yet :stuck_out_tongue:

Some information on this: Who-T: XKB breaking grabs - CVE-2012-0064
An Easy But Serious Screensaver Security Problem In X.Org - Phoronix
https://unix.stackexchange.com/a/40472

Also, just want to chime in and say that SDL_CaptureMouse is meant for extremely short grabs that need to track the mouse outside the window…the use-case is you’re building a GUI toolkit on top of SDL and need to deal with things like dragging windows to resize them and size (indeed, this function was added so Unreal Engine 4 could do that very thing).

So most cases you shouldn’t need to be using this function to have to debug around it.

(you might have a perfectly valid reason, I’m just leaving this note for future readers.)

You hit a valid point.
The usage comes form ImGui which is a GUI toolkit.

I am not able to tell you why they have decided to use it, nor which problems they were trying to solve.