Skip to content

Conversation

@AQtun81
Copy link

@AQtun81 AQtun81 commented Dec 1, 2025

Description

This PR adds a SDL_SetWindowMousePassthrough function which allows users to create windows that don't capture input allowing them to create overlay-style apps.

This implementation has only been tested on Windows 11, other platforms require validation.
(testing used SDL window's HWND with contents of WIN_SetWindowMousePassthrough directly)

TODO:
SDL_video.h is missing docummentation comment explaining the functionality
DYNAPI is possibly misconfigured/missing information (the new function is not present in build)

Existing Issue(s)

#12683

@slouken
Copy link
Collaborator

slouken commented Dec 1, 2025

For dynapi, go ahead and revert your changes in that directory and run src/dynapi/gendynapi.py instead.

Also, in general, please use the SDL coding style, e.g.

if (x) {
} else {
}

@slouken slouken added this to the 3.x milestone Dec 2, 2025
@AQtun81
Copy link
Author

AQtun81 commented Dec 3, 2025

I have made so many small errors and commits fixing them, so I went ahead and squashed the commits, it should make the changes more readable.

Regarding the API, should SDL_SetWindowMousePassthrough return a bool? Since as far as I know some platforms don't allow for mouse passthrough for bordered windows, this function could enforce that it can only be used on borderless windows ensuring consistent cross-platform behavior. On the other hand not everyone might care about cross-platform as long as their platform/s supports it.

This is the program I'm using to test the feature, it renders a red square on a transparent window and toggles mouse passthrough when mouse cursor hovers over it or leaves the area.

#include <SDL3/SDL.h>

int main()
{
    const SDL_FRect testRect = {100, 100, 200, 200};
    bool running = true;
    bool mousePassthrough = false;
    SDL_Event event;

    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("SDL_Init failed: %s", SDL_GetError());
        return 1;
    }

    SDL_Window* window = SDL_CreateWindow("Mouse Passthrough", 800, 600,
        SDL_WINDOW_TRANSPARENT | SDL_WINDOW_BORDERLESS | SDL_WINDOW_ALWAYS_ON_TOP);
    if (!window) {
        SDL_Log("SDL_CreateWindow failed: %s", SDL_GetError());
        SDL_Quit();
        return 1;
    }

    SDL_Renderer* renderer = SDL_CreateRenderer(window, NULL);
    if (!renderer) {
        SDL_Log("SDL_CreateRenderer failed: %s", SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }
    SDL_SetRenderVSync(renderer, 1);
    SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

    while (running) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_EVENT_QUIT) {
                running = false;
            }
            if (event.type == SDL_EVENT_KEY_DOWN) {
                if (event.key.key == SDLK_ESCAPE) {
                    running = false;
                }
            }
        }

        // toggle mouse passthrough
        float x, y;
        int xOffset, yOffset;
        SDL_GetGlobalMouseState(&x, &y);
        SDL_GetWindowPosition(window, &xOffset, &yOffset);
        x -= (float)xOffset;
        y -= (float)yOffset;
        bool hovered = x >= testRect.x && x < testRect.x + testRect.w &&
                       y >= testRect.y && y < testRect.y + testRect.h;
        if (!hovered != mousePassthrough) {
            mousePassthrough = !hovered;
            SDL_SetWindowMousePassthrough(window, mousePassthrough);
            SDL_Log(mousePassthrough ? "passthrough enabled\n" : "passthrough disabled\n");
        }

        // render
        SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
        SDL_RenderClear(renderer);
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 128);
        SDL_RenderFillRect(renderer, &testRect);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants