Просмотр исходного кода

Up-to-date QNX support (#14806)

Co-authored-by: Roberto Speranza <rsperanza@qnx.com>
Co-authored-by: Darcy Phipps <dphipps@qnx.com>
Co-authored-by: Pierce McKinnon <pimckinnon@qnx.com>
eleir9268 1 месяц назад
Родитель
Сommit
f4a541682a

+ 1 - 1
CMakeLists.txt

@@ -1813,7 +1813,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
           "${SDL3_SOURCE_DIR}/src/audio/netbsd/*.h"
           "${SDL3_SOURCE_DIR}/src/audio/netbsd/*.h"
         )
         )
         set(HAVE_SDL_AUDIO TRUE)
         set(HAVE_SDL_AUDIO TRUE)
-    elseif(QNX)
+    elseif(QNX AND (CMAKE_SYSTEM_VERSION VERSION_LESS "8.0.0"))
         set(SDL_AUDIO_DRIVER_QNX 1)
         set(SDL_AUDIO_DRIVER_QNX 1)
         sdl_glob_sources(
         sdl_glob_sources(
           "${SDL3_SOURCE_DIR}/src/audio/qnx/*.c"
           "${SDL3_SOURCE_DIR}/src/audio/qnx/*.c"

+ 44 - 0
docs/README-qnx.md

@@ -0,0 +1,44 @@
+QNX
+=======
+
+SDL port for QNX, providing both screen and Wayland video backends.
+
+This was originally contributed by Elad Lahav for QNX 7.0.
+
+The port was later improved and adapted for QNX 8.0 by:
+- Ethan Leir
+- Roberto Speranza
+- Darcy Phipps
+- Jai Moraes
+- Pierce McKinnon
+
+Further changes to enable Wayland with the EGL backend were made by Felix Xing
+and Aaron Bassett.
+
+
+## Building
+
+Building SDL3 for QNX requires Wayland to be built and installed. The commands
+to build it are,
+```bash
+# Note, if you're cross-compiling, you will need to source qnxsdp-env.sh and
+# provide the path to a cmake toolchain file with -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_DIR/qnx.nto.toolchain.cmake
+cmake -B build -DCMAKE_BUILD_TYPE=Release -DSDL_X11=0
+cmake --build build
+cmake --install build
+```
+
+## QNX self-hosted
+
+QNX provides a self-hosted environment, available with [the free license](https://www.qnx.com/products/everywhere/).
+This is the easiest way to get your hands on SDL.
+
+## QNX build-files
+
+You can find the cross-compiled build tools at https://github.com/qnx-ports/build-files
+
+## Notes - screen
+
+- Currently, only software and OpenGLES2 rendering is supported.
+- Unless your application is managed by a window manager capable of closing the application, you will need to quit it yourself.
+- Restraining the mouse to a window or warping the mouse cursor will not work.

+ 1 - 1
include/SDL3/SDL_platform_defines.h

@@ -277,7 +277,7 @@
 #define SDL_PLATFORM_OSF 1
 #define SDL_PLATFORM_OSF 1
 #endif
 #endif
 
 
-#ifdef __QNXNTO__
+#if defined(__QNXNTO__) || defined(__QNX__)
 
 
 /**
 /**
  * A preprocessor macro that is only defined if compiling for QNX Neutrino.
  * A preprocessor macro that is only defined if compiling for QNX Neutrino.

+ 9 - 0
include/SDL3/SDL_video.h

@@ -1560,6 +1560,13 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_GetWindowParent(SDL_Window *window)
  * - `SDL_PROP_WINDOW_OPENVR_OVERLAY_ID_NUMBER`: the OpenVR Overlay Handle ID
  * - `SDL_PROP_WINDOW_OPENVR_OVERLAY_ID_NUMBER`: the OpenVR Overlay Handle ID
  *   for the associated overlay window.
  *   for the associated overlay window.
  *
  *
+ * On QNX:
+ *
+ * - `SDL_PROP_WINDOW_QNX_WINDOW_POINTER`: the screen_window_t associated with
+ *   the window.
+ * - `SDL_PROP_WINDOW_QNX_SURFACE_POINTER`: the EGLSurface associated with
+ *   the window
+ *
  * On Vivante:
  * On Vivante:
  *
  *
  * - `SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER`: the EGLNativeDisplayType
  * - `SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER`: the EGLNativeDisplayType
@@ -1644,6 +1651,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetWindowProperties(SDL_Window
 #define SDL_PROP_WINDOW_COCOA_WINDOW_POINTER                        "SDL.window.cocoa.window"
 #define SDL_PROP_WINDOW_COCOA_WINDOW_POINTER                        "SDL.window.cocoa.window"
 #define SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER                 "SDL.window.cocoa.metal_view_tag"
 #define SDL_PROP_WINDOW_COCOA_METAL_VIEW_TAG_NUMBER                 "SDL.window.cocoa.metal_view_tag"
 #define SDL_PROP_WINDOW_OPENVR_OVERLAY_ID_NUMBER                    "SDL.window.openvr.overlay_id"
 #define SDL_PROP_WINDOW_OPENVR_OVERLAY_ID_NUMBER                    "SDL.window.openvr.overlay_id"
+#define SDL_PROP_WINDOW_QNX_WINDOW_POINTER                          "SDL.window.qnx.window"
+#define SDL_PROP_WINDOW_QNX_SURFACE_POINTER                         "SDL.window.qnx.surface"
 #define SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER                     "SDL.window.vivante.display"
 #define SDL_PROP_WINDOW_VIVANTE_DISPLAY_POINTER                     "SDL.window.vivante.display"
 #define SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER                      "SDL.window.vivante.window"
 #define SDL_PROP_WINDOW_VIVANTE_WINDOW_POINTER                      "SDL.window.vivante.window"
 #define SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER                     "SDL.window.vivante.surface"
 #define SDL_PROP_WINDOW_VIVANTE_SURFACE_POINTER                     "SDL.window.vivante.surface"

+ 5 - 0
src/thread/pthread/SDL_systhread.c

@@ -252,7 +252,12 @@ bool SDL_SYS_SetThreadPriority(SDL_ThreadPriority priority)
     if (priority == SDL_THREAD_PRIORITY_LOW) {
     if (priority == SDL_THREAD_PRIORITY_LOW) {
         sched.sched_priority = sched_get_priority_min(policy);
         sched.sched_priority = sched_get_priority_min(policy);
     } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
     } else if (priority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
+#if defined(__QNX__)
+        /* io_snd complains about a client thread having priority >= 49 */
+        sched.sched_priority = 48;
+#else
         sched.sched_priority = sched_get_priority_max(policy);
         sched.sched_priority = sched_get_priority_max(policy);
+#endif
     } else {
     } else {
         int min_priority = sched_get_priority_min(policy);
         int min_priority = sched_get_priority_min(policy);
         int max_priority = sched_get_priority_max(policy);
         int max_priority = sched_get_priority_max(policy);

+ 7 - 0
src/video/SDL_egl.c

@@ -102,6 +102,13 @@
 #define DEFAULT_OGL_ES_PVR "libGLES_CM.so"
 #define DEFAULT_OGL_ES_PVR "libGLES_CM.so"
 #define DEFAULT_OGL_ES     "libGLESv1_CM.so"
 #define DEFAULT_OGL_ES     "libGLESv1_CM.so"
 
 
+#elif defined(SDL_VIDEO_DRIVER_QNX)
+// QNX
+#define DEFAULT_EGL        "libEGL.so.1"
+#define DEFAULT_OGL_ES2    "libGLESv2.so.1"
+#define DEFAULT_OGL_ES_PVR "libGLESv2.so.1"
+#define DEFAULT_OGL_ES     "libGLESv2.so.1"
+
 #else
 #else
 // Desktop Linux/Unix-like
 // Desktop Linux/Unix-like
 #define DEFAULT_OGL        "libGL.so.1"
 #define DEFAULT_OGL        "libGL.so.1"

+ 1 - 1
src/video/SDL_video.c

@@ -279,7 +279,7 @@ typedef struct
 
 
 static Uint32 SDL_DefaultGraphicsBackends(SDL_VideoDevice *_this)
 static Uint32 SDL_DefaultGraphicsBackends(SDL_VideoDevice *_this)
 {
 {
-#if (defined(SDL_VIDEO_OPENGL) && defined(SDL_PLATFORM_MACOS)) || (defined(SDL_PLATFORM_IOS) && !TARGET_OS_MACCATALYST)
+#if (defined(SDL_VIDEO_OPENGL) && defined(SDL_PLATFORM_MACOS)) || (defined(SDL_PLATFORM_IOS) && !TARGET_OS_MACCATALYST) || defined(SDL_PLATFORM_QNXNTO)
     if (_this->GL_CreateContext) {
     if (_this->GL_CreateContext) {
         return SDL_WINDOW_OPENGL;
         return SDL_WINDOW_OPENGL;
     }
     }

+ 44 - 5
src/video/qnx/SDL_qnx.h

@@ -1,6 +1,6 @@
 /*
 /*
   Simple DirectMedia Layer
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  Copyright (C) 2026 BlackBerry Limited
 
 
   This software is provided 'as-is', without any express or implied
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
   warranty.  In no event will the authors be held liable for any damages
@@ -26,23 +26,62 @@
 #include <screen/screen.h>
 #include <screen/screen.h>
 #include <EGL/egl.h>
 #include <EGL/egl.h>
 
 
-typedef struct
+typedef struct SDL_DisplayData
+{
+    screen_display_t screen_display;
+} SDL_DisplayData;
+
+typedef struct SDL_DisplayModeData
+{
+    int                   screen_format;
+    screen_display_mode_t screen_display_mode;
+} SDL_DisplayModeData;
+
+typedef struct SDL_WindowData
 {
 {
     screen_window_t window;
     screen_window_t window;
     EGLSurface      surface;
     EGLSurface      surface;
     EGLConfig       conf;
     EGLConfig       conf;
-} window_impl_t;
+    SDL_GLContext   context;
+    int             resize;
+    bool            has_focus;
+} SDL_WindowData;
+
+typedef struct SDL_CursorData
+{
+    screen_session_t session;
+    int              realized_shape;
+    bool             is_visible;
+} SDL_CursorData;
+
+typedef struct SDL_MouseData
+{
+    int      x_prev;
+    int      y_prev;
+} SDL_MouseData;
+
+extern screen_context_t * getContext();
+extern screen_event_t * getEvent();
 
 
 extern void handleKeyboardEvent(screen_event_t event);
 extern void handleKeyboardEvent(screen_event_t event);
+extern void handlePointerEvent(screen_event_t event);
 
 
-extern bool glGetConfig(EGLConfig *pconf, int *pformat);
+extern bool glInitConfig(SDL_WindowData *impl, int *pformat);
 extern bool glLoadLibrary(SDL_VideoDevice *_this, const char *name);
 extern bool glLoadLibrary(SDL_VideoDevice *_this, const char *name);
 extern SDL_FunctionPointer glGetProcAddress(SDL_VideoDevice *_this, const char *proc);
 extern SDL_FunctionPointer glGetProcAddress(SDL_VideoDevice *_this, const char *proc);
 extern SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window);
 extern SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window);
 extern bool glSetSwapInterval(SDL_VideoDevice *_this, int interval);
 extern bool glSetSwapInterval(SDL_VideoDevice *_this, int interval);
 extern bool glSwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
 extern bool glSwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
 extern bool glMakeCurrent(SDL_VideoDevice *_this, SDL_Window * window, SDL_GLContext context);
 extern bool glMakeCurrent(SDL_VideoDevice *_this, SDL_Window * window, SDL_GLContext context);
-extern void glDeleteContext(SDL_VideoDevice *_this, SDL_GLContext context);
+extern bool glDeleteContext(SDL_VideoDevice *_this, SDL_GLContext context);
 extern void glUnloadLibrary(SDL_VideoDevice *_this);
 extern void glUnloadLibrary(SDL_VideoDevice *_this);
 
 
+extern SDL_PixelFormat screenToPixelFormat(int screen_format);
+extern bool getDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
+extern bool setDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
+extern bool getDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect);
+
+extern void initMouse(SDL_VideoDevice *_this);
+extern void quitMouse(SDL_VideoDevice *_this);
+
 #endif
 #endif

+ 163 - 40
src/video/qnx/SDL_qnxgl.c

@@ -1,6 +1,6 @@
 /*
 /*
   Simple DirectMedia Layer
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  Copyright (C) 2026 BlackBerry Limited
 
 
   This software is provided 'as-is', without any express or implied
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
   warranty.  In no event will the authors be held liable for any damages
@@ -19,11 +19,119 @@
   3. This notice may not be removed or altered from any source distribution.
   3. This notice may not be removed or altered from any source distribution.
 */
 */
 
 
-#include "../../SDL_internal.h"
+#include "SDL_internal.h"
 #include "SDL_qnx.h"
 #include "SDL_qnx.h"
 
 
 static EGLDisplay   egl_disp;
 static EGLDisplay   egl_disp;
 
 
+struct DummyConfig
+{
+    int red_size;
+    int green_size;
+    int blue_size;
+    int alpha_size;
+    int native_id;
+};
+
+static struct DummyConfig getDummyConfigFromScreenSettings(int format)
+{
+    struct DummyConfig dummyConfig= {};
+
+    dummyConfig.native_id = format;
+    switch (format) {
+         case SCREEN_FORMAT_RGBX4444:
+            dummyConfig.red_size = 4;
+            dummyConfig.green_size = 4;
+            dummyConfig.blue_size = 4;
+            dummyConfig.alpha_size = 4;
+            break;
+         case SCREEN_FORMAT_RGBA5551:
+            dummyConfig.red_size = 5;
+            dummyConfig.green_size = 5;
+            dummyConfig.blue_size = 5;
+            dummyConfig.alpha_size = 1;
+            break;
+         case SCREEN_FORMAT_RGB565:
+            dummyConfig.red_size = 5;
+            dummyConfig.green_size = 6;
+            dummyConfig.blue_size = 5;
+            dummyConfig.alpha_size = 0;
+            break;
+         case SCREEN_FORMAT_RGB888:
+            dummyConfig.red_size = 8;
+            dummyConfig.green_size = 8;
+            dummyConfig.blue_size = 8;
+            dummyConfig.alpha_size = 0;
+            break;
+            case SCREEN_FORMAT_BGRA8888:
+            case SCREEN_FORMAT_BGRX8888:
+            case SCREEN_FORMAT_RGBA8888:
+         case SCREEN_FORMAT_RGBX8888:
+            dummyConfig.red_size = 8;
+            dummyConfig.green_size = 8;
+            dummyConfig.blue_size = 8;
+            dummyConfig.alpha_size = 8;
+            break;
+            default:
+                break;
+    }
+    return dummyConfig;
+}
+
+static EGLConfig chooseConfig(struct DummyConfig dummyConfig, EGLConfig* egl_configs, EGLint egl_num_configs)
+{
+   EGLConfig glConfig = (EGLConfig)0;
+
+    for (size_t ii = 0; ii < egl_num_configs; ii++) {
+        EGLint val;
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_SURFACE_TYPE, &val);
+        if (!(val & EGL_WINDOW_BIT)) {
+            continue;
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_RENDERABLE_TYPE, &val);
+        if (!(val & EGL_OPENGL_ES2_BIT)) {
+            continue;
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_DEPTH_SIZE, &val);
+        if (val == 0) {
+            continue;
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_RED_SIZE, &val);
+        if (val != dummyConfig.red_size) {
+           continue;
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_GREEN_SIZE, &val);
+        if (val != dummyConfig.green_size) {
+           continue;
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_BLUE_SIZE, &val);
+        if (val != dummyConfig.blue_size) {
+           continue;
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_ALPHA_SIZE, &val);
+        if (val != dummyConfig.alpha_size) {
+            continue;
+        }
+        if(!glConfig)
+        {
+            glConfig = egl_configs[ii];
+        }
+
+        eglGetConfigAttrib(egl_disp, egl_configs[ii], EGL_NATIVE_VISUAL_ID, &val);
+        if ((val != 0) && (val == dummyConfig.native_id)) {
+            return egl_configs[ii];
+        }
+    }
+    return glConfig;
+}
+
 /**
 /**
  * Detertmines the pixel format to use based on the current display and EGL
  * Detertmines the pixel format to use based on the current display and EGL
  * configuration.
  * configuration.
@@ -42,7 +150,7 @@ static int chooseFormat(EGLConfig egl_conf)
         case 32:
         case 32:
             return SCREEN_FORMAT_RGBX8888;
             return SCREEN_FORMAT_RGBX8888;
         case 24:
         case 24:
-            return SDL_PIXELFORMAT_RGB24;
+            return SCREEN_FORMAT_RGB888;
         case 16:
         case 16:
             switch (alpha_bit_depth) {
             switch (alpha_bit_depth) {
                 case 4:
                 case 4:
@@ -59,20 +167,18 @@ static int chooseFormat(EGLConfig egl_conf)
 
 
 /**
 /**
  * Enumerates the supported EGL configurations and chooses a suitable one.
  * Enumerates the supported EGL configurations and chooses a suitable one.
- * @param[out]  pconf   The chosen configuration
  * @param[out]  pformat The chosen pixel format
  * @param[out]  pformat The chosen pixel format
- * @return true if successful, -1 on error
+ * @return true if successful, false on error
  */
  */
-bool glGetConfig(EGLConfig *pconf, int *pformat)
+bool glInitConfig(SDL_WindowData *impl, int *pformat)
 {
 {
     EGLConfig egl_conf = (EGLConfig)0;
     EGLConfig egl_conf = (EGLConfig)0;
     EGLConfig *egl_configs;
     EGLConfig *egl_configs;
     EGLint egl_num_configs;
     EGLint egl_num_configs;
-    EGLint val;
     EGLBoolean rc;
     EGLBoolean rc;
-    EGLint i;
+    struct DummyConfig dummyconfig = {};
 
 
-    // Determine the numbfer of configurations.
+    // Determine the number of configurations.
     rc = eglGetConfigs(egl_disp, NULL, 0, &egl_num_configs);
     rc = eglGetConfigs(egl_disp, NULL, 0, &egl_num_configs);
     if (rc != EGL_TRUE) {
     if (rc != EGL_TRUE) {
         return false;
         return false;
@@ -96,30 +202,12 @@ bool glGetConfig(EGLConfig *pconf, int *pformat)
         return false;
         return false;
     }
     }
 
 
-    // Find a good configuration.
-    for (i = 0; i < egl_num_configs; i++) {
-        eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_SURFACE_TYPE, &val);
-        if (!(val & EGL_WINDOW_BIT)) {
-            continue;
-        }
-
-        eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_RENDERABLE_TYPE, &val);
-        if (!(val & EGL_OPENGL_ES2_BIT)) {
-            continue;
-        }
-
-        eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_DEPTH_SIZE, &val);
-        if (val == 0) {
-            continue;
-        }
-
-        egl_conf = egl_configs[i];
-        break;
-    }
+    dummyconfig = getDummyConfigFromScreenSettings(*pformat);
+    egl_conf = chooseConfig(dummyconfig, egl_configs, egl_num_configs);
+    *pformat = chooseFormat(egl_conf);
 
 
     SDL_free(egl_configs);
     SDL_free(egl_configs);
-    *pconf = egl_conf;
-    *pformat = chooseFormat(egl_conf);
+    impl->conf = egl_conf;
 
 
     return true;
     return true;
 }
 }
@@ -128,7 +216,7 @@ bool glGetConfig(EGLConfig *pconf, int *pformat)
  * Initializes the EGL library.
  * Initializes the EGL library.
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
  * @param   name    unused
  * @param   name    unused
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 bool glLoadLibrary(SDL_VideoDevice *_this, const char *name)
 bool glLoadLibrary(SDL_VideoDevice *_this, const char *name)
 {
 {
@@ -165,7 +253,7 @@ SDL_FunctionPointer glGetProcAddress(SDL_VideoDevice *_this, const char *proc)
  */
  */
 SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window)
 SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
     EGLContext      context;
     EGLContext      context;
     EGLSurface      surface;
     EGLSurface      surface;
 
 
@@ -201,6 +289,10 @@ SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window)
     eglMakeCurrent(egl_disp, surface, surface, context);
     eglMakeCurrent(egl_disp, surface, surface, context);
 
 
     impl->surface = surface;
     impl->surface = surface;
+    impl->context = context;
+
+    SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_QNX_SURFACE_POINTER, impl->surface);
+
     return context;
     return context;
 }
 }
 
 
