Add click-through functionality for windows
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
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 {
}
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;
}
Yes, it should return a bool.