Bladeren bron

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 maand geleden
bovenliggende
commit
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"
         )
         set(HAVE_SDL_AUDIO TRUE)
-    elseif(QNX)
+    elseif(QNX AND (CMAKE_SYSTEM_VERSION VERSION_LESS "8.0.0"))
         set(SDL_AUDIO_DRIVER_QNX 1)
         sdl_glob_sources(
           "${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
 #endif
 
-#ifdef __QNXNTO__
+#if defined(__QNXNTO__) || defined(__QNX__)
 
 /**
  * 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
  *   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:
  *
  * - `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_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_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_WINDOW_POINTER                      "SDL.window.vivante.window"
 #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) {
         sched.sched_priority = sched_get_priority_min(policy);
     } 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);
+#endif
     } else {
         int min_priority = sched_get_priority_min(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     "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
 // Desktop Linux/Unix-like
 #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)
 {
-#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) {
         return SDL_WINDOW_OPENGL;
     }

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

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  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
@@ -26,23 +26,62 @@
 #include <screen/screen.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;
     EGLSurface      surface;
     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 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 SDL_FunctionPointer glGetProcAddress(SDL_VideoDevice *_this, const char *proc);
 extern SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window);
 extern bool glSetSwapInterval(SDL_VideoDevice *_this, int interval);
 extern bool glSwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
 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 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

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

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  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
@@ -19,11 +19,119 @@
   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"
 
 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
  * configuration.
@@ -42,7 +150,7 @@ static int chooseFormat(EGLConfig egl_conf)
         case 32:
             return SCREEN_FORMAT_RGBX8888;
         case 24:
-            return SDL_PIXELFORMAT_RGB24;
+            return SCREEN_FORMAT_RGB888;
         case 16:
             switch (alpha_bit_depth) {
                 case 4:
@@ -59,20 +167,18 @@ static int chooseFormat(EGLConfig egl_conf)
 
 /**
  * Enumerates the supported EGL configurations and chooses a suitable one.
- * @param[out]  pconf   The chosen configuration
  * @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_configs;
     EGLint egl_num_configs;
-    EGLint val;
     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);
     if (rc != EGL_TRUE) {
         return false;
@@ -96,30 +202,12 @@ bool glGetConfig(EGLConfig *pconf, int *pformat)
         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);
-    *pconf = egl_conf;
-    *pformat = chooseFormat(egl_conf);
+    impl->conf = egl_conf;
 
     return true;
 }
@@ -128,7 +216,7 @@ bool glGetConfig(EGLConfig *pconf, int *pformat)
  * Initializes the EGL library.
  * @param   SDL_VideoDevice *_this
  * @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)
 {
@@ -165,7 +253,7 @@ SDL_FunctionPointer glGetProcAddress(SDL_VideoDevice *_this, const char *proc)
  */
 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;
     EGLSurface      surface;
 
@@ -201,6 +289,10 @@ SDL_GLContext glCreateContext(SDL_VideoDevice *_this, SDL_Window *window)
     eglMakeCurrent(egl_disp, surface, surface, context);
 
     impl->surface = surface;
+    impl->context = context;
+
+    SDL_SetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_QNX_SURFACE_POINTER, impl->surface);
+
     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.
  * @param   SDL_VideoDevice *_this
  * @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)
 {
@@ -223,13 +315,44 @@ bool glSetSwapInterval(SDL_VideoDevice *_this, int interval)
  * Swaps the EGL buffers associated with the given window
  * @param   SDL_VideoDevice *_this
  * @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)
 {
     // !!! 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   window  SDL window associated with the context (maybe NULL)
  * @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)
 {
-    window_impl_t   *impl;
+    SDL_WindowData   *impl;
     EGLSurface      surface = NULL;
 
     if (window) {
-        impl = (window_impl_t *)window->internal;
+        impl = (SDL_WindowData *)window->internal;
         surface = impl->surface;
     }
 

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

@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 2017 BlackBerry Limited
+  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
@@ -19,7 +19,7 @@
   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 "SDL3/SDL_scancode.h"
 #include "SDL3/SDL_events.h"
@@ -84,10 +84,81 @@ static int key_to_sdl[] = {
     [KEYCODE_LEFT] = SDL_SCANCODE_LEFT,
     [KEYCODE_PG_UP] = SDL_SCANCODE_PAGEUP,
     [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_RETURN] = SDL_SCANCODE_RETURN,
     [KEYCODE_TAB] = SDL_SCANCODE_TAB,
     [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)
 {
     int             val;
+    int             cap;
+    char            ascii_text[2];
     SDL_Scancode    scancode;
 
     // Get the key value.
@@ -105,6 +178,10 @@ void handleKeyboardEvent(screen_event_t event)
         return;
     }
 
+    if (screen_get_event_property_iv(event, SCREEN_PROPERTY_KEY_CAP, &cap) < 0) {
+        return;
+    }
+
     // Skip unrecognized keys.
     if ((val < 0) || (val >= SDL_arraysize(key_to_sdl))) {
         return;
@@ -126,6 +203,17 @@ void handleKeyboardEvent(screen_event_t event)
     // Need to handle more key states (such as key combinations).
     if (val & KEY_DOWN) {
         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 {
         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
-  Copyright (C) 2017 BlackBerry Limited
+  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
@@ -18,25 +18,54 @@
      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_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>
+
 static screen_context_t context;
 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.
  * Creates the Screen context and event handles used for all window operations
  * by the plugin.
  * @param   SDL_VideoDevice *_this
- * @return  0 if successful, -1 on error
+ * @return  true if successful, false on error
  */
 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) {
         return false;
@@ -46,21 +75,101 @@ static bool videoInit(SDL_VideoDevice *_this)
         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;
     }
 
+    /* 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
     SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL);
     SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL);
 
+    video_initialized = true;
+
+    SDL_free(screen_display);
+
     return true;
 }
 
 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.
  * @param   SDL_VideoDevice *_this
  * @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             position[2];
     int             numbufs;
     int             format;
     int             usage;
+    int             has_focus_i;
 
     impl = SDL_calloc(1, sizeof(*impl));
     if (!impl) {
         return false;
     }
+    window->internal = impl;
 
     // Create a native window.
     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.
     size[0] = window->w;
     size[1] = window->h;
+    position[0] = window->x;
+    position[1] = window->y;
 
     if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_SIZE,
                                       size) < 0) {
@@ -102,23 +220,46 @@ static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window)
         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).
     if (window->flags & SDL_WINDOW_OPENGL) {
-        if (glGetConfig(&impl->conf, &format) < 0) {
+        if (!glInitConfig(impl, &format)) {
             goto fail;
         }
         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,
                                           &usage) < 0) {
-            return false;
+            goto fail;
         }
     } else {
-        format = SCREEN_FORMAT_RGBX8888;
         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.
     if (screen_set_window_property_iv(impl->window, SCREEN_PROPERTY_FORMAT,
                                       &format) < 0) {
@@ -126,19 +267,32 @@ static bool createWindow(SDL_VideoDevice *_this, SDL_Window *window)
     }
 
     // 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;
     }
 
-    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;
 
 fail:
     if (impl->window) {
         screen_destroy_window(impl->window);
     }
+    if (display_data) {
+        SDL_free(display_data);
+        display->internal = NULL;
+    }
 
     SDL_free(impl);
+    window->internal = NULL;
     return false;
 }
 
@@ -147,35 +301,43 @@ fail:
  * that the buffer is actually created in createWindow().
  * @param       SDL_VideoDevice *_this
  * @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]  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,
                         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.
     if (screen_get_window_property_pv(impl->window, SCREEN_PROPERTY_BUFFERS,
-                                      (void **)&buffer) < 0) {
+                                      (void **)buffer) < 0) {
         return false;
     }
 
-    if (screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER,
+    if (screen_get_buffer_property_pv(*buffer, SCREEN_PROPERTY_POINTER,
                                       pixels) < 0) {
         return false;
     }
 
     // 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) {
         return false;
     }
 
-    *format = SDL_PIXELFORMAT_XRGB8888;
+    *format = display->desktop_mode.format;
     return true;
 }
 
@@ -185,31 +347,150 @@ static bool createWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window * window,
  * @param   window      The window to update
  * @param   rects       An array of reectangular areas to update
  * @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,
                         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,
-                                      (void **)&buffer) < 0) {
+                                      (void **)buffer) < 0) {
         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;
 }
 
+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.
  * @param   SDL_VideoDevice *_this
  */
 static void pumpEvents(SDL_VideoDevice *_this)
 {
+    SDL_Window      *window;
+    SDL_WindowData   *impl;
     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 (;;) {
         if (screen_get_event(context, event, 0) < 0) {
@@ -230,6 +511,10 @@ static void pumpEvents(SDL_VideoDevice *_this)
             handleKeyboardEvent(event);
             break;
 
+        case SCREEN_EVENT_POINTER:
+            handlePointerEvent(event);
+            break;
+
         default:
             break;
         }
@@ -243,14 +528,25 @@ static void pumpEvents(SDL_VideoDevice *_this)
  */
 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];
 
     size[0] = window->pending.w;
     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)
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
     const int       visible = 1;
 
     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)
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
     const int       visible = 0;
 
     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)
 {
-    window_impl_t   *impl = (window_impl_t *)window->internal;
+    SDL_WindowData   *impl = (SDL_WindowData *)window->internal;
 
     if (impl) {
         screen_destroy_window(impl->window);
@@ -325,8 +621,15 @@ static SDL_VideoDevice *createDevice(void)
     device->CreateWindowFramebuffer = createWindowFramebuffer;
     device->UpdateWindowFramebuffer = updateWindowFramebuffer;
     device->SetWindowSize = setWindowSize;
+    device->SetWindowFullscreen = setWindowFullscreen;
     device->ShowWindow = showWindow;
     device->HideWindow = hideWindow;
+    device->GetDisplayForWindow = getDisplayForWindow;
+    device->GetDisplayBounds = getDisplayBounds;
+    device->GetDisplayModes = getDisplayModes;
+#if 0
+    device->SetDisplayMode = setDisplayMode;
+#endif
     device->PumpEvents = pumpEvents;
     device->DestroyWindow = destroyWindow;
 
@@ -340,6 +643,7 @@ static SDL_VideoDevice *createDevice(void)
     device->GL_UnloadLibrary = glUnloadLibrary;
 
     device->free = deleteDevice;
+
     return device;
 }