-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Description
I'm writing a python app using Kivy, which uses SDL as its backend.
I decided it would be useful to show a native context menu on macos instead of rendering my own, and was pretty delighted by how easy this was to achieve, even from python using pyobjc.
There's however a problem with my approach, it seems like macos doesn't send a mouseup event when opening the menu on mousedown (which seems to be common practice on macos). This confuses SDL and causes it to ignore the next mouse press. I have checked the native event queue and it does send a correct "mousedown" event after the menu closes, but it seems like SDL is ignoring the button state that it sends and instead uses its own internal state to track this.
I realize that using such contrived methods with python is probably out of the scope for this project.
Therefore I have crafted a minimum example using ObjC to illustrate the issue:
#include <unistd.h>
#include <SDL3/SDL.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_events.h>
#include <AppKit/NSMenu.h>
@interface MenuHandler : NSObject
@end
@implementation MenuHandler
- (void)itemClicked:(id)sender {
SDL_Log("Clicked!");
}
@end
void open_context_menu(void) {
NSMenu *menu = [[NSMenu alloc] initWithTitle:@"My Menu"];
MenuHandler *handler = [[MenuHandler alloc] init];
NSMenuItem *item1 = [[NSMenuItem alloc] initWithTitle:@"Item 1" action:@selector(itemClicked:) keyEquivalent:@""];
[item1 setTarget:handler];
[menu addItem:item1];
[menu popUpMenuPositioningItem:nil atLocation:NSEvent.mouseLocation inView:nil];
}
int main(int argc, char *argv[]) {
sleep(1);
SDL_SetLogPriorities(SDL_LOG_PRIORITY_VERBOSE);
if (!SDL_Init(SDL_INIT_VIDEO) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
return 1;
}
SDL_Window *window = SDL_CreateWindow("SDL3 Window", 800, 600, SDL_WINDOW_OPENGL);
if (window == NULL) {
SDL_Log("Unable to create Window: %s", SDL_GetError());
return 1;
}
bool quit = false;
while (!quit) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_EVENT_QUIT) {
quit = true;
}
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (e.button.button == SDL_BUTTON_RIGHT) {
open_context_menu();
float x; float y;
int buttons = SDL_GetMouseState(&x, &y);
SDL_Log("left: %d, right: %d, middle: %d", (buttons & SDL_BUTTON_LMASK) != 0, (buttons & SDL_BUTTON_RMASK) != 0, (buttons & SDL_BUTTON_MMASK) != 0);
}
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
The menu opens on the first right click while holding the right mouse button down and then dismisses on releasing the button.
This incorrectly reports the right mouse button as being pressed after the menu is closed.
The next right click doesn't trigger the menu, and then the mouse state fixes itself. The next press works again.
I actually went over triggering a mouse up event from my code after the menu closes and that has fixed the situation. To do this I need accessibility privileges on my app tho, which I would like to avoid.
I think the fix would be to listen to the button state that the event reports instead of tracking this manually.
I could try and come up with a PR, if SDL wants to support this use case.
If there's a way to work around this problem somehow I'd love to know a solution!