@@ -208,7 +300,7 @@ SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window)
  * Sets a new value for the number of frames to display before swapping buffers.
  * Sets a new value for the number of frames to display before swapping buffers.
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
  * @param   interval    New interval value
  * @param   interval    New interval value
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 bool glSetSwapInterval(SDL_VideoDevice *_this, int interval)
 bool glSetSwapInterval(SDL_VideoDevice *_this, int interval)
 {
 {
@@ -223,13 +315,44 @@ bool glSetSwapInterval(SDL_VideoDevice *_this, int interval)
  * Swaps the EGL buffers associated with the given window
  * Swaps the EGL buffers associated with the given window
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
  * @param   window  Window to swap buffers for
  * @param   window  Window to swap buffers for
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 bool glSwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
 bool glSwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
 {
     // !!! FIXME: should we migrate this all over to use SDL_egl.c?
     // !!! FIXME: should we migrate this all over to use SDL_egl.c?
-    window_impl_t   *impl = (window_impl_t *)window->internal;
-    return eglSwapBuffers(egl_disp, impl->surface) == EGL_TRUE ? 0 : -1;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
+    {
+        if (impl->resize) {
+            EGLSurface surface;
+            struct {
+                EGLint render_buffer[2];
+                EGLint none;
+            } egl_surf_attr = {
+                .render_buffer = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER },
+                .none = EGL_NONE
+            };
+
+            if (eglMakeCurrent(egl_disp, NULL, NULL, impl->context) != EGL_TRUE) {
+                return false;
+            }
+            eglDestroySurface(egl_disp, impl->surface);
+
+            surface = eglCreateWindowSurface(egl_disp, impl->conf, impl->window,
+                                     (EGLint *)&egl_surf_attr);
+            if (surface == EGL_NO_SURFACE) {
+                return false;
+            }
+
+            if (eglMakeCurrent(egl_disp, surface, surface, impl->context) != EGL_TRUE) {
+                return false;
+            }
+
+            impl->surface = surface;
+            impl->resize = 0;
+        }
+    }
+
+    return eglSwapBuffers(egl_disp, impl->surface) == EGL_TRUE ? true : false;
 }
 }
 
 
 /**
 /**
@@ -237,15 +360,15 @@ bool glSwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
  * @param   window  SDL window associated with the context (maybe NULL)
  * @param   window  SDL window associated with the context (maybe NULL)
  * @param   context The context to activate
  * @param   context The context to activate
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 bool glMakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
 bool glMakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context)
 {
 {
-    window_impl_t   *impl;
+    SDL_WindowData   *impl;
     EGLSurface      surface = NULL;
     EGLSurface      surface = NULL;
 
 
     if (window) {
     if (window) {
-        impl = (window_impl_t *)window->internal;
+        impl = (SDL_WindowData *)window->internal;
         surface = impl->surface;
         surface = impl->surface;
     }
     }
 
 

+ 90 - 2
src/video/qnx/SDL_qnxkeyboard.c

@@ -1,6 +1,6 @@
 /*
 /*
   Simple DirectMedia Layer
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  Copyright (C) 2026 BlackBerry Limited
 
 
   This software is provided 'as-is', without any express or implied
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
   warranty.  In no event will the authors be held liable for any damages
@@ -19,7 +19,7 @@
   3. This notice may not be removed or altered from any source distribution.
   3. This notice may not be removed or altered from any source distribution.
 */
 */
 
 
-#include "../../SDL_internal.h"
+#include "SDL_internal.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "SDL3/SDL_scancode.h"
 #include "SDL3/SDL_scancode.h"
 #include "SDL3/SDL_events.h"
 #include "SDL3/SDL_events.h"
@@ -84,10 +84,81 @@ static int key_to_sdl[] = {
     [KEYCODE_LEFT] = SDL_SCANCODE_LEFT,
     [KEYCODE_LEFT] = SDL_SCANCODE_LEFT,
     [KEYCODE_PG_UP] = SDL_SCANCODE_PAGEUP,
     [KEYCODE_PG_UP] = SDL_SCANCODE_PAGEUP,
     [KEYCODE_PG_DOWN] = SDL_SCANCODE_PAGEDOWN,
     [KEYCODE_PG_DOWN] = SDL_SCANCODE_PAGEDOWN,
+    [KEYCODE_PRINT] = SDL_SCANCODE_PRINTSCREEN,
+    [KEYCODE_SCROLL_LOCK] = SDL_SCANCODE_SCROLLLOCK,
+    [KEYCODE_PAUSE] = SDL_SCANCODE_PAUSE,
+    [KEYCODE_INSERT] = SDL_SCANCODE_INSERT,
+    [KEYCODE_HOME] = SDL_SCANCODE_HOME,
+    [KEYCODE_DELETE] = SDL_SCANCODE_DELETE,
+    [KEYCODE_END] = SDL_SCANCODE_END,
+    [KEYCODE_NUM_LOCK] = SDL_SCANCODE_NUMLOCKCLEAR,
     [KEYCODE_RIGHT] = SDL_SCANCODE_RIGHT,
     [KEYCODE_RIGHT] = SDL_SCANCODE_RIGHT,
     [KEYCODE_RETURN] = SDL_SCANCODE_RETURN,
     [KEYCODE_RETURN] = SDL_SCANCODE_RETURN,
     [KEYCODE_TAB] = SDL_SCANCODE_TAB,
     [KEYCODE_TAB] = SDL_SCANCODE_TAB,
     [KEYCODE_ESCAPE] = SDL_SCANCODE_ESCAPE,
     [KEYCODE_ESCAPE] = SDL_SCANCODE_ESCAPE,
+    [KEYCODE_LEFT_CTRL] = SDL_SCANCODE_LCTRL,
+    [KEYCODE_RIGHT_CTRL] = SDL_SCANCODE_RCTRL,
+    [KEYCODE_LEFT_SHIFT] = SDL_SCANCODE_LSHIFT,
+    [KEYCODE_RIGHT_SHIFT] = SDL_SCANCODE_RSHIFT,
+    [KEYCODE_LEFT_ALT] = SDL_SCANCODE_LALT,
+    [KEYCODE_RIGHT_ALT] = SDL_SCANCODE_RALT,
+    [KEYCODE_BACKSPACE] = SDL_SCANCODE_BACKSPACE,
+    [KEYCODE_CAPS_LOCK] = SDL_SCANCODE_CAPSLOCK,
+    [KEYCODE_F1] = SDL_SCANCODE_F1,
+    [KEYCODE_F2] = SDL_SCANCODE_F2,
+    [KEYCODE_F3] = SDL_SCANCODE_F3,
+    [KEYCODE_F4] = SDL_SCANCODE_F4,
+    [KEYCODE_F5] = SDL_SCANCODE_F5,
+    [KEYCODE_F6] = SDL_SCANCODE_F6,
+    [KEYCODE_F7] = SDL_SCANCODE_F7,
+    [KEYCODE_F8] = SDL_SCANCODE_F8,
+    [KEYCODE_F9] = SDL_SCANCODE_F9,
+    [KEYCODE_F10] = SDL_SCANCODE_F10,
+    [KEYCODE_F11] = SDL_SCANCODE_F11,
+    [KEYCODE_F12] = SDL_SCANCODE_F12,
+    [KEYCODE_KP_DIVIDE] = SDL_SCANCODE_KP_DIVIDE,
+    [KEYCODE_KP_MULTIPLY] = SDL_SCANCODE_KP_MULTIPLY,
+    [KEYCODE_KP_MINUS] = SDL_SCANCODE_KP_MINUS,
+    [KEYCODE_KP_PLUS] = SDL_SCANCODE_KP_PLUS,
+    [KEYCODE_KP_ENTER] = SDL_SCANCODE_KP_ENTER,
+    /* NO SCREEN MAPPING FOR KEYPAD DIGITS
+    [KEYCODE_ZERO] = SDL_SCANCODE_KP_0,
+    [KEYCODE_ONE] = SDL_SCANCODE_KP_1,
+    [KEYCODE_TWO] = SDL_SCANCODE_KP_2,
+    [KEYCODE_THREE] = SDL_SCANCODE_KP_3,
+    [KEYCODE_FOUR] = SDL_SCANCODE_KP_4,
+    [KEYCODE_FIVE] = SDL_SCANCODE_KP_5,
+    [KEYCODE_SIX] = SDL_SCANCODE_KP_6,
+    [KEYCODE_SEVEN] = SDL_SCANCODE_KP_7,
+    [KEYCODE_EIGHT] = SDL_SCANCODE_KP_8,
+    [KEYCODE_NINE] = SDL_SCANCODE_KP_9,
+    [KEYCODE_PERIOD] = SDL_SCANCODE_KP_PERIOD,
+    */
+    [KEYCODE_POWER] = SDL_SCANCODE_POWER,
+    [KEYCODE_PLAY] = SDL_SCANCODE_EXECUTE,
+    [KEYCODE_HELP] = SDL_SCANCODE_HELP,
+    [KEYCODE_MENU] = SDL_SCANCODE_MENU,
+    [KEYCODE_AC_SELECT_ALL] = SDL_SCANCODE_SELECT,
+    [KEYCODE_STOP] = SDL_SCANCODE_STOP,
+    [KEYCODE_AC_UNDO] = SDL_SCANCODE_UNDO,
+    [KEYCODE_AC_CUT] = SDL_SCANCODE_CUT,
+    [KEYCODE_AC_COPY] = SDL_SCANCODE_COPY,
+    [KEYCODE_AC_PASTE] = SDL_SCANCODE_PASTE,
+    [KEYCODE_AC_FIND] = SDL_SCANCODE_FIND,
+    [KEYCODE_MUTE] = SDL_SCANCODE_MUTE,
+    [KEYCODE_VOLUME_UP] = SDL_SCANCODE_VOLUMEUP,
+    [KEYCODE_VOLUME_DOWN] = SDL_SCANCODE_VOLUMEDOWN,
+    [KEYCODE_SYSREQ] = SDL_SCANCODE_SYSREQ,
+    [KEYCODE_AC_CANCEL] = SDL_SCANCODE_CANCEL,
+    [KEYCODE_AC_SEARCH] = SDL_SCANCODE_AC_SEARCH,
+    [KEYCODE_AC_HOME] = SDL_SCANCODE_AC_HOME,
+    [KEYCODE_AC_BACK] = SDL_SCANCODE_AC_BACK,
+    [KEYCODE_AC_FORWARD] = SDL_SCANCODE_AC_FORWARD,
+    [KEYCODE_AC_STOP] = SDL_SCANCODE_AC_STOP,
+    [KEYCODE_AC_REFRESH] = SDL_SCANCODE_AC_REFRESH,
+    [KEYCODE_AC_BOOKMARKS] = SDL_SCANCODE_AC_BOOKMARKS,
+    [KEYCODE_EJECT] = SDL_SCANCODE_MEDIA_EJECT,
+    [KEYCODE_SLEEP] = SDL_SCANCODE_SLEEP,
 };
 };
 
 
 /**
 /**
@@ -98,6 +169,8 @@ static int key_to_sdl[] = {
 void handleKeyboardEvent(screen_event_t event)
 void handleKeyboardEvent(screen_event_t event)
 {
 {
     int             val;
     int             val;
+    int             cap;
+    char            ascii_text[2];
     SDL_Scancode    scancode;
     SDL_Scancode    scancode;
 
 
     // Get the key value.
     // Get the key value.
@@ -105,6 +178,10 @@ void handleKeyboardEvent(screen_event_t event)
         return;
         return;
     }
     }
 
 
+    if (screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap) < 0) {
+        return;
+    }
+
     // Skip unrecognized keys.
     // Skip unrecognized keys.
     if ((val < 0) || (val >= SDL_arraysize(key_to_sdl))) {
     if ((val < 0) || (val >= SDL_arraysize(key_to_sdl))) {
         return;
         return;
@@ -126,6 +203,17 @@ void handleKeyboardEvent(screen_event_t event)
     // Need to handle more key states (such as key combinations).
     // Need to handle more key states (such as key combinations).
     if (val & KEY_DOWN) {
     if (val & KEY_DOWN) {
         SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, val, scancode, true);
         SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, val, scancode, true);
+
+        // TODO: To simplify, we're ignoring keycodes that aren't just ascii.
+        if ((val < UNICODE_PRIVATE_USE_AREA_FIRST) && ((cap & 0xFF) == cap)) {
+            ascii_text[0] = cap;
+            ascii_text[1] = 0;
+            SDL_SendKeyboardText(ascii_text);
+        } else if ((KEYCODE_PC_KEYS <= val) && (val < KEYCODE_CONSUMER_KEYS)) {
+            ascii_text[0] = val & 0xFF;
+            ascii_text[1] = 0;
+            SDL_SendKeyboardText(ascii_text);
+        }
     } else {
     } else {
         SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, val, scancode, false);
         SDL_SendKeyboardKey(0, SDL_DEFAULT_KEYBOARD_ID, val, scancode, false);
     }
     }

+ 195 - 0
src/video/qnx/SDL_qnxmodes.c

@@ -0,0 +1,195 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 2026 BlackBerry Limited
+
+  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, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+#include "../SDL_sysvideo.h"
+#include "../../events/SDL_keyboard_c.h"
+#include "../../events/SDL_mouse_c.h"
+#include "../../events/SDL_windowevents_c.h"
+#include "SDL_qnx.h"
+
+#include <errno.h>
+
+// All indices not already assigned will be zero'd to SDL_PIXELFORMAT_UNKNOWN.
+static const SDL_PixelFormat _format_map[] = {
+    [SCREEN_FORMAT_RGBA4444] = SDL_PIXELFORMAT_RGBA4444,
+    [SCREEN_FORMAT_RGBA5551] = SDL_PIXELFORMAT_RGBA5551,
+    [SCREEN_FORMAT_RGB565] = SDL_PIXELFORMAT_RGB565,
+    [SCREEN_FORMAT_RGBA8888] = SDL_PIXELFORMAT_RGBA8888,
+    [SCREEN_FORMAT_RGBX8888] = SDL_PIXELFORMAT_RGBX8888,
+    [SCREEN_FORMAT_NV12] = SDL_PIXELFORMAT_NV12,
+    [SCREEN_FORMAT_YV12] = SDL_PIXELFORMAT_YV12,
+    [SCREEN_FORMAT_UYVY] = SDL_PIXELFORMAT_UYVY,
+    [SCREEN_FORMAT_YUY2] = SDL_PIXELFORMAT_YUY2,
+    [SCREEN_FORMAT_YVYU] = SDL_PIXELFORMAT_YVYU,
+    [SCREEN_FORMAT_P010] = SDL_PIXELFORMAT_P010,
+    [SCREEN_FORMAT_BGRA8888] = SDL_PIXELFORMAT_BGRA8888,
+    [SCREEN_FORMAT_BGRX8888] = SDL_PIXELFORMAT_BGRX8888,
+};
+
+SDL_PixelFormat screenToPixelFormat(int screen_format)
+{
+    if ((screen_format < 0) || (screen_format >= SDL_arraysize(_format_map))) {
+        return SDL_PIXELFORMAT_UNKNOWN;
+    }
+
+    return _format_map[screen_format];
+}
+
+bool getDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
+{
+    SDL_DisplayData     *display_data = display->internal;
+    SDL_DisplayMode     display_mode;
+    SDL_DisplayModeData *display_mode_data;
+
+    int index;
+    int display_mode_count;
+
+    screen_display_t      screen_display;
+    screen_display_mode_t *screen_display_modes;
+    int                   screen_format;
+    int                   screen_refresh_rate;
+
+    if (display_data == NULL) {
+        return false;
+    }
+    screen_display = display_data->screen_display;
+
+    /* create SDL display imodes based on display mode info from the display */
+    if (screen_get_display_property_iv(screen_display, SCREEN_PROPERTY_MODE_COUNT, &display_mode_count) < 0) {
+        return false;
+    }
+
+    screen_display_modes = SDL_calloc(display_mode_count, sizeof(screen_display_mode_t));
+    if (screen_display_modes == NULL) {
+        return false;
+    }
+
+    if(screen_get_display_modes(screen_display, display_mode_count, screen_display_modes) < 0) {
+        SDL_free(screen_display_modes);
+        return false;
+    }
+
+    for (index = 0; index < display_mode_count; index++) {
+        display_mode_data = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
+        if (display_mode_data == NULL) {
+            // Not much we can do about the objs we've already created at this point.
+            SDL_free(screen_display_modes);
+            return false;
+        }
+
+        SDL_zero(display_mode);
+        display_mode.w = screen_display_modes[index].width;
+        display_mode.h = screen_display_modes[index].height;
+        display_mode.pixel_density = 1.0;
+        display_mode.internal = display_mode_data;
+
+        if (screen_display_modes[index].flags & SCREEN_DISPLAY_MODE_REFRESH_VALID) {
+            screen_refresh_rate = screen_display_modes[index].refresh;
+        } else {
+            // Fallback
+            screen_refresh_rate = 60;
+        }
+        if (screen_display_modes[index].flags & SCREEN_DISPLAY_MODE_FORMAT_VALID) {
+            screen_format = screen_display_modes[index].format;
+        } else {
+            // Fallback
+            screen_format = SCREEN_FORMAT_RGBX8888;
+        }
+        display_mode.refresh_rate = screen_refresh_rate;
+        display_mode.format = screenToPixelFormat(screen_format);
+        display_mode_data->screen_format = screen_format;
+        display_mode_data->screen_display_mode = screen_display_modes[index];
+
+        // This op can fail if the mode already exists.
+        SDL_AddFullscreenDisplayMode(display, &display_mode);
+    }
+
+    SDL_free(screen_display_modes);
+
+    return true;
+}
+
+#if 0
+// FIXME: This seems to invalidate the screen_display_t, causing issues with the
+// (get|set)_display_property_*() apis. For now, mode switching is emulated
+// instead.
+bool setDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
+{
+    SDL_DisplayData     *display_data = display->internal;
+    SDL_DisplayModeData *display_mode_data = mode->internal;
+
+    if ((display_data == NULL) || (display_mode_data == NULL)) {
+        return false;
+    }
+
+    // TODO: May need to call glInitConfig and screen_create_window_buffers.
+    if (screen_set_display_property_iv(display_data->screen_display,
+        SCREEN_PROPERTY_MODE, (int *)&display_mode_data->screen_display_mode.index) < 0) {
+        return false;
+    }
+
+    return true;
+}
+
+bool getDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
+{
+    SDL_DisplayData *data = display->internal;
+    int             size[2] = { 0, 0 };
+
+    if (data == NULL) {
+        return false;
+    }
+
+    if (screen_get_display_property_iv(data->screen_display, SCREEN_PROPERTY_SIZE, size) < 0) {
+        return false;
+    }
+
+    rect->x = 0;
+    rect->y = 0;
+    rect->w = size[0];
+    rect->h = size[1];
+    return true;
+}
+#else
+bool getDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_Rect *rect)
+{
+    if (display->current_mode == NULL) {
+        return false;
+    }
+
+    rect->x = 0;
+    rect->y = 0;
+
+    // When an emulated, exclusive fullscreen window has focus, treat the mode dimensions as the display bounds.
+    if (display->fullscreen_window &&
+        display->fullscreen_window->fullscreen_exclusive &&
+        display->fullscreen_window->current_fullscreen_mode.w != 0 &&
+        display->fullscreen_window->current_fullscreen_mode.h != 0) {
+        rect->w = display->fullscreen_window->current_fullscreen_mode.w;
+        rect->h = display->fullscreen_window->current_fullscreen_mode.h;
+    } else {
+        rect->w = display->current_mode->w;
+        rect->h = display->current_mode->h;
+    }
+    return true;
+}
+#endif

+ 189 - 0
src/video/qnx/SDL_qnxmouse.c

@@ -0,0 +1,189 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 2026 BlackBerry Limited
+
+  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, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+#include "../SDL_sysvideo.h"
+#include "SDL_qnx.h"
+#include "../../events/SDL_mouse_c.h"
+
+#include <errno.h>
+
+
+static int SDLToScreenCursorShape(SDL_SystemCursor id)
+{
+    // This is reserved by screen, but still not used for anything.
+    int shape = -1;
+
+    switch(id)
+    {
+    case SDL_SYSTEM_CURSOR_DEFAULT:
+    case SDL_SYSTEM_CURSOR_NOT_ALLOWED:
+        shape = SCREEN_CURSOR_SHAPE_ARROW;
+        break;
+    case SDL_SYSTEM_CURSOR_TEXT:
+        shape = SCREEN_CURSOR_SHAPE_IBEAM;
+        break;
+    case SDL_SYSTEM_CURSOR_WAIT:
+        shape = SCREEN_CURSOR_SHAPE_WAIT;
+        break;
+    case SDL_SYSTEM_CURSOR_CROSSHAIR:
+        shape = SCREEN_CURSOR_SHAPE_CROSS;
+        break;
+    case SDL_SYSTEM_CURSOR_NWSE_RESIZE:
+    case SDL_SYSTEM_CURSOR_NESW_RESIZE:
+    case SDL_SYSTEM_CURSOR_EW_RESIZE:
+    case SDL_SYSTEM_CURSOR_NS_RESIZE:
+    case SDL_SYSTEM_CURSOR_MOVE:
+        shape = SCREEN_CURSOR_SHAPE_MOVE;
+        break;
+    case SDL_SYSTEM_CURSOR_POINTER:
+        shape = SCREEN_CURSOR_SHAPE_HAND;
+        break;
+    default:
+        break;
+    }
+    return shape;
+}
+
+static SDL_Cursor *genericCreateCursor(int shape)
+{
+    SDL_Cursor          *cursor;
+    SDL_CursorData      *impl;
+    screen_session_t    session;
+    screen_context_t    *context = getContext();
+
+    cursor = SDL_calloc(1, sizeof(SDL_Cursor));
+    if (cursor) {
+        impl = SDL_calloc(1, sizeof(SDL_CursorData));;
+        if (impl == NULL) {
+            SDL_free(cursor);
+            SDL_OutOfMemory();
+        }
+        impl->realized_shape = shape;
+
+        screen_create_session_type(&session, *context, SCREEN_EVENT_POINTER);
+        screen_set_session_property_iv(session, SCREEN_PROPERTY_CURSOR, &shape);
+
+        impl->session = session;
+        impl->is_visible = true;
+        cursor->internal = (void*)impl;
+    } else {
+        SDL_OutOfMemory();
+    }
+
+    return cursor;
+}
+
+static SDL_Cursor *createCursor(SDL_Surface * surface, int hot_x, int hot_y)
+{
+    return genericCreateCursor(SCREEN_CURSOR_SHAPE_ARROW);
+}
+
+static SDL_Cursor *createSystemCursor(SDL_SystemCursor id)
+{
+    int shape = SDLToScreenCursorShape(id);
+    if (shape < 0) {
+        SDL_assert(0);
+        return NULL;
+    }
+
+    return genericCreateCursor(shape);
+}
+
+static bool showCursor(SDL_Cursor * cursor)
+{
+    SDL_CursorData      *impl;
+    screen_session_t    session;
+    int shape;
+
+    // SDL does not provide information about previous visibility to its
+    // drivers. We need to track that ourselves.
+    if (cursor) {
+        impl = (SDL_CursorData*)cursor->internal;
+        SDL_assert(impl != NULL);
+        if (impl->is_visible) {
+            return true;
+        }
+        session = impl->session;
+        shape = impl->realized_shape;
+        impl->is_visible = true;
+    } else {
+        cursor = SDL_GetCursor();
+        if (cursor == NULL) {
+            return false;
+        }
+        impl = (SDL_CursorData*)cursor->internal;
+        SDL_assert(impl != NULL);
+        if (!impl->is_visible) {
+            return 0;
+        }
+        session = impl->session;
+        shape = SCREEN_CURSOR_SHAPE_NONE;
+        impl->is_visible = false;
+    }
+
+    if (screen_set_session_property_iv(session, SCREEN_PROPERTY_CURSOR, &shape) < 0) {
+        return false;
+    }
+
+    return true;
+}
+
+static void freeCursor(SDL_Cursor * cursor)
+{
+    SDL_CursorData *impl = (SDL_CursorData*)cursor->internal;
+    if (impl != NULL) {
+        screen_destroy_session(impl->session);
+        SDL_free(impl);
+    }
+    SDL_free(cursor);
+}
+
+static bool setRelativeMouseMode(bool enabled)
+{
+    // We're tracking rel-position explicitly, but this is still needed so
+    // SDL_SetRelativeMouseMode() & friends aren't a no-op.
+    //
+    // TODO: It may be possible to achieve this using SCREEN_PROPERTY_DISPLACEMENT instead.
+    return true;
+}
+
+void initMouse(SDL_VideoDevice *_this)
+{
+    SDL_Mouse       *mouse = SDL_GetMouse();
+    SDL_MouseData   *mouse_data;
+
+    mouse_data = (SDL_MouseData *)SDL_calloc(1, sizeof(SDL_MouseData));
+    if (mouse_data == NULL) {
+        return;
+    }
+    SDL_zerop(mouse_data);
+    mouse->internal = mouse_data;
+
+    mouse->CreateCursor = createCursor;
+    mouse->CreateSystemCursor = createSystemCursor;
+    mouse->ShowCursor = showCursor;
+    mouse->FreeCursor = freeCursor;
+
+    mouse->SetRelativeMouseMode = setRelativeMouseMode;
+
+    SDL_SetDefaultCursor(createCursor(NULL, 0, 0));
+}

+ 89 - 0
src/video/qnx/SDL_qnxpointer.c

@@ -0,0 +1,89 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 2026 BlackBerry Limited
+
+  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, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+#include "SDL_qnx.h"
+#include "SDL3/SDL_mouse.h"
+#include "SDL3/SDL_events.h"
+#include "SDL3/SDL_video.h"
+#include "../../events/SDL_mouse_c.h"
+
+
+static Uint8 screenToMouseButton(int x)
+{
+    //Screen only supports 3 mouse buttons.
+    switch(x){
+        case SCREEN_LEFT_MOUSE_BUTTON: // 1 << 0
+        return SDL_BUTTON_LEFT;
+        case SCREEN_RIGHT_MOUSE_BUTTON: //1 << 1
+        return SDL_BUTTON_RIGHT;
+        case SCREEN_MIDDLE_MOUSE_BUTTON: //1 << 2
+        return SDL_BUTTON_MIDDLE;
+    }
+    return 0;
+}
+
+void handlePointerEvent(screen_event_t event)
+{
+    int              buttons = 0;
+    int              mouse_wheel = 0;
+    int              mouse_h_wheel = 0;
+    int              pos[2] = {0,0};
+
+    Uint64           timestamp = SDL_GetTicksNS();
+    SDL_Mouse        *mouse;
+    SDL_MouseData    *mouse_data;
+    SDL_Window       *window;
+
+    screen_get_event_property_iv(event, SCREEN_PROPERTY_BUTTONS, &buttons);
+    screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_WHEEL, &mouse_wheel);
+    screen_get_event_property_iv(event, SCREEN_PROPERTY_MOUSE_HORIZONTAL_WHEEL, &mouse_h_wheel);
+    screen_get_event_property_iv(event, SCREEN_PROPERTY_POSITION, pos);
+
+    mouse = SDL_GetMouse();
+
+    window = mouse->focus;
+    mouse_data = mouse->internal;
+    SDL_assert(mouse_data != NULL);
+
+    if (mouse->relative_mode) {
+        // The mouse is hidden. We don't have control over its actual position
+        // with SCREEN_PROPERTY_POSITION, just the position of the icon.
+        SDL_SendMouseMotion(timestamp, window, SDL_DEFAULT_MOUSE_ID, true, pos[0] - mouse_data->x_prev, pos[1] - mouse_data->y_prev);
+    } else {
+        SDL_SendMouseMotion(timestamp, window, SDL_DEFAULT_MOUSE_ID, false, pos[0], pos[1]);
+    }
+
+    mouse_data->x_prev = pos[0];
+    mouse_data->y_prev = pos[1];
+
+    // Capture button presses
+    for (int i = 0; i < 3; ++i)
+    {
+        Uint8 ret = screenToMouseButton(1 << i);
+        SDL_SendMouseButton(timestamp, window, SDL_DEFAULT_MOUSE_ID, ret, (bool) ((buttons & (1 << i)) == (1 << i)));
+    }
+
+    // Capture mouse wheel
+    // TODO: Verify this. I can at least confirm that this behaves the same
+    //       way as x11.
+    SDL_SendMouseWheel(timestamp, window, 0, (float) mouse_wheel, (float) mouse_h_wheel, SDL_MOUSEWHEEL_NORMAL);
+}

+ 339 - 35
src/video/qnx/SDL_qnxvideo.c

@@ -1,6 +1,6 @@
 /*
 /*
   Simple DirectMedia Layer
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  Copyright (C) 2026 BlackBerry Limited
 
 
   This software is provided 'as-is', without any express or implied
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
   warranty.  In no event will the authors be held liable for any damages
@@ -18,25 +18,54 @@
      misrepresented as being the original software.
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
   3. This notice may not be removed or altered from any source distribution.
 */
 */
-#include "../../SDL_internal.h"
+
+#include "SDL_internal.h"
 #include "../SDL_sysvideo.h"
 #include "../SDL_sysvideo.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../events/SDL_mouse_c.h"
 #include "../../events/SDL_mouse_c.h"
+#include "../../events/SDL_windowevents_c.h"
 #include "SDL_qnx.h"
 #include "SDL_qnx.h"
 
 
+#include <errno.h>
+
 static screen_context_t context;
 static screen_context_t context;
 static screen_event_t   event;
 static screen_event_t   event;
+static bool video_initialized = false;
+
+screen_context_t * getContext()
+{
+    return &context;
+}
+
+screen_event_t * getEvent()
+{
+    return &event;
+}
 
 
 /**
 /**
  * Initializes the QNX video plugin.
  * Initializes the QNX video plugin.
  * Creates the Screen context and event handles used for all window operations
  * Creates the Screen context and event handles used for all window operations
  * by the plugin.
  * by the plugin.
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 static bool videoInit(SDL_VideoDevice *_this)
 static bool videoInit(SDL_VideoDevice *_this)
 {
 {
-    SDL_VideoDisplay display;
+    SDL_VideoDisplay     display;
+    SDL_DisplayData      *display_data;
+    SDL_DisplayMode      display_mode;
+    SDL_DisplayModeData  *display_mode_data;
+
+    int size[2];
+    int index;
+    int display_count;
+    int active;
+
+    screen_display_t *screen_display;
+
+    if (video_initialized) {
+        return true;
+    }
 
 
     if (screen_create_context(&context, 0) < 0) {
     if (screen_create_context(&context, 0) < 0) {
         return false;
         return false;
@@ -46,21 +75,101 @@ static bool videoInit(SDL_VideoDevice *_this)
         return false;
         return false;
     }
     }
 
 
-    SDL_zero(display);
+    /* start with no displays and increment as attached displays are found */
+    if (screen_get_context_property_iv(context, SCREEN_PROPERTY_DISPLAY_COUNT, &display_count) < 0) {
+        return false;
+    }
+
+    screen_display = SDL_calloc(display_count, sizeof(screen_display_t));
+    if (screen_display == NULL) {
+        return false;
+    }
 
 
-    if (SDL_AddVideoDisplay(&display, false) == 0) {
+    if (screen_get_context_property_pv(context, SCREEN_PROPERTY_DISPLAYS, (void **)screen_display) < 0) {
         return false;
         return false;
     }
     }
 
 
+    /* create SDL displays based on display info from the screen API */
+    for (index = 0; index < display_count; index++) {
+        active = 0;
+        if (screen_get_display_property_iv(screen_display[index], SCREEN_PROPERTY_ATTACHED, &active) < 0) {
+            SDL_free(screen_display);
+            return false;
+        }
+
+        if (active) {
+            display_data = (SDL_DisplayData *)SDL_calloc(1, sizeof(SDL_DisplayData));
+            if (display_data == NULL) {
+                SDL_free(screen_display);
+                return false;
+            }
+            SDL_zerop(display_data);
+
+            if (screen_get_display_property_iv(screen_display[index], SCREEN_PROPERTY_SIZE, size) < 0) {
+                SDL_free(screen_display);
+                SDL_free(display_data);
+                return false;
+            }
+
+            display_mode_data = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData));
+            if (display_mode_data == NULL) {
+                SDL_free(screen_display);
+                SDL_free(display_data);
+                return false;
+            }
+            SDL_zerop(display_mode_data);
+
+            SDL_zero(display);
+            SDL_zero(display_mode);
+
+            display_data->screen_display = screen_display[index];
+            display.internal = (void *)display_data;
+
+            display_mode.w = size[0];
+            display_mode.h = size[1];
+            display_mode.refresh_rate = 60;
+            display_mode.pixel_density = 1.0;
+            display_mode.internal = display_mode_data;
+            // This is assigned later when the window is created. For now, use a
+            // safe guess.
+            display_mode.format = SDL_PIXELFORMAT_RGBX8888;
+            display_mode_data->screen_format = SCREEN_FORMAT_RGBX8888;
+            // Be able to revert to the default display mode despite not having
+            // the actual object to refer to.
+            display_mode_data->screen_display_mode.index = SCREEN_DISPLAY_MODE_PREFERRED_INDEX;
+
+            // Added to current_mode when the display is added.
+            display.desktop_mode = display_mode;
+
+            if (!SDL_AddVideoDisplay(&display, false)) {
+                SDL_free(screen_display);
+                SDL_free(display_mode_data);
+                SDL_free(display_data);
+                return false;
+            }
+        }
+    }
+
+    initMouse(_this);
+
     // Assume we have a mouse and keyboard
     // Assume we have a mouse and keyboard
     SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL);
     SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL);
     SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL);
     SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL);
 
 
