libsdl-org / SDL

Simple Directmedia Layer
https://libsdl.org
zlib License
9.93k stars 1.84k forks source link

SDL_SetWindowGrab doesn't work with mouse not hidden (SDL2, X11 video driver only) #9162

Closed ell1e closed 8 months ago

ell1e commented 8 months ago

SDL_SetWindowGrab doesn't work with the mouse cursor still shown. The mouse is only confined to the window if SDL_ShowCursor is also set to SDL_FALSE, but the documentation of SDL_SetWindowGrab seems to claim that's not an intended precondition:

When input is grabbed, the mouse is confined to the window.

(Note no mention that the confinement depends on the mouse cursor being hidden.)

This is at least broken when using SDL 2.30.0-1.1 as packaged by OpenSUSE with KDE Plasma Shell 5.27.10 with a Wayland session, SDL defaulting to SDL_VIDEODRIVER=x11 with this setup. Not sure about whether this actually works on other platforms.

Edit: Here's code to reproduce it:


#include <SDL2/SDL.h>
#include <stdio.h>

int main(int argc, const char **argv) {
    SDL_Window *window = NULL;
    if (SDL_Init(SDL_INIT_VIDEO) < 0) return 1;
    window = SDL_CreateWindow("Test",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
        200, 200, SDL_WINDOW_SHOWN);
    if (window == NULL) return 1;
    SDL_SetWindowGrab(window, SDL_TRUE);

    int hadsurfaceupdate = 0;
    printf("Window should now be visible.\n");
    while (1) {
        SDL_Event e;
        while (SDL_PollEvent(&e) != 0) {
            if (e.type == SDL_MOUSEMOTION) {
                printf("Mouse at: %d, %d\n",
                    (int)e.motion.x, (int)e.motion.y);
            } else if (e.type == SDL_WINDOWEVENT) {
                if (e.window.event == SDL_WINDOWEVENT_ENTER) {
                    printf("Mouse enters window.\n");
                } else if (e.window.event == SDL_WINDOWEVENT_LEAVE) {
                    printf("Mouse leaves window.\n");
                }
            }
        }

        SDL_Surface *srf = SDL_GetWindowSurface(window);
        SDL_UpdateWindowSurface(window);
        if (!hadsurfaceupdate) {
            printf("First surface update done.\n");
            hadsurfaceupdate = 1;
        }
    }
    return 0;
}

What happens, with the expected behavior that the mouse cursor is trapped to the window at least as long as the window has focus (I click it during the runtime of the video but it doesn't make any difference):

https://github.com/libsdl-org/SDL/assets/64124388/a343084c-bad1-4367-93ad-3e0135053b40

ell1e commented 8 months ago

Thanks to this helpful comment I was able to test confinement with SDL_VIDEODRIVER=wayland where it works. So this is an SDL_VIDEODRIVER=x11-with-Plasma-Wayland/XWayland bug, it seems like.

Sadly, many distributions still seem to ship SDL2 with SDL_VIDEODRIVER=x11 as a default and probably also without libdecor. Therefore, this bug still seems relevant today.

slouken commented 8 months ago

@Kontrabant, is this possible to fix when XWayland is in use?

Kontrabant commented 8 months ago

Will look into this, although this might be a KDE bug, as it works fine on GNOME with XWayland.

Kontrabant commented 8 months ago

Something is definitely broken in KDE. When grabbing on my test system (Fedora 39 with all updates), the cursor is blocked on the sides and bottom, but can escape from the top in both this sample and when grabbing via ctrl+G in testsprite. When the cursor escapes from the top, no leave event occurs and mouse events continue to arrive when moving the mouse, even though the cursor is not over the window anymore.

Note that on both GNOME and KDE, if a grab is initiated while the cursor is outside of the window, it won't be confined until the mouse is clicked while the cursor over the window. I'm guessing this is some internal XWayland behavior since it happens regardless of desktop.

This needs to go upstream, as there's nothing that SDL can do to fix this.

ell1e commented 8 months ago

Thanks so much for looking into this! I filed a KDE bug here: https://bugs.kde.org/show_bug.cgi?id=482448

ell1e commented 8 months ago

Since it seems like this is being fixed in KWin/KDE now, I'll close this here! Thanks again for your help.