Browse Source

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.
Francesco Abbate 4 years ago
parent
commit
0dd7024d55

+ 106 - 0
src/events/SDL_events.c

@@ -759,6 +759,80 @@ SDL_PollEvent(SDL_Event * event)
     return SDL_WaitEventTimeout(event, 0);
     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
 int
 SDL_WaitEvent(SDL_Event * event)
 SDL_WaitEvent(SDL_Event * event)
 {
 {
@@ -768,11 +842,24 @@ SDL_WaitEvent(SDL_Event * event)
 int
 int
 SDL_WaitEventTimeout(SDL_Event * event, int timeout)
 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;
     Uint32 expiration = 0;
 
 
     if (timeout > 0)
     if (timeout > 0)
         expiration = SDL_GetTicks() + timeout;
         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 (;;) {
     for (;;) {
         SDL_PumpEvents();
         SDL_PumpEvents();
         switch (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
         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
 int
 SDL_PushEvent(SDL_Event * event)
 SDL_PushEvent(SDL_Event * event)
 {
 {
@@ -845,6 +950,7 @@ SDL_PushEvent(SDL_Event * event)
         return -1;
         return -1;
     }
     }
 
 
+    SDL_SendWakeupEvent();
     SDL_GestureProcessEvent(event);
     SDL_GestureProcessEvent(event);
 
 
     return 1;
     return 1;

+ 5 - 0
src/video/SDL_sysvideo.h

@@ -290,6 +290,8 @@ struct SDL_VideoDevice
     /*
     /*
      * Event manager functions
      * Event manager functions
      */
      */
+    int (*WaitEventTimeout) (_THIS, int timeout);
+    void (*SendWakeupEvent) (_THIS, SDL_Window *window);
     void (*PumpEvents) (_THIS);
     void (*PumpEvents) (_THIS);
 
 
     /* Suspend the screensaver */
     /* Suspend the screensaver */
@@ -324,6 +326,9 @@ struct SDL_VideoDevice
     /* Data common to all drivers */
     /* Data common to all drivers */
     SDL_bool is_dummy;
     SDL_bool is_dummy;
     SDL_bool suspend_screensaver;
     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;
     int num_displays;
     SDL_VideoDisplay *displays;
     SDL_VideoDisplay *displays;
     SDL_Window *windows;
     SDL_Window *windows;

+ 2 - 0
src/video/cocoa/SDL_cocoaevents.h

@@ -25,6 +25,8 @@
 
 
 extern void Cocoa_RegisterApp(void);
 extern void Cocoa_RegisterApp(void);
 extern void Cocoa_PumpEvents(_THIS);
 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);
 extern void Cocoa_SuspendScreenSaver(_THIS);
 
 
 #endif /* SDL_cocoaevents_h_ */
 #endif /* SDL_cocoaevents_h_ */

+ 49 - 5
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
 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
     /* Update activity every 30 seconds to prevent screensaver */
     /* Update activity every 30 seconds to prevent screensaver */
@@ -530,9 +529,9 @@ Cocoa_PumpEvents(_THIS)
 #endif
 #endif
 
 
     for ( ; ; ) {
     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 ) {
         if ( event == nil ) {
-            break;
+            return 0;
         }
         }
 
 
         if (!s_bShouldHandleEventsInSDLApplication) {
         if (!s_bShouldHandleEventsInSDLApplication) {
@@ -541,7 +540,52 @@ Cocoa_PumpEvents(_THIS)
 
 
         // Pass events down to SDLApplication to be handled in sendEvent:
         // Pass events down to SDLApplication to be handled in sendEvent:
         [NSApp sendEvent:event];
         [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
 void

+ 6 - 0
src/video/cocoa/SDL_cocoavideo.m

@@ -38,6 +38,9 @@ static void Cocoa_VideoQuit(_THIS);
 static void
 static void
 Cocoa_DeleteDevice(SDL_VideoDevice * device)
 Cocoa_DeleteDevice(SDL_VideoDevice * device)
 {
 {
+    if (device->wakeup_lock) {
+        SDL_DestroyMutex(device->wakeup_lock);
+    }
     SDL_free(device->driverdata);
     SDL_free(device->driverdata);
     SDL_free(device);
     SDL_free(device);
 }
 }
@@ -63,6 +66,7 @@ Cocoa_CreateDevice(int devindex)
         return NULL;
         return NULL;
     }
     }
     device->driverdata = data;
     device->driverdata = data;
+    device->wakeup_lock = SDL_CreateMutex();
 
 
     /* Set the function pointers */
     /* Set the function pointers */
     device->VideoInit = Cocoa_VideoInit;
     device->VideoInit = Cocoa_VideoInit;
@@ -73,6 +77,8 @@ Cocoa_CreateDevice(int devindex)
     device->GetDisplayModes = Cocoa_GetDisplayModes;
     device->GetDisplayModes = Cocoa_GetDisplayModes;
     device->SetDisplayMode = Cocoa_SetDisplayMode;
     device->SetDisplayMode = Cocoa_SetDisplayMode;
     device->PumpEvents = Cocoa_PumpEvents;
     device->PumpEvents = Cocoa_PumpEvents;
+    device->WaitEventTimeout = Cocoa_WaitEventTimeout;
+    device->SendWakeupEvent = Cocoa_SendWakeupEvent;
     device->SuspendScreenSaver = Cocoa_SuspendScreenSaver;
     device->SuspendScreenSaver = Cocoa_SuspendScreenSaver;
 
 
     device->CreateSDLWindow = Cocoa_CreateWindow;
     device->CreateSDLWindow = Cocoa_CreateWindow;

+ 39 - 0
src/video/windows/SDL_windowsevents.c

@@ -1276,6 +1276,45 @@ void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata)
     g_WindowsMessageHookData = 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
 void
 WIN_PumpEvents(_THIS)
 WIN_PumpEvents(_THIS)
 {
 {

+ 2 - 0
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,
 extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam,
                                        LPARAM lParam);
                                        LPARAM lParam);
 extern void WIN_PumpEvents(_THIS);
 extern void WIN_PumpEvents(_THIS);
+extern void WIN_SendWakeupEvent(_THIS, SDL_Window *window);
+extern int  WIN_WaitEventTimeout(_THIS, int timeout);
 
 
 #endif /* SDL_windowsevents_h_ */
 #endif /* SDL_windowsevents_h_ */
 
 

+ 10 - 1
src/video/windows/SDL_windowsvideo.c

@@ -87,7 +87,9 @@ WIN_DeleteDevice(SDL_VideoDevice * device)
     if (data->shcoreDLL) {
     if (data->shcoreDLL) {
         SDL_UnloadObject(data->shcoreDLL);
         SDL_UnloadObject(data->shcoreDLL);
     }
     }
-
+    if (device->wakeup_lock) {
+        SDL_DestroyMutex(device->wakeup_lock);
+    }
     SDL_free(device->driverdata);
     SDL_free(device->driverdata);
     SDL_free(device);
     SDL_free(device);
 }
 }
@@ -113,6 +115,7 @@ WIN_CreateDevice(int devindex)
         return NULL;
         return NULL;
     }
     }
     device->driverdata = data;
     device->driverdata = data;