+    video_initialized = true;
+
+    SDL_free(screen_display);
+
     return true;
     return true;
 }
 }
 
 
 static void videoQuit(SDL_VideoDevice *_this)
 static void videoQuit(SDL_VideoDevice *_this)
 {
 {
+    if (video_initialized) {
+        screen_destroy_event(event);
+        screen_destroy_context(context);
+        video_initialized = false;
+    }
 }
 }
 
 
 /**
 /**
@@ -68,20 +177,27 @@ static void videoQuit(SDL_VideoDevice *_this)
  * window.
  * window.
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
  * @param   window  SDL window to initialize
  * @param   window  SDL window to initialize
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
-static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window)
+static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props)
 {
 {
-    window_impl_t   *impl;
+    SDL_WindowData       *impl;
+    SDL_VideoDisplay     *display = NULL;
+    SDL_DisplayData      *display_data = NULL;
+    SDL_DisplayModeData  *display_mode_data = NULL;
+
     int             size[2];
     int             size[2];
+    int             position[2];
     int             numbufs;
     int             numbufs;
     int             format;
     int             format;
     int             usage;
     int             usage;
+    int             has_focus_i;
 
 
     impl = SDL_calloc(1, sizeof(*impl));
     impl = SDL_calloc(1, sizeof(*impl));
     if (!impl) {
     if (!impl) {
         return false;
         return false;
     }
     }
+    window->internal = impl;
 
 
     // Create a native window.
     // Create a native window.
     if (screen_create_window(&impl->window, context) < 0) {
     if (screen_create_window(&impl->window, context) < 0) {
@@ -91,6 +207,8 @@ static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window)
     // Set the native window's size to match the SDL window.
     // Set the native window's size to match the SDL window.
     size[0] = window->w;
     size[0] = window->w;
     size[1] = window->h;
     size[1] = window->h;
+    position[0] = window->x;
+    position[1] = window->y;
 
 
     if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE,
     if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE,
                                       size) < 0) {
                                       size) < 0) {
@@ -102,23 +220,46 @@ static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window)
         goto fail;
         goto fail;
     }
     }
 
 
+    if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_POSITION,
+                                      position) < 0) {
+        goto fail;
+    }
+
+    display = SDL_GetVideoDisplayForWindow(window);
+    SDL_assert(display != NULL);
+    SDL_assert(display->desktop_mode.internal != NULL);
+    display_mode_data = display->desktop_mode.internal;
+
+    if (screen_get_window_property_iv(impl->window, SCREEN_PROPERTY_FORMAT,
+                                      &format) < 0) {
+        format = display_mode_data->screen_format;
+    }
+
     // Create window buffer(s).
     // Create window buffer(s).
     if (window->flags & SDL_WINDOW_OPENGL) {
     if (window->flags & SDL_WINDOW_OPENGL) {
-        if (glGetConfig(&impl->conf, &format) < 0) {
+        if (!glInitConfig(impl, &format)) {
             goto fail;
             goto fail;
         }
         }
         numbufs = 2;
         numbufs = 2;
 
 
-        usage = SCREEN_USAGE_OPENGL_ES2;
+        usage = SCREEN_USAGE_OPENGL_ES2 | SCREEN_USAGE_OPENGL_ES3;
         if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_USAGE,
         if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_USAGE,
                                           &usage) < 0) {
                                           &usage) < 0) {
-            return false;
+            goto fail;
         }
         }
     } else {
     } else {
-        format = SCREEN_FORMAT_RGBX8888;
         numbufs = 1;
         numbufs = 1;
     }
     }
 
 
+    // We now know what the pixel format is, so we need to provide it to the
+    // right SDL APIs.
+    display->desktop_mode.format = screenToPixelFormat(format);
+    display_mode_data->screen_format = format;
+
+    display_data = display->internal;
+    // Initialized in videoInit()
+    SDL_assert(display_data != NULL);
+
     // Set pixel format.
     // Set pixel format.
     if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_FORMAT,
     if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_FORMAT,
                                       &format) < 0) {
                                       &format) < 0) {
@@ -126,19 +267,32 @@ static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window)
     }
     }
 
 
     // Create buffer(s).
     // Create buffer(s).
-    if (screen_create_window_buffers(impl->window, numbufs) < 0) {
+    if (screen_create_window_buffers(impl->window, numbufs>0?numbufs:1) < 0) {
         goto fail;
         goto fail;
     }
     }
 
 
-    window->internal = impl;
+    // Get initial focus state. Fallback to true.
+    if(screen_get_window_property_iv(impl->window, SCREEN_PROPERTY_FOCUS, &has_focus_i) < 0){
+        impl->has_focus = true;
+    } else {
+        impl->has_focus = (bool)has_focus_i;
+    }
+
+    SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_QNX_WINDOW_POINTER, impl->window);
+
     return true;
     return true;
 
 
 fail:
 fail:
     if (impl->window) {
     if (impl->window) {
         screen_destroy_window(impl->window);
         screen_destroy_window(impl->window);
     }
     }
+    if (display_data) {
+        SDL_free(display_data);
+        display->internal = NULL;
+    }
 
 
     SDL_free(impl);
     SDL_free(impl);
+    window->internal = NULL;
     return false;
     return false;
 }
 }
 
 
@@ -147,35 +301,43 @@ fail:
  * that the buffer is actually created in createWindow().
  * that the buffer is actually created in createWindow().
  * @param       SDL_VideoDevice *_this
  * @param       SDL_VideoDevice *_this
  * @param       window  SDL window to get the buffer for
  * @param       window  SDL window to get the buffer for
- * @param[out]  pixles  Holds a pointer to the window's buffer
+ * @param[out]  pixels  Holds a pointer to the window's buffer
  * @param[out]  format  Holds the pixel format for the buffer
  * @param[out]  format  Holds the pixel format for the buffer
  * @param[out]  pitch   Holds the number of bytes per line
  * @param[out]  pitch   Holds the number of bytes per line
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 static bool createWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window * window, SDL_PixelFormat * format,
 static bool createWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window * window, SDL_PixelFormat * format,
                         void ** pixels, int *pitch)
                         void ** pixels, int *pitch)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
-    screen_buffer_t buffer;
+    int              buffer_count;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
+    screen_buffer_t  *buffer;
+    SDL_VideoDisplay *display = SDL_GetVideoDisplayForWindow(window);
+
+    if (screen_get_window_property_iv(impl->window, SCREEN_PROPERTY_BUFFER_COUNT,
+                                      &buffer_count) < 0) {
+        return false;
+    }
+    buffer = SDL_calloc(buffer_count, sizeof(screen_buffer_t));
 
 
     // Get a pointer to the buffer's memory.
     // Get a pointer to the buffer's memory.
     if (screen_get_window_property_pv(impl->window, SCREEN_PROPERTY_BUFFERS,
     if (screen_get_window_property_pv(impl->window, SCREEN_PROPERTY_BUFFERS,
-                                      (void **)&buffer) < 0) {
+                                      (void **)buffer) < 0) {
         return false;
         return false;
     }
     }
 
 
-    if (screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER,
+    if (screen_get_buffer_property_pv(*buffer, SCREEN_PROPERTY_POINTER,
                                       pixels) < 0) {
                                       pixels) < 0) {
         return false;
         return false;
     }
     }
 
 
     // Set format and pitch.
     // Set format and pitch.
-    if (screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE,
+    if (screen_get_buffer_property_iv(*buffer, SCREEN_PROPERTY_STRIDE,
                                       pitch) < 0) {
                                       pitch) < 0) {
         return false;
         return false;
     }
     }
 
 
-    *format = SDL_PIXELFORMAT_XRGB8888;
+    *format = display->desktop_mode.format;
     return true;
     return true;
 }
 }
 
 
@@ -185,31 +347,150 @@ static bool createWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window * window,
  * @param   window      The window to update
  * @param   window      The window to update
  * @param   rects       An array of reectangular areas to update
  * @param   rects       An array of reectangular areas to update
  * @param   numrects    Rect array length
  * @param   numrects    Rect array length
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
  */
 static bool updateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects,
 static bool updateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects,
                         int numrects)
                         int numrects)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
-    screen_buffer_t buffer;
+    int buffer_count, *rects_int;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
+    screen_buffer_t *buffer;
+
+    if (screen_get_window_property_iv(impl->window, SCREEN_PROPERTY_BUFFER_COUNT,
+                                      &buffer_count) < 0) {
+        return false;
+    }
+    buffer = SDL_calloc(buffer_count, sizeof(screen_buffer_t));
 
 
     if (screen_get_window_property_pv(impl->window, SCREEN_PROPERTY_BUFFERS,
     if (screen_get_window_property_pv(impl->window, SCREEN_PROPERTY_BUFFERS,
-                                      (void **)&buffer) < 0) {
+                                      (void **)buffer) < 0) {
         return false;
         return false;
     }
     }
 
 
