From 0dd7024d55019182b9e92d65221824a7a4dac992 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 12 Mar 2021 21:58:20 +0100 Subject: [PATCH] Modifies WaitEvent and WaitEventTimeout to actually wait instead of polling When possible use native os functions to make a blocking call waiting for an incoming event. Previous behavior was to continuously poll the event queue with a small delay between each poll. The blocking call uses a new optional video driver event, WaitEventTimeout, if available. It is called only if an window already shown is available. If present the window is designated using the variable wakeup_window to receive a wakeup event if needed. The WaitEventTimeout function accept a timeout parameter. If positive the call will wait for an event or return if the timeout expired without any event. If the timeout is zero it will implement a polling behavior. If the timeout is negative the function will block indefinetely waiting for an event. To let the main thread sees events sent form a different thread a "wake-up" signal is sent to the main thread if the main thread is in a blocking state. The wake-up event is sent to the designated wakeup_window if present. The wake-up event is sent only if the PushEvent call is coming from a different thread. Before sending the wake-up event the ID of the thread making the blocking call is saved using the variable blocking_thread_id and it is compared to the current thread's id to decide if the wake-up event should be sent. Two new optional video device methods are introduced: WaitEventTimeout SendWakeupEvent in addition the mutex wakeup_lock which is defined and initialized but only for the drivers supporting the methods above. If the methods are not present the system behaves as previously performing a periodic polling of the events queue. The blocking call is disabled if a joystick or sensor is detected and falls back to previous behavior. --- src/events/SDL_events.c | 106 +++++++++ src/video/SDL_sysvideo.h | 5 + src/video/cocoa/SDL_cocoaevents.h | 2 + src/video/cocoa/SDL_cocoaevents.m | 54 ++++- src/video/cocoa/SDL_cocoavideo.m | 6 + src/video/windows/SDL_windowsevents.c | 39 ++++ src/video/windows/SDL_windowsevents.h | 2 + src/video/windows/SDL_windowsvideo.c | 11 +- src/video/windows/SDL_windowsvideo.h | 1 + src/video/x11/SDL_x11events.c | 317 ++++++++++++++++---------- src/video/x11/SDL_x11events.h | 2 + src/video/x11/SDL_x11video.c | 21 ++ src/video/x11/SDL_x11video.h | 2 + test/CMakeLists.txt | 1 + test/Makefile.in | 4 + test/checkkeysthreads.c | 275 ++++++++++++++++++++++ 16 files changed, 716 insertions(+), 132 deletions(-) create mode 100644 test/checkkeysthreads.c diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index ec8c0b185..7f34cbc3b 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -759,6 +759,80 @@ SDL_PollEvent(SDL_Event * event) return SDL_WaitEventTimeout(event, 0); } +static int +SDL_WaitEventTimeout_Device(_THIS, SDL_Window *wakeup_window, SDL_Event * event, int timeout) +{ + /* Release any keys held down from last frame */ + SDL_ReleaseAutoReleaseKeys(); + + for (;;) { + if (!_this->wakeup_lock || SDL_LockMutex(_this->wakeup_lock) == 0) { + int status = SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT); + /* If status == 0 we are going to block so wakeup will be needed. */ + if (status == 0) { + _this->wakeup_window = wakeup_window; + _this->blocking_thread_id = SDL_ThreadID(); + } else { + _this->wakeup_window = NULL; + _this->blocking_thread_id = 0; + } + if (_this->wakeup_lock) { + SDL_UnlockMutex(_this->wakeup_lock); + } + if (status < 0) { + /* Got an error: return */ + break; + } + if (status > 0) { + /* There is an event, we can return. */ + SDL_SendPendingSignalEvents(); /* in case we had a signal handler fire, etc. */ + return 1; + } + /* No events found in the queue, call WaitEventTimeout to wait for an event. */ + status = _this->WaitEventTimeout(_this, timeout); + /* Set wakeup_window to NULL without holding the lock. */ + _this->wakeup_window = NULL; + if (status <= 0) { + /* There is either an error or the timeout is elapsed: return */ + return 0; + } + /* An event was found and pumped into the SDL events queue. Continue the loop + to let SDL_PeepEvents pick it up .*/ + } + } + return 0; +} + +static int +SDL_events_need_polling() { + SDL_bool need_polling = SDL_FALSE; + +#if !SDL_JOYSTICK_DISABLED + need_polling = \ + (!SDL_disabled_events[SDL_JOYAXISMOTION >> 8] || SDL_JoystickEventState(SDL_QUERY)) \ + && (SDL_NumJoysticks() > 0); +#endif + +#if !SDL_SENSOR_DISABLED + need_polling = need_polling || (!SDL_disabled_events[SDL_SENSORUPDATE >> 8] && \ + (SDL_NumSensors() > 0)); +#endif + + return need_polling; +} + +static SDL_Window * +SDL_find_active_window(SDL_VideoDevice * _this) +{ + SDL_Window *window; + for (window = _this->windows; window; window = window->next) { + if (!window->is_destroying) { + return window; + } + } + return NULL; +} + int SDL_WaitEvent(SDL_Event * event) { @@ -768,11 +842,24 @@ SDL_WaitEvent(SDL_Event * event) int SDL_WaitEventTimeout(SDL_Event * event, int timeout) { + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + SDL_bool need_polling = SDL_events_need_polling(); + SDL_Window *wakeup_window = NULL; Uint32 expiration = 0; if (timeout > 0) expiration = SDL_GetTicks() + timeout; + if (!need_polling && _this) { + /* Look if a shown window is available to send the wakeup event. */ + wakeup_window = SDL_find_active_window(_this); + need_polling = (wakeup_window == NULL); + } + + if (!need_polling && _this && _this->WaitEventTimeout && _this->SendWakeupEvent) { + return SDL_WaitEventTimeout_Device(_this, wakeup_window, event, timeout); + } + for (;;) { SDL_PumpEvents(); switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) { @@ -796,6 +883,24 @@ SDL_WaitEventTimeout(SDL_Event * event, int timeout) } } +static int +SDL_SendWakeupEvent() +{ + SDL_VideoDevice *_this = SDL_GetVideoDevice(); + if (!_this || !_this->SendWakeupEvent) { + return 0; + } + if (!_this->wakeup_lock || SDL_LockMutex(_this->wakeup_lock) == 0) { + if (_this->wakeup_window && _this->blocking_thread_id != 0 && _this->blocking_thread_id != SDL_ThreadID()) { + _this->SendWakeupEvent(_this, _this->wakeup_window); + } + if (_this->wakeup_lock) { + SDL_UnlockMutex(_this->wakeup_lock); + } + } + return 0; +} + int SDL_PushEvent(SDL_Event * event) { @@ -845,6 +950,7 @@ SDL_PushEvent(SDL_Event * event) return -1; } + SDL_SendWakeupEvent(); SDL_GestureProcessEvent(event); return 1; diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 682e52592..8395e8680 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -290,6 +290,8 @@ struct SDL_VideoDevice /* * Event manager functions */ + int (*WaitEventTimeout) (_THIS, int timeout); + void (*SendWakeupEvent) (_THIS, SDL_Window *window); void (*PumpEvents) (_THIS); /* Suspend the screensaver */ @@ -324,6 +326,9 @@ struct SDL_VideoDevice /* Data common to all drivers */ SDL_bool is_dummy; SDL_bool suspend_screensaver; + SDL_Window *wakeup_window; + SDL_threadID blocking_thread_id; + SDL_mutex *wakeup_lock; /* Initialized only if WaitEventTimeout/SendWakeupEvent are supported */ int num_displays; SDL_VideoDisplay *displays; SDL_Window *windows; diff --git a/src/video/cocoa/SDL_cocoaevents.h b/src/video/cocoa/SDL_cocoaevents.h index ece463c19..f4694269c 100644 --- a/src/video/cocoa/SDL_cocoaevents.h +++ b/src/video/cocoa/SDL_cocoaevents.h @@ -25,6 +25,8 @@ extern void Cocoa_RegisterApp(void); extern void Cocoa_PumpEvents(_THIS); +extern int Cocoa_WaitEventTimeout(_THIS, int timeout); +extern void Cocoa_SendWakeupEvent(_THIS, SDL_Window *window); extern void Cocoa_SuspendScreenSaver(_THIS); #endif /* SDL_cocoaevents_h_ */ diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m index f496cb24f..5a0ab1fb4 100644 --- a/src/video/cocoa/SDL_cocoaevents.m +++ b/src/video/cocoa/SDL_cocoaevents.m @@ -512,9 +512,8 @@ Cocoa_RegisterApp(void) } }} -void -Cocoa_PumpEvents(_THIS) -{ @autoreleasepool +int +Cocoa_PumpEventsUntilDate(_THIS, NSDate *expiration, bool accumulate) { #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 /* Update activity every 30 seconds to prevent screensaver */ @@ -530,9 +529,9 @@ Cocoa_PumpEvents(_THIS) #endif for ( ; ; ) { - NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES ]; + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:expiration inMode:NSDefaultRunLoopMode dequeue:YES ]; if ( event == nil ) { - break; + return 0; } if (!s_bShouldHandleEventsInSDLApplication) { @@ -541,7 +540,52 @@ Cocoa_PumpEvents(_THIS) // Pass events down to SDLApplication to be handled in sendEvent: [NSApp sendEvent:event]; + if ( !accumulate) { + break; + } } + return 1; +} + +int +Cocoa_WaitEventTimeout(_THIS, int timeout) +{ @autoreleasepool +{ + if (timeout > 0) { + NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow: (double) timeout / 1000.0]; + return Cocoa_PumpEventsUntilDate(_this, limitDate, false); + } else if (timeout == 0) { + return Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], false); + } else { + while (Cocoa_PumpEventsUntilDate(_this, [NSDate distantFuture], false) == 0) { + } + } + return 1; +}} + +void +Cocoa_PumpEvents(_THIS) +{ @autoreleasepool +{ + Cocoa_PumpEventsUntilDate(_this, [NSDate distantPast], true); +}} + +void Cocoa_SendWakeupEvent(_THIS, SDL_Window *window) +{ @autoreleasepool +{ + NSWindow *nswindow = ((SDL_WindowData *) window->driverdata)->nswindow; + + NSEvent* event = [NSEvent otherEventWithType: NSEventTypeApplicationDefined + location: NSMakePoint(0,0) + modifierFlags: 0 + timestamp: 0.0 + windowNumber: nswindow.windowNumber + context: nil + subtype: 0 + data1: 0 + data2: 0]; + + [NSApp postEvent: event atStart: YES]; }} void diff --git a/src/video/cocoa/SDL_cocoavideo.m b/src/video/cocoa/SDL_cocoavideo.m index 5019d3e7c..f16b8aa66 100644 --- a/src/video/cocoa/SDL_cocoavideo.m +++ b/src/video/cocoa/SDL_cocoavideo.m @@ -38,6 +38,9 @@ static void Cocoa_VideoQuit(_THIS); static void Cocoa_DeleteDevice(SDL_VideoDevice * device) { + if (device->wakeup_lock) { + SDL_DestroyMutex(device->wakeup_lock); + } SDL_free(device->driverdata); SDL_free(device); } @@ -63,6 +66,7 @@ Cocoa_CreateDevice(int devindex) return NULL; } device->driverdata = data; + device->wakeup_lock = SDL_CreateMutex(); /* Set the function pointers */ device->VideoInit = Cocoa_VideoInit; @@ -73,6 +77,8 @@ Cocoa_CreateDevice(int devindex) device->GetDisplayModes = Cocoa_GetDisplayModes; device->SetDisplayMode = Cocoa_SetDisplayMode; device->PumpEvents = Cocoa_PumpEvents; + device->WaitEventTimeout = Cocoa_WaitEventTimeout; + device->SendWakeupEvent = Cocoa_SendWakeupEvent; device->SuspendScreenSaver = Cocoa_SuspendScreenSaver; device->CreateSDLWindow = Cocoa_CreateWindow; diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index bb86108a3..a8108a8ea 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1276,6 +1276,45 @@ void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata) g_WindowsMessageHookData = userdata; } +int +WIN_WaitEventTimeout(_THIS, int timeout) +{ + MSG msg; + if (g_WindowsEnableMessageLoop) { + BOOL message_result; + UINT_PTR timer_id = 0; + if (timeout > 0) { + timer_id = SetTimer(NULL, 0, timeout, NULL); + message_result = GetMessage(&msg, 0, 0, 0); + KillTimer(NULL, timer_id); + } else if (timeout == 0) { + message_result = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); + } else { + message_result = GetMessage(&msg, 0, 0, 0); + } + if (message_result) { + if (msg.message == WM_TIMER && msg.hwnd == NULL && msg.wParam == timer_id) { + return 0; + } + if (g_WindowsMessageHook) { + g_WindowsMessageHook(g_WindowsMessageHookData, msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */ + TranslateMessage(&msg); + DispatchMessage(&msg); + return 1; + } + } + return 0; +} + +void +WIN_SendWakeupEvent(_THIS, SDL_Window *window) +{ + SDL_WindowData *data = (SDL_WindowData *) window->driverdata; + PostMessage(data->hwnd, data->videodata->_SDL_WAKEUP, 0, 0); +} + void WIN_PumpEvents(_THIS) { diff --git a/src/video/windows/SDL_windowsevents.h b/src/video/windows/SDL_windowsevents.h index fcd716dcf..93b41e4dc 100644 --- a/src/video/windows/SDL_windowsevents.h +++ b/src/video/windows/SDL_windowsevents.h @@ -31,6 +31,8 @@ extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lP extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); extern void WIN_PumpEvents(_THIS); +extern void WIN_SendWakeupEvent(_THIS, SDL_Window *window); +extern int WIN_WaitEventTimeout(_THIS, int timeout); #endif /* SDL_windowsevents_h_ */ diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 8eb32b2ef..4956ced70 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -87,7 +87,9 @@ WIN_DeleteDevice(SDL_VideoDevice * device) if (data->shcoreDLL) { SDL_UnloadObject(data->shcoreDLL); } - + if (device->wakeup_lock) { + SDL_DestroyMutex(device->wakeup_lock); + } SDL_free(device->driverdata); SDL_free(device); } @@ -113,6 +115,7 @@ WIN_CreateDevice(int devindex) return NULL; } device->driverdata = data; + device->wakeup_lock = SDL_CreateMutex(); data->userDLL = SDL_LoadObject("USER32.DLL"); if (data->userDLL) { @@ -139,6 +142,8 @@ WIN_CreateDevice(int devindex) device->GetDisplayModes = WIN_GetDisplayModes; device->SetDisplayMode = WIN_SetDisplayMode; device->PumpEvents = WIN_PumpEvents; + device->WaitEventTimeout = WIN_WaitEventTimeout; + device->SendWakeupEvent = WIN_SendWakeupEvent; device->SuspendScreenSaver = WIN_SuspendScreenSaver; device->CreateSDLWindow = WIN_CreateWindow; @@ -226,6 +231,8 @@ VideoBootStrap WINDOWS_bootstrap = { int WIN_VideoInit(_THIS) { + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + if (WIN_InitModes(_this) < 0) { return -1; } @@ -236,6 +243,8 @@ WIN_VideoInit(_THIS) SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL); SDL_AddHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL); + data->_SDL_WAKEUP = RegisterWindowMessageA("_SDL_WAKEUP"); + return 0; } diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index c7c4caaf2..5f4bd4a6c 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -190,6 +190,7 @@ typedef struct SDL_VideoData TSFSink *ime_ippasink; BYTE pre_hook_key_state[256]; + UINT _SDL_WAKEUP; } SDL_VideoData; extern SDL_bool g_WindowsEnableMessageLoop; diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 37c76c5e9..8c654f23d 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -670,42 +670,35 @@ isReparentNotify(Display *display, XEvent *ev, XPointer arg) } static void -X11_DispatchEvent(_THIS) +X11_DispatchEvent(_THIS, XEvent *xevent) { SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + XkbEvent* xkbEvent = (XkbEvent*) xevent; Display *display; SDL_WindowData *data; - XEvent xevent; - XkbEvent* xkbEvent; int orig_event_type; KeyCode orig_keycode; XClientMessageEvent m; int i; - if (!videodata) { - return; - } + SDL_assert(videodata != NULL); display = videodata->display; - SDL_zero(xevent); /* valgrind fix. --ryan. */ - X11_XNextEvent(display, &xevent); - xkbEvent = (XkbEvent*) &xevent; - /* Save the original keycode for dead keys, which are filtered out by the XFilterEvent() call below. */ - orig_event_type = xevent.type; + orig_event_type = xevent->type; if (orig_event_type == KeyPress || orig_event_type == KeyRelease) { - orig_keycode = xevent.xkey.keycode; + orig_keycode = xevent->xkey.keycode; } else { orig_keycode = 0; } /* filter events catchs XIM events and sends them to the correct handler */ - if (X11_XFilterEvent(&xevent, None) == True) { + if (X11_XFilterEvent(xevent, None) == True) { #if 0 printf("Filtered event type = %d display = %d window = %d\n", - xevent.type, xevent.xany.display, xevent.xany.window); + xevent->type, xevent->xany.display, xevent->xany.window); #endif /* Make sure dead key press/release events are sent */ /* But only if we're using one of the DBus IMEs, otherwise @@ -714,7 +707,7 @@ X11_DispatchEvent(_THIS) #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX) SDL_Scancode scancode = videodata->key_layout[orig_keycode]; videodata->filter_code = orig_keycode; - videodata->filter_time = xevent.xkey.time; + videodata->filter_time = xevent->xkey.time; if (orig_event_type == KeyPress) { SDL_SendKeyboardKey(SDL_PRESSED, scancode); @@ -727,8 +720,8 @@ X11_DispatchEvent(_THIS) } #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS - if(xevent.type == GenericEvent) { - X11_HandleGenericEvent(videodata, &xevent); + if(xevent->type == GenericEvent) { + X11_HandleGenericEvent(videodata, xevent); return; } #endif @@ -739,18 +732,18 @@ X11_DispatchEvent(_THIS) SDL_VERSION(&wmmsg.version); wmmsg.subsystem = SDL_SYSWM_X11; - wmmsg.msg.x11.event = xevent; + wmmsg.msg.x11.event = *xevent; SDL_SendSysWMEvent(&wmmsg); } #if 0 printf("type = %d display = %d window = %d\n", - xevent.type, xevent.xany.display, xevent.xany.window); + xevent->type, xevent->xany.display, xevent->xany.window); #endif if ((videodata->clipboard_window != None) && - (videodata->clipboard_window == xevent.xany.window)) { - X11_HandleClipboardEvent(_this, &xevent); + (videodata->clipboard_window == xevent->xany.window)) { + X11_HandleClipboardEvent(_this, xevent); return; } @@ -758,7 +751,7 @@ X11_DispatchEvent(_THIS) if (videodata && videodata->windowlist) { for (i = 0; i < videodata->numwindows; ++i) { if ((videodata->windowlist[i] != NULL) && - (videodata->windowlist[i]->xwindow == xevent.xany.window)) { + (videodata->windowlist[i]->xwindow == xevent->xany.window)) { data = videodata->windowlist[i]; break; } @@ -766,19 +759,19 @@ X11_DispatchEvent(_THIS) } if (!data) { /* The window for KeymapNotify, etc events is 0 */ - if (xevent.type == KeymapNotify) { + if (xevent->type == KeymapNotify) { if (SDL_GetKeyboardFocus() != NULL) { X11_ReconcileKeyboardState(_this); } - } else if (xevent.type == MappingNotify || xkbEvent->any.xkb_type == XkbStateNotify) { + } else if (xevent->type == MappingNotify || xkbEvent->any.xkb_type == XkbStateNotify) { /* Has the keyboard layout changed? */ - const int request = xevent.xmapping.request; + const int request = xevent->xmapping.request; #ifdef DEBUG_XEVENTS printf("window %p: MappingNotify!\n", data); #endif if ((request == MappingKeyboard) || (request == MappingModifier)) { - X11_XRefreshKeyboardMapping(&xevent.xmapping); + X11_XRefreshKeyboardMapping(&xevent->xmapping); } X11_UpdateKeymap(_this); @@ -787,28 +780,28 @@ X11_DispatchEvent(_THIS) return; } - switch (xevent.type) { + switch (xevent->type) { /* Gaining mouse coverage? */ case EnterNotify:{ SDL_Mouse *mouse = SDL_GetMouse(); #ifdef DEBUG_XEVENTS printf("window %p: EnterNotify! (%d,%d,%d)\n", data, - xevent.xcrossing.x, - xevent.xcrossing.y, - xevent.xcrossing.mode); - if (xevent.xcrossing.mode == NotifyGrab) + xevent->xcrossing.x, + xevent->xcrossing.y, + xevent->xcrossing.mode); + if (xevent->xcrossing.mode == NotifyGrab) printf("Mode: NotifyGrab\n"); - if (xevent.xcrossing.mode == NotifyUngrab) + if (xevent->xcrossing.mode == NotifyUngrab) printf("Mode: NotifyUngrab\n"); #endif SDL_SetMouseFocus(data->window); - mouse->last_x = xevent.xcrossing.x; - mouse->last_y = xevent.xcrossing.y; + mouse->last_x = xevent->xcrossing.x; + mouse->last_y = xevent->xcrossing.y; if (!mouse->relative_mode) { - SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y); + SDL_SendMouseMotion(data->window, 0, 0, xevent->xcrossing.x, xevent->xcrossing.y); } /* We ungrab in LeaveNotify, so we may need to grab again here */ @@ -819,21 +812,21 @@ X11_DispatchEvent(_THIS) case LeaveNotify:{ #ifdef DEBUG_XEVENTS printf("window %p: LeaveNotify! (%d,%d,%d)\n", data, - xevent.xcrossing.x, - xevent.xcrossing.y, - xevent.xcrossing.mode); - if (xevent.xcrossing.mode == NotifyGrab) + xevent->xcrossing.x, + xevent->xcrossing.y, + xevent->xcrossing.mode); + if (xevent->xcrossing.mode == NotifyGrab) printf("Mode: NotifyGrab\n"); - if (xevent.xcrossing.mode == NotifyUngrab) + if (xevent->xcrossing.mode == NotifyUngrab) printf("Mode: NotifyUngrab\n"); #endif if (!SDL_GetMouse()->relative_mode) { - SDL_SendMouseMotion(data->window, 0, 0, xevent.xcrossing.x, xevent.xcrossing.y); + SDL_SendMouseMotion(data->window, 0, 0, xevent->xcrossing.x, xevent->xcrossing.y); } - if (xevent.xcrossing.mode != NotifyGrab && - xevent.xcrossing.mode != NotifyUngrab && - xevent.xcrossing.detail != NotifyInferior) { + if (xevent->xcrossing.mode != NotifyGrab && + xevent->xcrossing.mode != NotifyUngrab && + xevent->xcrossing.detail != NotifyInferior) { /* In order for interaction with the window decorations and menu to work properly on Mutter, we need to ungrab the keyboard when the the mouse leaves. */ @@ -848,7 +841,7 @@ X11_DispatchEvent(_THIS) /* Gaining input focus? */ case FocusIn:{ - if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) { + if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { /* Someone is handling a global hotkey, ignore it */ #ifdef DEBUG_XEVENTS printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data); @@ -856,7 +849,7 @@ X11_DispatchEvent(_THIS) break; } - if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) { + if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { #ifdef DEBUG_XEVENTS printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data); #endif @@ -882,14 +875,14 @@ X11_DispatchEvent(_THIS) /* Losing input focus? */ case FocusOut:{ - if (xevent.xfocus.mode == NotifyGrab || xevent.xfocus.mode == NotifyUngrab) { + if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { /* Someone is handling a global hotkey, ignore it */ #ifdef DEBUG_XEVENTS printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data); #endif break; } - if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) { + if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { /* We still have focus if a child gets focus. We also don't care about the position of the pointer when the keyboard focus changed. */ @@ -917,20 +910,20 @@ X11_DispatchEvent(_THIS) /* Key press? */ case KeyPress:{ - KeyCode keycode = xevent.xkey.keycode; + KeyCode keycode = xevent->xkey.keycode; KeySym keysym = NoSymbol; char text[SDL_TEXTINPUTEVENT_TEXT_SIZE]; Status status = 0; SDL_bool handled_by_ime = SDL_FALSE; #ifdef DEBUG_XEVENTS - printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode); + printf("window %p: KeyPress (X11 keycode = 0x%X)\n", data, xevent->xkey.keycode); #endif #if 1 if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) { int min_keycode, max_keycode; X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); - keysym = X11_KeyCodeToSym(_this, keycode, xevent.xkey.state >> 13); + keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13); fprintf(stderr, "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n", keycode, keycode - min_keycode, keysym, @@ -941,13 +934,13 @@ X11_DispatchEvent(_THIS) SDL_zeroa(text); #ifdef X_HAVE_UTF8_STRING if (data->ic) { - X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text), + X11_Xutf8LookupString(data->ic, &xevent->xkey, text, sizeof(text), &keysym, &status); } else { - X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL); + X11_XLookupString(&xevent->xkey, text, sizeof(text), &keysym, NULL); } #else - X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL); + X11_XLookupString(&xevent->xkey, text, sizeof(text), &keysym, NULL); #endif #ifdef SDL_USE_IME @@ -957,7 +950,7 @@ X11_DispatchEvent(_THIS) #endif if (!handled_by_ime) { /* Don't send the key if it looks like a duplicate of a filtered key sent by an IME */ - if (xevent.xkey.keycode != videodata->filter_code || xevent.xkey.time != videodata->filter_time) { + if (xevent->xkey.keycode != videodata->filter_code || xevent->xkey.time != videodata->filter_time) { SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]); } if(*text) { @@ -965,18 +958,18 @@ X11_DispatchEvent(_THIS) } } - X11_UpdateUserTime(data, xevent.xkey.time); + X11_UpdateUserTime(data, xevent->xkey.time); } break; /* Key release? */ case KeyRelease:{ - KeyCode keycode = xevent.xkey.keycode; + KeyCode keycode = xevent->xkey.keycode; #ifdef DEBUG_XEVENTS - printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent.xkey.keycode); + printf("window %p: KeyRelease (X11 keycode = 0x%X)\n", data, xevent->xkey.keycode); #endif - if (X11_KeyRepeat(display, &xevent)) { + if (X11_KeyRepeat(display, xevent)) { /* We're about to get a repeated key down, ignore the key up */ break; } @@ -992,8 +985,8 @@ X11_DispatchEvent(_THIS) printf("window %p: UnmapNotify!\n", data); #endif - if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent.xunmap)) { - X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent.xunmap); + if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) { + X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap); } else { X11_DispatchUnmapNotify(data); } @@ -1013,27 +1006,27 @@ X11_DispatchEvent(_THIS) case ConfigureNotify:{ #ifdef DEBUG_XEVENTS printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data, - xevent.xconfigure.x, xevent.xconfigure.y, - xevent.xconfigure.width, xevent.xconfigure.height); + xevent->xconfigure.x, xevent->xconfigure.y, + xevent->xconfigure.width, xevent->xconfigure.height); #endif /* Real configure notify events are relative to the parent, synthetic events are absolute. */ - if (!xevent.xconfigure.send_event) { + if (!xevent->xconfigure.send_event) { unsigned int NumChildren; Window ChildReturn, Root, Parent; Window * Children; /* Translate these coodinates back to relative to root */ - X11_XQueryTree(data->videodata->display, xevent.xconfigure.window, &Root, &Parent, &Children, &NumChildren); - X11_XTranslateCoordinates(xevent.xconfigure.display, - Parent, DefaultRootWindow(xevent.xconfigure.display), - xevent.xconfigure.x, xevent.xconfigure.y, - &xevent.xconfigure.x, &xevent.xconfigure.y, + X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren); + X11_XTranslateCoordinates(xevent->xconfigure.display, + Parent, DefaultRootWindow(xevent->xconfigure.display), + xevent->xconfigure.x, xevent->xconfigure.y, + &xevent->xconfigure.x, &xevent->xconfigure.y, &ChildReturn); } - if (xevent.xconfigure.x != data->last_xconfigure.x || - xevent.xconfigure.y != data->last_xconfigure.y) { + if (xevent->xconfigure.x != data->last_xconfigure.x || + xevent->xconfigure.y != data->last_xconfigure.y) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, - xevent.xconfigure.x, xevent.xconfigure.y); + xevent->xconfigure.x, xevent->xconfigure.y); #ifdef SDL_USE_IME if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ /* Update IME candidate list position */ @@ -1041,13 +1034,13 @@ X11_DispatchEvent(_THIS) } #endif } - if (xevent.xconfigure.width != data->last_xconfigure.width || - xevent.xconfigure.height != data->last_xconfigure.height) { + if (xevent->xconfigure.width != data->last_xconfigure.width || + xevent->xconfigure.height != data->last_xconfigure.height) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, - xevent.xconfigure.width, - xevent.xconfigure.height); + xevent->xconfigure.width, + xevent->xconfigure.height); } - data->last_xconfigure = xevent.xconfigure; + data->last_xconfigure = xevent->xconfigure; } break; @@ -1056,11 +1049,11 @@ X11_DispatchEvent(_THIS) static int xdnd_version=0; - if (xevent.xclient.message_type == videodata->XdndEnter) { + if (xevent->xclient.message_type == videodata->XdndEnter) { - SDL_bool use_list = xevent.xclient.data.l[1] & 1; - data->xdnd_source = xevent.xclient.data.l[0]; - xdnd_version = (xevent.xclient.data.l[1] >> 24); + SDL_bool use_list = xevent->xclient.data.l[1] & 1; + data->xdnd_source = xevent->xclient.data.l[0]; + xdnd_version = (xevent->xclient.data.l[1] >> 24); #ifdef DEBUG_XEVENTS printf("XID of source window : %ld\n", data->xdnd_source); printf("Protocol version to use : %d\n", xdnd_version); @@ -1076,15 +1069,15 @@ X11_DispatchEvent(_THIS) X11_XFree(p.data); } else { /* pick from list of three */ - data->xdnd_req = X11_PickTargetFromAtoms(display, xevent.xclient.data.l[2], xevent.xclient.data.l[3], xevent.xclient.data.l[4]); + data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]); } } - else if (xevent.xclient.message_type == videodata->XdndPosition) { + else if (xevent->xclient.message_type == videodata->XdndPosition) { #ifdef DEBUG_XEVENTS Atom act= videodata->XdndActionCopy; if(xdnd_version >= 2) { - act = xevent.xclient.data.l[4]; + act = xevent->xclient.data.l[4]; } printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act)); #endif @@ -1093,8 +1086,8 @@ X11_DispatchEvent(_THIS) /* reply with status */ memset(&m, 0, sizeof(XClientMessageEvent)); m.type = ClientMessage; - m.display = xevent.xclient.display; - m.window = xevent.xclient.data.l[0]; + m.display = xevent->xclient.display; + m.window = xevent->xclient.data.l[0]; m.message_type = videodata->XdndStatus; m.format=32; m.data.l[0] = data->xwindow; @@ -1103,47 +1096,47 @@ X11_DispatchEvent(_THIS) m.data.l[3] = 0; m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */ - X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); + X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent*)&m); X11_XFlush(display); } - else if(xevent.xclient.message_type == videodata->XdndDrop) { + else if(xevent->xclient.message_type == videodata->XdndDrop) { if (data->xdnd_req == None) { /* say again - not interested! */ memset(&m, 0, sizeof(XClientMessageEvent)); m.type = ClientMessage; - m.display = xevent.xclient.display; - m.window = xevent.xclient.data.l[0]; + m.display = xevent->xclient.display; + m.window = xevent->xclient.data.l[0]; m.message_type = videodata->XdndFinished; m.format=32; m.data.l[0] = data->xwindow; m.data.l[1] = 0; m.data.l[2] = None; /* fail! */ - X11_XSendEvent(display, xevent.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); + X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent*)&m); } else { /* convert */ if(xdnd_version >= 1) { - X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent.xclient.data.l[2]); + X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, xevent->xclient.data.l[2]); } else { X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime); } } } - else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && - (xevent.xclient.format == 32) && - (xevent.xclient.data.l[0] == videodata->_NET_WM_PING)) { + else if ((xevent->xclient.message_type == videodata->WM_PROTOCOLS) && + (xevent->xclient.format == 32) && + (xevent->xclient.data.l[0] == videodata->_NET_WM_PING)) { Window root = DefaultRootWindow(display); #ifdef DEBUG_XEVENTS printf("window %p: _NET_WM_PING\n", data); #endif - xevent.xclient.window = root; - X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xevent); + xevent->xclient.window = root; + X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent); break; } - else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && - (xevent.xclient.format == 32) && - (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) { + else if ((xevent->xclient.message_type == videodata->WM_PROTOCOLS) && + (xevent->xclient.format == 32) && + (xevent->xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) { #ifdef DEBUG_XEVENTS printf("window %p: WM_DELETE_WINDOW\n", data); @@ -1151,9 +1144,9 @@ X11_DispatchEvent(_THIS) SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0); break; } - else if ((xevent.xclient.message_type == videodata->WM_PROTOCOLS) && - (xevent.xclient.format == 32) && - (xevent.xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) { + else if ((xevent->xclient.message_type == videodata->WM_PROTOCOLS) && + (xevent->xclient.format == 32) && + (xevent->xclient.data.l[0] == videodata->WM_TAKE_FOCUS)) { #ifdef DEBUG_XEVENTS printf("window %p: WM_TAKE_FOCUS\n", data); @@ -1167,7 +1160,7 @@ X11_DispatchEvent(_THIS) /* Do we need to refresh ourselves? */ case Expose:{ #ifdef DEBUG_XEVENTS - printf("window %p: Expose (count = %d)\n", data, xevent.xexpose.count); + printf("window %p: Expose (count = %d)\n", data, xevent->xexpose.count); #endif SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0); } @@ -1177,10 +1170,10 @@ X11_DispatchEvent(_THIS) SDL_Mouse *mouse = SDL_GetMouse(); if(!mouse->relative_mode || mouse->relative_mode_warp) { #ifdef DEBUG_MOTION - printf("window %p: X11 motion: %d,%d\n", data, xevent.xmotion.x, xevent.xmotion.y); + printf("window %p: X11 motion: %d,%d\n", data, xevent->xmotion.x, xevent->xmotion.y); #endif - SDL_SendMouseMotion(data->window, 0, 0, xevent.xmotion.x, xevent.xmotion.y); + SDL_SendMouseMotion(data->window, 0, 0, xevent->xmotion.x, xevent->xmotion.y); } } break; @@ -1188,15 +1181,15 @@ X11_DispatchEvent(_THIS) case ButtonPress:{ int xticks = 0, yticks = 0; #ifdef DEBUG_XEVENTS - printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent.xbutton.button); + printf("window %p: ButtonPress (X11 button = %d)\n", data, xevent->xbutton.button); #endif - if (X11_IsWheelEvent(display,&xevent,&xticks, &yticks)) { + if (X11_IsWheelEvent(display,xevent,&xticks, &yticks)) { SDL_SendMouseWheel(data->window, 0, (float) xticks, (float) yticks, SDL_MOUSEWHEEL_NORMAL); } else { SDL_bool ignore_click = SDL_FALSE; - int button = xevent.xbutton.button; + int button = xevent->xbutton.button; if(button == Button1) { - if (ProcessHitTest(_this, data, &xevent)) { + if (ProcessHitTest(_this, data, xevent)) { SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0); break; /* don't pass this event on to app. */ } @@ -1217,18 +1210,18 @@ X11_DispatchEvent(_THIS) SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button); } } - X11_UpdateUserTime(data, xevent.xbutton.time); + X11_UpdateUserTime(data, xevent->xbutton.time); } break; case ButtonRelease:{ - int button = xevent.xbutton.button; + int button = xevent->xbutton.button; /* The X server sends a Release event for each Press for wheels. Ignore them. */ int xticks = 0, yticks = 0; #ifdef DEBUG_XEVENTS - printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent.xbutton.button); + printf("window %p: ButtonRelease (X11 button = %d)\n", data, xevent->xbutton.button); #endif - if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) { + if (!X11_IsWheelEvent(display, xevent, &xticks, &yticks)) { if (button > 7) { /* see explanation at case ButtonPress */ button -= (8-SDL_BUTTON_X1); @@ -1245,13 +1238,13 @@ X11_DispatchEvent(_THIS) Atom real_type; unsigned long items_read, items_left; - char *name = X11_XGetAtomName(display, xevent.xproperty.atom); + char *name = X11_XGetAtomName(display, xevent->xproperty.atom); if (name) { - printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent.xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent.xproperty.time); + printf("window %p: PropertyNotify: %s %s time=%lu\n", data, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time); X11_XFree(name); } - status = X11_XGetWindowProperty(display, data->xwindow, xevent.xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); + status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); if (status == Success && items_read > 0) { if (real_type == XA_INTEGER) { int *values = (int *)propdata; @@ -1323,16 +1316,16 @@ X11_DispatchEvent(_THIS) set _NET_WM_USER_TIME here, though. That's only for legit user interaction with the window. */ if (!data->user_time) { - data->user_time = xevent.xproperty.time; + data->user_time = xevent->xproperty.time; } - if (xevent.xproperty.atom == data->videodata->_NET_WM_STATE) { + if (xevent->xproperty.atom == data->videodata->_NET_WM_STATE) { /* Get the new state from the window manager. Compositing window managers can alter visibility of windows without ever mapping / unmapping them, so we handle that here, because they use the NETWM protocol to notify us of changes. */ - const Uint32 flags = X11_GetNetWMState(_this, xevent.xproperty.window); + const Uint32 flags = X11_GetNetWMState(_this, xevent->xproperty.window); const Uint32 changed = flags ^ data->window->flags; if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) { @@ -1350,7 +1343,7 @@ X11_DispatchEvent(_THIS) SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESTORED, 0, 0); } } - } else if (xevent.xproperty.atom == videodata->XKLAVIER_STATE) { + } else if (xevent->xproperty.atom == videodata->XKLAVIER_STATE) { /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify events when the keyboard layout changes (for example, changing from English to French on the menubar's keyboard @@ -1359,7 +1352,7 @@ X11_DispatchEvent(_THIS) right approach, but it seems to work. */ X11_UpdateKeymap(_this); SDL_SendKeymapChangedEvent(); - } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) { + } else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) { Atom type; int format; unsigned long nitems, bytes_after; @@ -1382,10 +1375,10 @@ X11_DispatchEvent(_THIS) break; case SelectionNotify: { - Atom target = xevent.xselection.target; + Atom target = xevent->xselection.target; #ifdef DEBUG_XEVENTS printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data, - xevent.xselection.requestor, xevent.xselection.target); + xevent->xselection.requestor, xevent->xselection.target); #endif if (target == data->xdnd_req) { /* read data */ @@ -1433,7 +1426,7 @@ X11_DispatchEvent(_THIS) default:{ #ifdef DEBUG_XEVENTS - printf("window %p: Unhandled event %d\n", data, xevent.type); + printf("window %p: Unhandled event %d\n", data, xevent->type); #endif } break; @@ -1482,10 +1475,79 @@ X11_Pending(Display * display) return (0); } +void +X11_SendWakeupEvent(_THIS, SDL_Window *window) +{ + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + Display *req_display = data->request_display; + Window xwindow = ((SDL_WindowData *) window->driverdata)->xwindow; + XClientMessageEvent event; + + memset(&event, 0, sizeof(XClientMessageEvent)); + event.type = ClientMessage; + event.display = req_display; + event.send_event = True; + event.message_type = data->_SDL_WAKEUP; + event.format = 8; + + X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *) &event); + /* XSendEvent returns a status and it could be BadValue or BadWindow. If an + error happens it is an SDL's internal error and there is nothing we can do here. */ + X11_XFlush(req_display); +} + +int +X11_WaitEventTimeout(_THIS, int timeout) +{ + SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata; + Display *display; + XEvent xevent; + + if (!videodata) { + return 0; + } + display = videodata->display; + + SDL_zero(xevent); + + if (timeout == 0) { + if (X11_Pending(display)) { + X11_XNextEvent(display, &xevent); + } else { + return 0; + } + } else if (timeout > 0) { + int display_fd = ConnectionNumber(display); + fd_set readset; + struct timeval tv_timeout; + FD_ZERO(&readset); + FD_SET(display_fd, &readset); + tv_timeout.tv_sec = (timeout / 1000); + tv_timeout.tv_usec = (timeout % 1000) * 1000; + if (select(display_fd + 1, &readset, NULL, NULL, &tv_timeout) > 0) { + X11_XNextEvent(display, &xevent); + } else { + return 0; + } + } else { + X11_XNextEvent(display, &xevent); + } + + X11_DispatchEvent(_this, &xevent); + +#ifdef SDL_USE_IME + if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){ + SDL_IME_PumpEvents(); + } +#endif + return 1; +} + void X11_PumpEvents(_THIS) { SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + XEvent xevent; if (data->last_mode_change_deadline) { if (SDL_TICKS_PASSED(SDL_GetTicks(), data->last_mode_change_deadline)) { @@ -1508,9 +1570,12 @@ X11_PumpEvents(_THIS) } } + SDL_zero(xevent); + /* Keep processing pending events */ while (X11_Pending(data->display)) { - X11_DispatchEvent(_this); + X11_XNextEvent(data->display, &xevent); + X11_DispatchEvent(_this, &xevent); } #ifdef SDL_USE_IME diff --git a/src/video/x11/SDL_x11events.h b/src/video/x11/SDL_x11events.h index 6f386e7c6..34899d458 100644 --- a/src/video/x11/SDL_x11events.h +++ b/src/video/x11/SDL_x11events.h @@ -24,6 +24,8 @@ #define SDL_x11events_h_ extern void X11_PumpEvents(_THIS); +extern int X11_WaitEventTimeout(_THIS, int timeout); +extern void X11_SendWakeupEvent(_THIS, SDL_Window *window); extern void X11_SuspendScreenSaver(_THIS); #endif /* SDL_x11events_h_ */ diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c index fa543adef..2dc7013da 100644 --- a/src/video/x11/SDL_x11video.c +++ b/src/video/x11/SDL_x11video.c @@ -105,7 +105,13 @@ X11_DeleteDevice(SDL_VideoDevice * device) X11_XSetErrorHandler(orig_x11_errhandler); X11_XCloseDisplay(data->display); } + if (data->request_display) { + X11_XCloseDisplay(data->request_display); + } SDL_free(data->windowlist); + if (device->wakeup_lock) { + SDL_DestroyMutex(device->wakeup_lock); + } SDL_free(device->driverdata); SDL_free(device); @@ -178,10 +184,22 @@ X11_CreateDevice(int devindex) return NULL; } device->driverdata = data; + device->wakeup_lock = SDL_CreateMutex(); data->global_mouse_changed = SDL_TRUE; data->display = x11_display; + data->request_display = X11_XOpenDisplay(display); + if (data->request_display == NULL) { + X11_XCloseDisplay(data->display); + SDL_free(device->driverdata); + SDL_free(device); + SDL_X11_UnloadSymbols(); + return NULL; + } + + device->wakeup_lock = SDL_CreateMutex(); + #ifdef X11_DEBUG X11_XSynchronize(data->display, True); #endif @@ -201,6 +219,8 @@ X11_CreateDevice(int devindex) device->SetDisplayMode = X11_SetDisplayMode; device->SuspendScreenSaver = X11_SuspendScreenSaver; device->PumpEvents = X11_PumpEvents; + device->WaitEventTimeout = X11_WaitEventTimeout; + device->SendWakeupEvent = X11_SendWakeupEvent; device->CreateSDLWindow = X11_CreateWindow; device->CreateSDLWindowFrom = X11_CreateWindowFrom; @@ -403,6 +423,7 @@ X11_VideoInit(_THIS) GET_ATOM(_NET_WM_USER_TIME); GET_ATOM(_NET_ACTIVE_WINDOW); GET_ATOM(_NET_FRAME_EXTENTS); + GET_ATOM(_SDL_WAKEUP); GET_ATOM(UTF8_STRING); GET_ATOM(PRIMARY); GET_ATOM(XdndEnter); diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index 11f7e9d84..7281e1130 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -75,6 +75,7 @@ typedef struct SDL_VideoData { Display *display; + Display *request_display; char *classname; pid_t pid; XIM im; @@ -111,6 +112,7 @@ typedef struct SDL_VideoData Atom _NET_WM_USER_TIME; Atom _NET_ACTIVE_WINDOW; Atom _NET_FRAME_EXTENTS; + Atom _SDL_WAKEUP; Atom UTF8_STRING; Atom PRIMARY; Atom XdndEnter; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 970578505..2b0b92b0e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,6 +20,7 @@ add_definitions(-DHAVE_OPENGL) endif() add_executable(checkkeys checkkeys.c) +add_executable(checkkeysthreads checkkeysthreads.c) add_executable(loopwave loopwave.c) add_executable(loopwavequeue loopwavequeue.c) add_executable(testresample testresample.c) diff --git a/test/Makefile.in b/test/Makefile.in index 6cf97ed2c..be907ae37 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -9,6 +9,7 @@ LIBS = @LIBS@ TARGETS = \ checkkeys$(EXE) \ + checkkeysthreads$(EXE) \ controllermap$(EXE) \ loopwave$(EXE) \ loopwavequeue$(EXE) \ @@ -83,6 +84,9 @@ Makefile: $(srcdir)/Makefile.in checkkeys$(EXE): $(srcdir)/checkkeys.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) +checkkeysthreads$(EXE): $(srcdir)/checkkeysthreads.c + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + loopwave$(EXE): $(srcdir)/loopwave.c $(CC) -o $@ $^ $(CFLAGS) $(LIBS) diff --git a/test/checkkeysthreads.c b/test/checkkeysthreads.c new file mode 100644 index 000000000..b4091ca03 --- /dev/null +++ b/test/checkkeysthreads.c @@ -0,0 +1,275 @@ +/* + Copyright (C) 1997-2020 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +/* Simple program: Loop, watching keystrokes + Note that you need to call SDL_PollEvent() or SDL_WaitEvent() to + pump the event loop and catch keystrokes. +*/ + +#include +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +#include "SDL.h" + +int done; + +/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ +static void +quit(int rc) +{ + SDL_Quit(); + exit(rc); +} + +static void +print_string(char **text, size_t *maxlen, const char *fmt, ...) +{ + int len; + va_list ap; + + va_start(ap, fmt); + len = SDL_vsnprintf(*text, *maxlen, fmt, ap); + if (len > 0) { + *text += len; + if ( ((size_t) len) < *maxlen ) { + *maxlen -= (size_t) len; + } else { + *maxlen = 0; + } + } + va_end(ap); +} + +static void +print_modifiers(char **text, size_t *maxlen) +{ + int mod; + print_string(text, maxlen, " modifiers:"); + mod = SDL_GetModState(); + if (!mod) { + print_string(text, maxlen, " (none)"); + return; + } + if (mod & KMOD_LSHIFT) + print_string(text, maxlen, " LSHIFT"); + if (mod & KMOD_RSHIFT) + print_string(text, maxlen, " RSHIFT"); + if (mod & KMOD_LCTRL) + print_string(text, maxlen, " LCTRL"); + if (mod & KMOD_RCTRL) + print_string(text, maxlen, " RCTRL"); + if (mod & KMOD_LALT) + print_string(text, maxlen, " LALT"); + if (mod & KMOD_RALT) + print_string(text, maxlen, " RALT"); + if (mod & KMOD_LGUI) + print_string(text, maxlen, " LGUI"); + if (mod & KMOD_RGUI) + print_string(text, maxlen, " RGUI"); + if (mod & KMOD_NUM) + print_string(text, maxlen, " NUM"); + if (mod & KMOD_CAPS) + print_string(text, maxlen, " CAPS"); + if (mod & KMOD_MODE) + print_string(text, maxlen, " MODE"); +} + +static void +PrintModifierState() +{ + char message[512]; + char *spot; + size_t left; + + spot = message; + left = sizeof(message); + + print_modifiers(&spot, &left); + SDL_Log("Initial state:%s\n", message); +} + +static void +PrintKey(SDL_Keysym * sym, SDL_bool pressed, SDL_bool repeat) +{ + char message[512]; + char *spot; + size_t left; + + spot = message; + left = sizeof(message); + + /* Print the keycode, name and state */ + if (sym->sym) { + print_string(&spot, &left, + "Key %s: scancode %d = %s, keycode 0x%08X = %s ", + pressed ? "pressed " : "released", + sym->scancode, + SDL_GetScancodeName(sym->scancode), + sym->sym, SDL_GetKeyName(sym->sym)); + } else { + print_string(&spot, &left, + "Unknown Key (scancode %d = %s) %s ", + sym->scancode, + SDL_GetScancodeName(sym->scancode), + pressed ? "pressed " : "released"); + } + print_modifiers(&spot, &left); + if (repeat) { + print_string(&spot, &left, " (repeat)"); + } + SDL_Log("%s\n", message); + fflush(stderr); +} + +static void +PrintText(char *eventtype, char *text) +{ + char *spot, expanded[1024]; + + expanded[0] = '\0'; + for ( spot = text; *spot; ++spot ) + { + size_t length = SDL_strlen(expanded); + SDL_snprintf(expanded + length, sizeof(expanded) - length, "\\x%.2x", (unsigned char)*spot); + } + SDL_Log("%s Text (%s): \"%s%s\"\n", eventtype, expanded, *text == '"' ? "\\" : "", text); +} + +void +loop() +{ + SDL_Event event; + /* Check for events */ + /*SDL_WaitEvent(&event); emscripten does not like waiting*/ + + fprintf(stderr, "starting loop\n"); fflush(stderr); + // while (SDL_PollEvent(&event)) { + while (!done && SDL_WaitEvent(&event)) { + fprintf(stderr, "got event type: %d\n", event.type); fflush(stderr); + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + PrintKey(&event.key.keysym, (event.key.state == SDL_PRESSED) ? SDL_TRUE : SDL_FALSE, (event.key.repeat) ? SDL_TRUE : SDL_FALSE); + break; + case SDL_TEXTEDITING: + PrintText("EDIT", event.text.text); + break; + case SDL_TEXTINPUT: + PrintText("INPUT", event.text.text); + break; + case SDL_MOUSEBUTTONDOWN: + /* Left button quits the app, other buttons toggles text input */ + fprintf(stderr, "mouse button down button: %d (LEFT=%d)\n", event.button.button, SDL_BUTTON_LEFT); fflush(stderr); + if (event.button.button == SDL_BUTTON_LEFT) { + done = 1; + } else { + if (SDL_IsTextInputActive()) { + SDL_Log("Stopping text input\n"); + SDL_StopTextInput(); + } else { + SDL_Log("Starting text input\n"); + SDL_StartTextInput(); + } + } + break; + case SDL_QUIT: + done = 1; + break; + default: + break; + } + fprintf(stderr, "waiting new event\n"); fflush(stderr); + } + fprintf(stderr, "exiting event loop\n"); fflush(stderr); +#ifdef __EMSCRIPTEN__ + if (done) { + emscripten_cancel_main_loop(); + } +#endif +} + +/* Very simple thread - counts 0 to 9 delaying 50ms between increments */ +static int ping_thread(void *ptr) +{ + int cnt; + SDL_Event sdlevent; + memset(&sdlevent, 0 , sizeof(SDL_Event)); + for (cnt = 0; cnt < 10; ++cnt) { + fprintf(stderr, "sending event (%d/%d) from thread.\n", cnt + 1, 10); fflush(stderr); + sdlevent.type = SDL_KEYDOWN; + sdlevent.key.keysym.sym = SDLK_1; + SDL_PushEvent(&sdlevent); + SDL_Delay(1000 + rand() % 1000); + } + return cnt; +} + +int +main(int argc, char *argv[]) +{ + SDL_Window *window; + + /* Enable standard application logging */ + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + /* Initialize SDL */ + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); + return (1); + } + + /* Set 640x480 video mode */ + window = SDL_CreateWindow("CheckKeys Test", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 640, 480, 0); + if (!window) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create 640x480 window: %s\n", + SDL_GetError()); + quit(2); + } + +#if __IPHONEOS__ + /* Creating the context creates the view, which we need to show keyboard */ + SDL_GL_CreateContext(window); +#endif + + SDL_StartTextInput(); + + /* Print initial modifier state */ + SDL_PumpEvents(); + PrintModifierState(); + + /* Watch keystrokes */ + done = 0; + + SDL_Thread *thread; + thread = SDL_CreateThread(ping_thread, "PingThread", (void *)NULL); + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(loop, 0, 1); +#else + while (!done) { + loop(); + } +#endif + + SDL_WaitThread(thread, NULL); + SDL_Quit(); + return (0); +} + +/* vi: set ts=4 sw=4 expandtab: */