+    device->wakeup_lock = SDL_CreateMutex();
 
 
     data->userDLL = SDL_LoadObject("USER32.DLL");
     data->userDLL = SDL_LoadObject("USER32.DLL");
     if (data->userDLL) {
     if (data->userDLL) {
@@ -139,6 +142,8 @@ WIN_CreateDevice(int devindex)
     device->GetDisplayModes = WIN_GetDisplayModes;
     device->GetDisplayModes = WIN_GetDisplayModes;
     device->SetDisplayMode = WIN_SetDisplayMode;
     device->SetDisplayMode = WIN_SetDisplayMode;
     device->PumpEvents = WIN_PumpEvents;
     device->PumpEvents = WIN_PumpEvents;
+    device->WaitEventTimeout = WIN_WaitEventTimeout;
+    device->SendWakeupEvent = WIN_SendWakeupEvent;
     device->SuspendScreenSaver = WIN_SuspendScreenSaver;
     device->SuspendScreenSaver = WIN_SuspendScreenSaver;
 
 
     device->CreateSDLWindow = WIN_CreateWindow;
     device->CreateSDLWindow = WIN_CreateWindow;
@@ -226,6 +231,8 @@ VideoBootStrap WINDOWS_bootstrap = {
 int
 int
 WIN_VideoInit(_THIS)
 WIN_VideoInit(_THIS)
 {
 {
+    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+
     if (WIN_InitModes(_this) < 0) {
     if (WIN_InitModes(_this) < 0) {
         return -1;
         return -1;
     }
     }
@@ -236,6 +243,8 @@ WIN_VideoInit(_THIS)
     SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
     SDL_AddHintCallback(SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, UpdateWindowsEnableMessageLoop, NULL);
     SDL_AddHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
     SDL_AddHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
 
 
+    data->_SDL_WAKEUP = RegisterWindowMessageA("_SDL_WAKEUP");
+
     return 0;
     return 0;
 }
 }
 
 

+ 1 - 0
src/video/windows/SDL_windowsvideo.h

@@ -190,6 +190,7 @@ typedef struct SDL_VideoData
     TSFSink *ime_ippasink;
     TSFSink *ime_ippasink;
 
 
     BYTE pre_hook_key_state[256];
     BYTE pre_hook_key_state[256];
+    UINT _SDL_WAKEUP;
 } SDL_VideoData;
 } SDL_VideoData;
 
 
 extern SDL_bool g_WindowsEnableMessageLoop;
 extern SDL_bool g_WindowsEnableMessageLoop;

+ 191 - 126
src/video/x11/SDL_x11events.c

@@ -670,42 +670,35 @@ isReparentNotify(Display *display, XEvent *ev, XPointer arg)
 }
 }
 
 
 static void
 static void