-    screen_post_window(impl->window, buffer, numrects, (int *)rects, 0);
-    screen_flush_context(context, 0);
+    if(numrects>0){
+        rects_int = SDL_calloc(4*numrects, sizeof(int));
+
+        for(int i = 0; i < numrects; i++){
+            rects_int[4*i]   = rects[i].x;
+            rects_int[4*i+1] = rects[i].y;
+            rects_int[4*i+2] = rects[i].w;
+            rects_int[4*i+3] = rects[i].h;
+        }
+
+        if(screen_post_window(impl->window, buffer[0], numrects, rects_int, 0)) {
+            return false;
+        }
+        if(screen_flush_context(context, 0)) {
+            return false;
+        }
+    }
     return true;
     return true;
 }
 }
 
 
+static SDL_FullscreenResult setWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen)
+{
+    SDL_WindowData *window_data = window->internal;
+    SDL_DisplayData *display_data = display->internal;
+    int size[2] = { 0, 0 };
+    int position[2] = { 0, 0 };
+
+    if (!(window->flags & SDL_WINDOW_FULLSCREEN) && !fullscreen) {
+        return SDL_FULLSCREEN_SUCCEEDED;
+    }
+
+    if (fullscreen) {
+        SDL_Rect bounds;
+
+        if (!getDisplayBounds(_this, display, &bounds)) {
+            return SDL_FULLSCREEN_FAILED;
+        }
+        position[0] = bounds.x;
+        position[1] = bounds.y;
+        size[0] = bounds.w;
+        size[1] = bounds.h;
+    } else {
+        position[0] = window->x;
+        position[1] = window->y;
+        size[0] = window->w;
+        size[1] = window->h;
+    }
+
+    if (screen_set_window_property_iv(window_data->window, SCREEN_PROPERTY_SIZE,
+                                      size) < 0) {
+        return SDL_FULLSCREEN_FAILED;
+    }
+
+    if (screen_set_window_property_iv(window_data->window, SCREEN_PROPERTY_SOURCE_SIZE,
+                                      size) < 0) {
+        return SDL_FULLSCREEN_FAILED;
+    }
+
+    if (screen_set_window_property_iv(window_data->window, SCREEN_PROPERTY_POSITION,
+                                      position) < 0) {
+        return SDL_FULLSCREEN_FAILED;
+    }
+
+    SDL_SendWindowEvent(window, fullscreen ? SDL_EVENT_WINDOW_ENTER_FULLSCREEN : SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0);
+
+    return SDL_FULLSCREEN_SUCCEEDED;
+}
+
+static SDL_DisplayID getDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window)
+{
+    // We need this, otherwise SDL will fallback to the primary display, meaning
+    // any data we store about the display will be inconveniently overwritten.
+    SDL_WindowData  *impl = (SDL_WindowData *)window->internal;
+    SDL_DisplayData *display_data;
+
+    screen_display_t screen_display;
+
+    if (impl == NULL) {
+        return 0;
+    }
+
+    if (screen_get_window_property_pv(impl->window, SCREEN_PROPERTY_DISPLAY,
+                                      (void **)&screen_display) < 0) {
+        return 0;
+    }
+
+    for (int i = 0; i < _this->num_displays; i++) {
+        display_data = _this->displays[i]->internal;
+        if (display_data && (display_data->screen_display == screen_display)) {
+            return _this->displays[i]->id;
+        }
+    }
+
+    return 0;
+}
+
 /**
 /**
  * Runs the main event loop.
  * Runs the main event loop.
  * @param   SDL_VideoDevice *_this
  * @param   SDL_VideoDevice *_this
  */
  */
 static void pumpEvents(SDL_VideoDevice *_this)
 static void pumpEvents(SDL_VideoDevice *_this)
 {
 {
+    SDL_Window      *window;
+    SDL_WindowData   *impl;
     int             type;
     int             type;
+    int             has_focus_i;
+    bool            has_focus;
+
+    // Let apps know the state of focus.
+    for (window = _this->windows; window; window = window->next) {
+        impl = (SDL_WindowData *)window->internal;
+        if (screen_get_window_property_iv(impl->window, SCREEN_PROPERTY_FOCUS, &has_focus_i) < 0){
+            continue;
+        }
+        has_focus = (bool)has_focus_i;
+
+        if (impl->has_focus != has_focus) {
+            SDL_SendWindowEvent(window, (has_focus ? SDL_EVENT_WINDOW_FOCUS_GAINED : SDL_EVENT_WINDOW_FOCUS_LOST), 0, 0);
+            SDL_SendWindowEvent(window, (has_focus ? SDL_EVENT_WINDOW_MOUSE_ENTER : SDL_EVENT_WINDOW_MOUSE_LEAVE), 0, 0);
+            // Update the SDL mouse to track the window it's focused on.
+            SDL_SetMouseFocus(window);
+            SDL_SetKeyboardFocus(window);
+        }
+    }
 
 
     for (;;) {
     for (;;) {
         if (screen_get_event(context, event, 0) < 0) {
         if (screen_get_event(context, event, 0) < 0) {
@@ -230,6 +511,10 @@ static void pumpEvents(SDL_VideoDevice *_this)
             handleKeyboardEvent(event);
             handleKeyboardEvent(event);
             break;
             break;
 
 
+        case SCREEN_EVENT_POINTER:
+            handlePointerEvent(event);
+            break;
+
         default:
         default:
             break;
             break;
         }
         }
@@ -243,14 +528,25 @@ static void pumpEvents(SDL_VideoDevice *_this)
  */
  */
 static void setWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
 static void setWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
     int             size[2];
     int             size[2];
 
 
     size[0] = window->pending.w;
     size[0] = window->pending.w;
     size[1] = window->pending.h;
     size[1] = window->pending.h;
 
 
-    screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE, size);
-    screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE, size);
+    if (!(window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED))) {
+        if (screen_destroy_window_buffers(impl->window) < 0) {
+            return;
+        }
+        impl->resize = 1;
+
+        screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE, size);
+        screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SOURCE_SIZE, size);
+
+        screen_create_window_buffers(impl->window, (window->flags & SDL_WINDOW_OPENGL) ? 2 : 1);
+    } else {
+        window->last_size_pending = false;
+    }
 }
 }
 
 
 /**
 /**
@@ -260,7 +556,7 @@ static void setWindowSize(SDL_VideoDevice *_this, SDL_Window *window)
  */
  */
 static void showWindow(SDL_VideoDevice *_this, SDL_Window *window)
 static void showWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
     const int       visible = 1;
     const int       visible = 1;
 
 
     screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_VISIBLE,
     screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_VISIBLE,
@@ -274,7 +570,7 @@ static void showWindow(SDL_VideoDevice *_this, SDL_Window *window)
  */
  */
 static void hideWindow(SDL_VideoDevice *_this, SDL_Window *window)
 static void hideWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
     const int       visible = 0;
     const int       visible = 0;
 
 
     screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_VISIBLE,
     screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_VISIBLE,
@@ -288,7 +584,7 @@ static void hideWindow(SDL_VideoDevice *_this, SDL_Window *window)
  */
  */
 static void destroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
 static void destroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
 
 
     if (impl) {
     if (impl) {
         screen_destroy_window(impl->window);
         screen_destroy_window(impl->window);
@@ -325,8 +621,15 @@ static SDL_VideoDevice *createDevice(void)
     device->CreateWindowFramebuffer = createWindowFramebuffer;
     device->CreateWindowFramebuffer = createWindowFramebuffer;
     device->UpdateWindowFramebuffer = updateWindowFramebuffer;
     device->UpdateWindowFramebuffer = updateWindowFramebuffer;
     device->SetWindowSize = setWindowSize;
     device->SetWindowSize = setWindowSize;
+    device->SetWindowFullscreen = setWindowFullscreen;
     device->ShowWindow = showWindow;
     device->ShowWindow = showWindow;
     device->HideWindow = hideWindow;
     device->HideWindow = hideWindow;
+    device->GetDisplayForWindow = getDisplayForWindow;
+    device->GetDisplayBounds = getDisplayBounds;
+    device->GetDisplayModes = getDisplayModes;
+#if 0
+    device->SetDisplayMode = setDisplayMode;
+#endif
     device->PumpEvents = pumpEvents;
     device->PumpEvents = pumpEvents;
     device->DestroyWindow = destroyWindow;
     device->DestroyWindow = destroyWindow;
 
 
@@ -340,6 +643,7 @@ static SDL_VideoDevice *createDevice(void)
     device->GL_UnloadLibrary = glUnloadLibrary;
     device->GL_UnloadLibrary = glUnloadLibrary;
 
 
     device->free = deleteDevice;
     device->free = deleteDevice;
+
     return device;
     return device;
 }
 }