-X11_DispatchEvent(_THIS)
+X11_DispatchEvent(_THIS, XEvent *xevent)
 {
 {
     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
+    XkbEvent* xkbEvent = (XkbEvent*) xevent;
     Display *display;
     Display *display;
     SDL_WindowData *data;
     SDL_WindowData *data;
-    XEvent xevent;
-    XkbEvent* xkbEvent;
     int orig_event_type;
     int orig_event_type;
     KeyCode orig_keycode;
     KeyCode orig_keycode;
     XClientMessageEvent m;
     XClientMessageEvent m;
     int i;
     int i;
 
 
-    if (!videodata) {
-        return;
-    }
+    SDL_assert(videodata != NULL);
     display = videodata->display;
     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
     /* Save the original keycode for dead keys, which are filtered out by
        the XFilterEvent() call below.
        the XFilterEvent() call below.
     */
     */
-    orig_event_type = xevent.type;
+    orig_event_type = xevent->type;
     if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
     if (orig_event_type == KeyPress || orig_event_type == KeyRelease) {
-        orig_keycode = xevent.xkey.keycode;
+        orig_keycode = xevent->xkey.keycode;
     } else {
     } else {
         orig_keycode = 0;
         orig_keycode = 0;
     }
     }
 
 
     /* filter events catchs XIM events and sends them to the correct handler */
     /* 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
 #if 0
         printf("Filtered event type = %d display = %d window = %d\n",
         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
 #endif
         /* Make sure dead key press/release events are sent */
         /* Make sure dead key press/release events are sent */
         /* But only if we're using one of the DBus IMEs, otherwise
         /* 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)
 #if defined(HAVE_IBUS_IBUS_H) || defined(HAVE_FCITX)
             SDL_Scancode scancode = videodata->key_layout[orig_keycode];
             SDL_Scancode scancode = videodata->key_layout[orig_keycode];
             videodata->filter_code = orig_keycode;
             videodata->filter_code = orig_keycode;
-            videodata->filter_time = xevent.xkey.time;
+            videodata->filter_time = xevent->xkey.time;
 
 
             if (orig_event_type == KeyPress) {
             if (orig_event_type == KeyPress) {
                 SDL_SendKeyboardKey(SDL_PRESSED, scancode);
                 SDL_SendKeyboardKey(SDL_PRESSED, scancode);
@@ -727,8 +720,8 @@ X11_DispatchEvent(_THIS)
     }
     }
 
 
 #if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
 #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;
         return;
     }
     }
 #endif
 #endif
@@ -739,18 +732,18 @@ X11_DispatchEvent(_THIS)
 
 
         SDL_VERSION(&wmmsg.version);
         SDL_VERSION(&wmmsg.version);
         wmmsg.subsystem = SDL_SYSWM_X11;
         wmmsg.subsystem = SDL_SYSWM_X11;
-        wmmsg.msg.x11.event = xevent;
+        wmmsg.msg.x11.event = *xevent;
         SDL_SendSysWMEvent(&wmmsg);
         SDL_SendSysWMEvent(&wmmsg);
     }
     }
 
 
 #if 0
 #if 0
     printf("type = %d display = %d window = %d\n",
     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
 #endif
 
 
     if ((videodata->clipboard_window != None) &&
     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;
         return;
     }
     }
 
 
@@ -758,7 +751,7 @@ X11_DispatchEvent(_THIS)
     if (videodata && videodata->windowlist) {
     if (videodata && videodata->windowlist) {
         for (i = 0; i < videodata->numwindows; ++i) {
         for (i = 0; i < videodata->numwindows; ++i) {
             if ((videodata->windowlist[i] != NULL) &&
             if ((videodata->windowlist[i] != NULL) &&
-                (videodata->windowlist[i]->xwindow == xevent.xany.window)) {
+                (videodata->windowlist[i]->xwindow == xevent->xany.window)) {
                 data = videodata->windowlist[i];
                 data = videodata->windowlist[i];
                 break;
                 break;
             }
             }
@@ -766,19 +759,19 @@ X11_DispatchEvent(_THIS)
     }
     }
     if (!data) {
     if (!data) {
         /* The window for KeymapNotify, etc events is 0 */
         /* The window for KeymapNotify, etc events is 0 */
-        if (xevent.type == KeymapNotify) {
+        if (xevent->type == KeymapNotify) {
             if (SDL_GetKeyboardFocus() != NULL) {
             if (SDL_GetKeyboardFocus() != NULL) {
                 X11_ReconcileKeyboardState(_this);
                 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? */
             /* Has the keyboard layout changed? */
-            const int request = xevent.xmapping.request;
+            const int request = xevent->xmapping.request;
 
 
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
             printf("window %p: MappingNotify!\n", data);
             printf("window %p: MappingNotify!\n", data);
 #endif
 #endif
             if ((request == MappingKeyboard) || (request == MappingModifier)) {
             if ((request == MappingKeyboard) || (request == MappingModifier)) {
-                X11_XRefreshKeyboardMapping(&xevent.xmapping);
+                X11_XRefreshKeyboardMapping(&xevent->xmapping);
             }
             }
 
 
             X11_UpdateKeymap(_this);
             X11_UpdateKeymap(_this);
@@ -787,28 +780,28 @@ X11_DispatchEvent(_THIS)
         return;
         return;
     }
     }
 
 
-    switch (xevent.type) {
+    switch (xevent->type) {
 
 
         /* Gaining mouse coverage? */
         /* Gaining mouse coverage? */
     case EnterNotify:{
     case EnterNotify:{
             SDL_Mouse *mouse = SDL_GetMouse();
             SDL_Mouse *mouse = SDL_GetMouse();
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
             printf("window %p: EnterNotify! (%d,%d,%d)\n", data,
             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");
                 printf("Mode: NotifyGrab\n");
-            if (xevent.xcrossing.mode == NotifyUngrab)
+            if (xevent->xcrossing.mode == NotifyUngrab)
                 printf("Mode: NotifyUngrab\n");
                 printf("Mode: NotifyUngrab\n");
 #endif
 #endif
             SDL_SetMouseFocus(data->window);
             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) {
             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 */
             /* We ungrab in LeaveNotify, so we may need to grab again here */
@@ -819,21 +812,21 @@ X11_DispatchEvent(_THIS)
     case LeaveNotify:{
     case LeaveNotify:{
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
             printf("window %p: LeaveNotify! (%d,%d,%d)\n", data,
             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");
                 printf("Mode: NotifyGrab\n");
-            if (xevent.xcrossing.mode == NotifyUngrab)
+            if (xevent->xcrossing.mode == NotifyUngrab)
                 printf("Mode: NotifyUngrab\n");
                 printf("Mode: NotifyUngrab\n");
 #endif
 #endif
             if (!SDL_GetMouse()->relative_mode) {
             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
                 /* 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. */
                    on Mutter, we need to ungrab the keyboard when the the mouse leaves. */
@@ -848,7 +841,7 @@ X11_DispatchEvent(_THIS)
 
 
         /* Gaining input focus? */
         /* Gaining input focus? */
     case FocusIn:{
     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 */
                 /* Someone is handling a global hotkey, ignore it */
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
                 printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data);
                 printf("window %p: FocusIn (NotifyGrab/NotifyUngrab, ignoring)\n", data);
@@ -856,7 +849,7 @@ X11_DispatchEvent(_THIS)
                 break;
                 break;
             }
             }
 
 
-            if (xevent.xfocus.detail == NotifyInferior || xevent.xfocus.detail == NotifyPointer) {
+            if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) {
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
                 printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data);
                 printf("window %p: FocusIn (NotifyInferior/NotifyPointer, ignoring)\n", data);
 #endif
 #endif
@@ -882,14 +875,14 @@ X11_DispatchEvent(_THIS)
 
 
         /* Losing input focus? */
         /* Losing input focus? */
     case FocusOut:{
     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 */
                 /* Someone is handling a global hotkey, ignore it */
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
                 printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data);
                 printf("window %p: FocusOut (NotifyGrab/NotifyUngrab, ignoring)\n", data);
 #endif
 #endif
                 break;
                 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
                 /* We still have focus if a child gets focus. We also don't
                    care about the position of the pointer when the keyboard
                    care about the position of the pointer when the keyboard
                    focus changed. */
                    focus changed. */
@@ -917,20 +910,20 @@ X11_DispatchEvent(_THIS)
 
 
         /* Key press? */
         /* Key press? */
     case KeyPress:{
     case KeyPress:{
-            KeyCode keycode = xevent.xkey.keycode;
+            KeyCode keycode = xevent->xkey.keycode;
             KeySym keysym = NoSymbol;
             KeySym keysym = NoSymbol;
             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
             char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
             Status status = 0;
             Status status = 0;
             SDL_bool handled_by_ime = SDL_FALSE;
             SDL_bool handled_by_ime = SDL_FALSE;
 
 
 #ifdef DEBUG_XEVENTS
 #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
 #endif
 #if 1
 #if 1
             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
             if (videodata->key_layout[keycode] == SDL_SCANCODE_UNKNOWN && keycode) {
                 int min_keycode, max_keycode;
                 int min_keycode, max_keycode;
                 X11_XDisplayKeycodes(display, &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,
                 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 <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
                         "The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).\n",
                         keycode, keycode - min_keycode, keysym,
                         keycode, keycode - min_keycode, keysym,
@@ -941,13 +934,13 @@ X11_DispatchEvent(_THIS)
             SDL_zeroa(text);
             SDL_zeroa(text);
 #ifdef X_HAVE_UTF8_STRING
 #ifdef X_HAVE_UTF8_STRING
             if (data->ic) {
             if (data->ic) {
-                X11_Xutf8LookupString(data->ic, &xevent.xkey, text, sizeof(text),
+                X11_Xutf8LookupString(data->ic, &xevent->xkey, text, sizeof(text),
                                   &keysym, &status);
                                   &keysym, &status);
             } else {
             } else {
-                X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
+                X11_XLookupString(&xevent->xkey, text, sizeof(text), &keysym, NULL);
             }
             }
 #else
 #else
-            X11_XLookupString(&xevent.xkey, text, sizeof(text), &keysym, NULL);
+            X11_XLookupString(&xevent->xkey, text, sizeof(text), &keysym, NULL);
 #endif
 #endif
 
 
 #ifdef SDL_USE_IME
 #ifdef SDL_USE_IME
@@ -957,7 +950,7 @@ X11_DispatchEvent(_THIS)
 #endif
 #endif
             if (!handled_by_ime) {
             if (!handled_by_ime) {
                 /* Don't send the key if it looks like a duplicate of a filtered key sent by an 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]);
                     SDL_SendKeyboardKey(SDL_PRESSED, videodata->key_layout[keycode]);
                 }
                 }
                 if(*text) {
                 if(*text) {
@@ -965,18 +958,18 @@ X11_DispatchEvent(_THIS)
                 }
                 }
             }
             }
 
 
-            X11_UpdateUserTime(data, xevent.xkey.time);
+            X11_UpdateUserTime(data, xevent->xkey.time);
         }
         }
         break;
         break;
 
 
         /* Key release? */
         /* Key release? */
     case KeyRelease:{
     case KeyRelease:{
-            KeyCode keycode = xevent.xkey.keycode;
+            KeyCode keycode = xevent->xkey.keycode;
 
 
 #ifdef DEBUG_XEVENTS
 #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
 #endif
-            if (X11_KeyRepeat(display, &xevent)) {
+            if (X11_KeyRepeat(display, xevent)) {
                 /* We're about to get a repeated key down, ignore the key up */
                 /* We're about to get a repeated key down, ignore the key up */
                 break;
                 break;
             }
             }
@@ -992,8 +985,8 @@ X11_DispatchEvent(_THIS)
             printf("window %p: UnmapNotify!\n", data);
             printf("window %p: UnmapNotify!\n", data);
 #endif
 #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 {
             } else {
                 X11_DispatchUnmapNotify(data);
                 X11_DispatchUnmapNotify(data);
             }
             }
@@ -1013,27 +1006,27 @@ X11_DispatchEvent(_THIS)
     case ConfigureNotify:{
     case ConfigureNotify:{
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
             printf("window %p: ConfigureNotify! (position: %d,%d, size: %dx%d)\n", data,
             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
 #endif
             /* Real configure notify events are relative to the parent, synthetic events are absolute. */
             /* 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;
                 unsigned int NumChildren;
                 Window ChildReturn, Root, Parent;
                 Window ChildReturn, Root, Parent;
                 Window * Children;
                 Window * Children;
                 /* Translate these coodinates back to relative to root */
                 /* 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);
                                         &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,
                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED,
-                                    xevent.xconfigure.x, xevent.xconfigure.y);
+                                    xevent->xconfigure.x, xevent->xconfigure.y);
 #ifdef SDL_USE_IME
 #ifdef SDL_USE_IME
                 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
                 if(SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE){
                     /* Update IME candidate list position */
                     /* Update IME candidate list position */
@@ -1041,13 +1034,13 @@ X11_DispatchEvent(_THIS)
                 }
                 }
 #endif
 #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,
                 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;
         break;
 
 
@@ -1056,11 +1049,11 @@ X11_DispatchEvent(_THIS)
 
 
             static int xdnd_version=0;
             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
 #ifdef DEBUG_XEVENTS
                 printf("XID of source window : %ld\n", data->xdnd_source);
                 printf("XID of source window : %ld\n", data->xdnd_source);
                 printf("Protocol version to use : %d\n", xdnd_version);
                 printf("Protocol version to use : %d\n", xdnd_version);
@@ -1076,15 +1069,15 @@ X11_DispatchEvent(_THIS)
                     X11_XFree(p.data);
                     X11_XFree(p.data);
                 } else {
                 } else {
                     /* pick from list of three */
                     /* 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
 #ifdef DEBUG_XEVENTS
                 Atom act= videodata->XdndActionCopy;
                 Atom act= videodata->XdndActionCopy;
                 if(xdnd_version >= 2) {
                 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));
                 printf("Action requested by user is : %s\n", X11_XGetAtomName(display , act));
 #endif
 #endif
@@ -1093,8 +1086,8 @@ X11_DispatchEvent(_THIS)
                 /* reply with status */
                 /* reply with status */
                 memset(&m, 0, sizeof(XClientMessageEvent));
                 memset(&m, 0, sizeof(XClientMessageEvent));
                 m.type = ClientMessage;
                 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.message_type = videodata->XdndStatus;
                 m.format=32;
                 m.format=32;
                 m.data.l[0] = data->xwindow;
                 m.data.l[0] = data->xwindow;
@@ -1103,47 +1096,47 @@ X11_DispatchEvent(_THIS)
                 m.data.l[3] = 0;
                 m.data.l[3] = 0;
                 m.data.l[4] = videodata->XdndActionCopy; /* we only accept copying anyway */
                 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);
                 X11_XFlush(display);
             }
             }
-            else if(xevent.xclient.message_type == videodata->XdndDrop) {
+            else if(xevent->xclient.message_type == videodata->XdndDrop) {
                 if (data->xdnd_req == None) {
                 if (data->xdnd_req == None) {
                     /* say again - not interested! */
                     /* say again - not interested! */
                     memset(&m, 0, sizeof(XClientMessageEvent));
                     memset(&m, 0, sizeof(XClientMessageEvent));
                     m.type = ClientMessage;
                     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.message_type = videodata->XdndFinished;
                     m.format=32;
                     m.format=32;
                     m.data.l[0] = data->xwindow;
                     m.data.l[0] = data->xwindow;
                     m.data.l[1] = 0;
                     m.data.l[1] = 0;
                     m.data.l[2] = None; /* fail! */
                     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 {
                 } else {
                     /* convert */
                     /* convert */
                     if(xdnd_version >= 1) {
                     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 {
                     } else {
                         X11_XConvertSelection(display, videodata->XdndSelection, data->xdnd_req, videodata->PRIMARY, data->xwindow, CurrentTime);
                         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);
                 Window root = DefaultRootWindow(display);
 
 
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
                 printf("window %p: _NET_WM_PING\n", data);
                 printf("window %p: _NET_WM_PING\n", data);
 #endif
 #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;
                 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
 #ifdef DEBUG_XEVENTS
                 printf("window %p: WM_DELETE_WINDOW\n", data);
                 printf("window %p: WM_DELETE_WINDOW\n", data);
@@ -1151,9 +1144,9 @@ X11_DispatchEvent(_THIS)
                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
                 SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_CLOSE, 0, 0);
                 break;
                 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
 #ifdef DEBUG_XEVENTS
                 printf("window %p: WM_TAKE_FOCUS\n", data);
                 printf("window %p: WM_TAKE_FOCUS\n", data);
@@ -1167,7 +1160,7 @@ X11_DispatchEvent(_THIS)
         /* Do we need to refresh ourselves? */
         /* Do we need to refresh ourselves? */
     case Expose:{
     case Expose:{
 #ifdef DEBUG_XEVENTS
 #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
 #endif
             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
             SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_EXPOSED, 0, 0);
         }
         }
@@ -1177,10 +1170,10 @@ X11_DispatchEvent(_THIS)
             SDL_Mouse *mouse = SDL_GetMouse();
             SDL_Mouse *mouse = SDL_GetMouse();
             if(!mouse->relative_mode || mouse->relative_mode_warp) {
             if(!mouse->relative_mode || mouse->relative_mode_warp) {
 #ifdef DEBUG_MOTION
 #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
 #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;
         break;
@@ -1188,15 +1181,15 @@ X11_DispatchEvent(_THIS)
     case ButtonPress:{
     case ButtonPress:{
             int xticks = 0, yticks = 0;
             int xticks = 0, yticks = 0;
 #ifdef DEBUG_XEVENTS
 #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
 #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);
                 SDL_SendMouseWheel(data->window, 0, (float) xticks, (float) yticks, SDL_MOUSEWHEEL_NORMAL);
             } else {
             } else {
                 SDL_bool ignore_click = SDL_FALSE;
                 SDL_bool ignore_click = SDL_FALSE;
-                int button = xevent.xbutton.button;
+                int button = xevent->xbutton.button;
                 if(button == Button1) {
                 if(button == Button1) {
-                    if (ProcessHitTest(_this, data, &xevent)) {
+                    if (ProcessHitTest(_this, data, xevent)) {
                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
                         SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_HIT_TEST, 0, 0);
                         break;  /* don't pass this event on to app. */
                         break;  /* don't pass this event on to app. */
                     }
                     }
@@ -1217,18 +1210,18 @@ X11_DispatchEvent(_THIS)
                     SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
                     SDL_SendMouseButton(data->window, 0, SDL_PRESSED, button);
                 }
                 }
             }
             }
-            X11_UpdateUserTime(data, xevent.xbutton.time);
+            X11_UpdateUserTime(data, xevent->xbutton.time);
         }
         }
         break;
         break;
 
 
     case ButtonRelease:{
     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. */
             /* The X server sends a Release event for each Press for wheels. Ignore them. */
             int xticks = 0, yticks = 0;
             int xticks = 0, yticks = 0;
 #ifdef DEBUG_XEVENTS
 #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
 #endif
-            if (!X11_IsWheelEvent(display, &xevent, &xticks, &yticks)) {
+            if (!X11_IsWheelEvent(display, xevent, &xticks, &yticks)) {
                 if (button > 7) {
                 if (button > 7) {
                     /* see explanation at case ButtonPress */
                     /* see explanation at case ButtonPress */
                     button -= (8-SDL_BUTTON_X1);
                     button -= (8-SDL_BUTTON_X1);
@@ -1245,13 +1238,13 @@ X11_DispatchEvent(_THIS)
             Atom real_type;
             Atom real_type;
             unsigned long items_read, items_left;
             unsigned long items_read, items_left;
 
 
-            char *name = X11_XGetAtomName(display, xevent.xproperty.atom);
+            char *name = X11_XGetAtomName(display, xevent->xproperty.atom);
             if (name) {
             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);
                 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 (status == Success && items_read > 0) {
                 if (real_type == XA_INTEGER) {
                 if (real_type == XA_INTEGER) {
                     int *values = (int *)propdata;
                     int *values = (int *)propdata;
@@ -1323,16 +1316,16 @@ X11_DispatchEvent(_THIS)
                 set _NET_WM_USER_TIME here, though. That's only for legit
                 set _NET_WM_USER_TIME here, though. That's only for legit
                 user interaction with the window. */
                 user interaction with the window. */
             if (!data->user_time) {
             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.
                 /* Get the new state from the window manager.
                    Compositing window managers can alter visibility of windows
                    Compositing window managers can alter visibility of windows
                    without ever mapping / unmapping them, so we handle that here,
                    without ever mapping / unmapping them, so we handle that here,
                    because they use the NETWM protocol to notify us of changes.
                    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;
                 const Uint32 changed = flags ^ data->window->flags;
 
 
                 if ((changed & SDL_WINDOW_HIDDEN) || (changed & SDL_WINDOW_FULLSCREEN)) {
                 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);
                         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
                 /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
                    events when the keyboard layout changes (for example,
                    events when the keyboard layout changes (for example,
                    changing from English to French on the menubar's keyboard
                    changing from English to French on the menubar's keyboard
@@ -1359,7 +1352,7 @@ X11_DispatchEvent(_THIS)
                    right approach, but it seems to work. */
                    right approach, but it seems to work. */
                 X11_UpdateKeymap(_this);
                 X11_UpdateKeymap(_this);
                 SDL_SendKeymapChangedEvent();
                 SDL_SendKeymapChangedEvent();
-            } else if (xevent.xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
+            } else if (xevent->xproperty.atom == videodata->_NET_FRAME_EXTENTS) {
                 Atom type;
                 Atom type;
                 int format;
                 int format;
                 unsigned long nitems, bytes_after;
                 unsigned long nitems, bytes_after;
@@ -1382,10 +1375,10 @@ X11_DispatchEvent(_THIS)
         break;
         break;
 
 
     case SelectionNotify: {
     case SelectionNotify: {
-            Atom target = xevent.xselection.target;
+            Atom target = xevent->xselection.target;
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
             printf("window %p: SelectionNotify (requestor = %ld, target = %ld)\n", data,
-                xevent.xselection.requestor, xevent.xselection.target);
+                xevent->xselection.requestor, xevent->xselection.target);
 #endif
 #endif
             if (target == data->xdnd_req) {
             if (target == data->xdnd_req) {
                 /* read data */
                 /* read data */
@@ -1433,7 +1426,7 @@ X11_DispatchEvent(_THIS)
 
 
     default:{
     default:{
 #ifdef DEBUG_XEVENTS
 #ifdef DEBUG_XEVENTS
-            printf("window %p: Unhandled event %d\n", data, xevent.type);
+            printf("window %p: Unhandled event %d\n", data, xevent->type);
 #endif
 #endif
         }
         }
         break;
         break;
@@ -1482,10 +1475,79 @@ X11_Pending(Display * display)
     return (0);
     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
 void
 X11_PumpEvents(_THIS)
 X11_PumpEvents(_THIS)
 {
 {
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+    XEvent xevent;
 
 
     if (data->last_mode_change_deadline) {
     if (data->last_mode_change_deadline) {
         if (SDL_TICKS_PASSED(SDL_GetTicks(), 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 */
     /* Keep processing pending events */
     while (X11_Pending(data->display)) {
     while (X11_Pending(data->display)) {
-        X11_DispatchEvent(_this);
+        X11_XNextEvent(data->display, &xevent);
+        X11_DispatchEvent(_this, &xevent);
     }
     }
 
 
 #ifdef SDL_USE_IME
 #ifdef SDL_USE_IME

+ 2 - 0
src/video/x11/SDL_x11events.h

@@ -24,6 +24,8 @@
 #define SDL_x11events_h_
 #define SDL_x11events_h_
 
 
 extern void X11_PumpEvents(_THIS);
 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);
 extern void X11_SuspendScreenSaver(_THIS);
 
 
 #endif /* SDL_x11events_h_ */
 #endif /* SDL_x11events_h_ */

+ 21 - 0
src/video/x11/SDL_x11video.c

@@ -105,7 +105,13 @@ X11_DeleteDevice(SDL_VideoDevice * device)
         X11_XSetErrorHandler(orig_x11_errhandler);
         X11_XSetErrorHandler(orig_x11_errhandler);
         X11_XCloseDisplay(data->display);
         X11_XCloseDisplay(data->display);
     }
     }
+    if (data->request_display) {
+        X11_XCloseDisplay(data->request_display);
+    }
     SDL_free(data->windowlist);
     SDL_free(data->windowlist);
+    if (device->wakeup_lock) {
+        SDL_DestroyMutex(device->wakeup_lock);
+    }
     SDL_free(device->driverdata);
     SDL_free(device->driverdata);
     SDL_free(device);
     SDL_free(device);
 
 
@@ -178,10 +184,22 @@ X11_CreateDevice(int devindex)
         return NULL;
         return NULL;
     }
     }
     device->driverdata = data;
     device->driverdata = data;
+    device->wakeup_lock = SDL_CreateMutex();
 
 
     data->global_mouse_changed = SDL_TRUE;
     data->global_mouse_changed = SDL_TRUE;
 
 
     data->display = x11_display;
     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
 #ifdef X11_DEBUG
     X11_XSynchronize(data->display, True);
     X11_XSynchronize(data->display, True);
 #endif
 #endif
@@ -201,6 +219,8 @@ X11_CreateDevice(int devindex)
     device->SetDisplayMode = X11_SetDisplayMode;
     device->SetDisplayMode = X11_SetDisplayMode;
     device->SuspendScreenSaver = X11_SuspendScreenSaver;
     device->SuspendScreenSaver = X11_SuspendScreenSaver;
     device->PumpEvents = X11_PumpEvents;
     device->PumpEvents = X11_PumpEvents;
+    device->WaitEventTimeout = X11_WaitEventTimeout;
+    device->SendWakeupEvent = X11_SendWakeupEvent;
 
 
     device->CreateSDLWindow = X11_CreateWindow;
     device->CreateSDLWindow = X11_CreateWindow;
     device->CreateSDLWindowFrom = X11_CreateWindowFrom;
     device->CreateSDLWindowFrom = X11_CreateWindowFrom;
@@ -403,6 +423,7 @@ X11_VideoInit(_THIS)
     GET_ATOM(_NET_WM_USER_TIME);
     GET_ATOM(_NET_WM_USER_TIME);
     GET_ATOM(_NET_ACTIVE_WINDOW);
     GET_ATOM(_NET_ACTIVE_WINDOW);
     GET_ATOM(_NET_FRAME_EXTENTS);
     GET_ATOM(_NET_FRAME_EXTENTS);
+    GET_ATOM(_SDL_WAKEUP);
     GET_ATOM(UTF8_STRING);
     GET_ATOM(UTF8_STRING);
     GET_ATOM(PRIMARY);
     GET_ATOM(PRIMARY);
     GET_ATOM(XdndEnter);
     GET_ATOM(XdndEnter);

+ 2 - 0
src/video/x11/SDL_x11video.h

@@ -75,6 +75,7 @@
 typedef struct SDL_VideoData
 typedef struct SDL_VideoData
 {
 {
     Display *display;
     Display *display;
+    Display *request_display;
     char *classname;
     char *classname;
     pid_t pid;
     pid_t pid;
     XIM im;
     XIM im;
@@ -111,6 +112,7 @@ typedef struct SDL_VideoData
     Atom _NET_WM_USER_TIME;
     Atom _NET_WM_USER_TIME;
     Atom _NET_ACTIVE_WINDOW;
     Atom _NET_ACTIVE_WINDOW;
     Atom _NET_FRAME_EXTENTS;
     Atom _NET_FRAME_EXTENTS;
+    Atom _SDL_WAKEUP;
     Atom UTF8_STRING;
     Atom UTF8_STRING;
     Atom PRIMARY;
     Atom PRIMARY;
     Atom XdndEnter;
     Atom XdndEnter;

+ 1 - 0
test/CMakeLists.txt

@@ -20,6 +20,7 @@ add_definitions(-DHAVE_OPENGL)
 endif()
 endif()
 
 
 add_executable(checkkeys checkkeys.c)
 add_executable(checkkeys checkkeys.c)
+add_executable(checkkeysthreads checkkeysthreads.c)
 add_executable(loopwave loopwave.c)
 add_executable(loopwave loopwave.c)
 add_executable(loopwavequeue loopwavequeue.c)
 add_executable(loopwavequeue loopwavequeue.c)
 add_executable(testresample testresample.c)
 add_executable(testresample testresample.c)

+ 4 - 0
test/Makefile.in

@@ -9,6 +9,7 @@ LIBS	= @LIBS@
 
 
 TARGETS = \
 TARGETS = \
 	checkkeys$(EXE) \
 	checkkeys$(EXE) \
+	checkkeysthreads$(EXE) \
 	controllermap$(EXE) \
 	controllermap$(EXE) \
 	loopwave$(EXE) \
 	loopwave$(EXE) \
 	loopwavequeue$(EXE) \
 	loopwavequeue$(EXE) \
@@ -83,6 +84,9 @@ Makefile: $(srcdir)/Makefile.in
 checkkeys$(EXE): $(srcdir)/checkkeys.c
 checkkeys$(EXE): $(srcdir)/checkkeys.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
 
+checkkeysthreads$(EXE): $(srcdir)/checkkeysthreads.c
+	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
+
 loopwave$(EXE): $(srcdir)/loopwave.c
 loopwave$(EXE): $(srcdir)/loopwave.c
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 	$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
 
 

+ 275 - 0
test/checkkeysthreads.c

@@ -0,0 +1,275 @@
+/*
+  Copyright (C) 1997-2020 Sam Lantinga <[email protected]>
+
+  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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#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: */