From f4a541682a3ac70302636db235995b39741a85e3 Mon Sep 17 00:00:00 2001 From: eleir9268 Date: Fri, 30 Jan 2026 18:32:43 -0500 Subject: [PATCH] Up-to-date QNX support (#14806) Co-authored-by: Roberto Speranza Co-authored-by: Darcy Phipps Co-authored-by: Pierce McKinnon --- CMakeLists.txt | 2 +- docs/README-qnx.md | 44 ++++ include/SDL3/SDL_platform_defines.h | 2 +- include/SDL3/SDL_video.h | 9 + src/thread/pthread/SDL_systhread.c | 5 + src/video/SDL_egl.c | 7 + src/video/SDL_video.c | 2 +- src/video/qnx/SDL_qnx.h | 49 +++- src/video/qnx/SDL_qnxgl.c | 203 ++++++++++++--- src/video/qnx/SDL_qnxkeyboard.c | 92 ++++++- src/video/qnx/SDL_qnxmodes.c | 195 +++++++++++++++ src/video/qnx/SDL_qnxmouse.c | 189 ++++++++++++++ src/video/qnx/SDL_qnxpointer.c | 89 +++++++ src/video/qnx/SDL_qnxvideo.c | 376 +++++++++++++++++++++++++--- 14 files changed, 1178 insertions(+), 86 deletions(-) create mode 100644 docs/README-qnx.md create mode 100644 src/video/qnx/SDL_qnxmodes.c create mode 100644 src/video/qnx/SDL_qnxmouse.c create mode 100644 src/video/qnx/SDL_qnxpointer.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1211d81acc..2221a3b290 100644 --- a/CMakeLists.txt +++ b/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" diff --git a/docs/README-qnx.md b/docs/README-qnx.md new file mode 100644 index 0000000000..bf08e2a784 --- /dev/null +++ b/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. diff --git a/include/SDL3/SDL_platform_defines.h b/include/SDL3/SDL_platform_defines.h index 79631491de..526bc0eaa1 100644 --- a/include/SDL3/SDL_platform_defines.h +++ b/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. diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 2f062959fe..da6413ed55 100644 --- a/include/SDL3/SDL_video.h +++ b/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" diff --git a/src/thread/pthread/SDL_systhread.c b/src/thread/pthread/SDL_systhread.c index 5ad354f8f2..37001982c7 100644 --- a/src/thread/pthread/SDL_systhread.c +++ b/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); diff --git a/src/video/SDL_egl.c b/src/video/SDL_egl.c index 1d35ef0dd4..6a45f1c2be 100644 --- a/src/video/SDL_egl.c +++ b/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" diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 287e0b482c..2c63af15be 100644 --- a/src/video/SDL_video.c +++ b/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; } diff --git a/src/video/qnx/SDL_qnx.h b/src/video/qnx/SDL_qnx.h index f8a50cfa73..b426d3a0f1 100644 --- a/src/video/qnx/SDL_qnx.h +++ b/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 #include -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 diff --git a/src/video/qnx/SDL_qnxgl.c b/src/video/qnx/SDL_qnxgl.c index 639e556900..4fe07aa660 100644 --- a/src/video/qnx/SDL_qnxgl.c +++ b/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; } diff --git a/src/video/qnx/SDL_qnxkeyboard.c b/src/video/qnx/SDL_qnxkeyboard.c index b224caa25b..44f953b45f 100644 --- a/src/video/qnx/SDL_qnxkeyboard.c +++ b/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); } diff --git a/src/video/qnx/SDL_qnxmodes.c b/src/video/qnx/SDL_qnxmodes.c new file mode 100644 index 0000000000..cd7702aa78 --- /dev/null +++ b/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 + +// 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 diff --git a/src/video/qnx/SDL_qnxmouse.c b/src/video/qnx/SDL_qnxmouse.c new file mode 100644 index 0000000000..849c62d595 --- /dev/null +++ b/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 + + +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)); +} diff --git a/src/video/qnx/SDL_qnxpointer.c b/src/video/qnx/SDL_qnxpointer.c new file mode 100644 index 0000000000..e5d050fa6e --- /dev/null +++ b/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); +} diff --git a/src/video/qnx/SDL_qnxvideo.c b/src/video/qnx/SDL_qnxvideo.c index 4b3c07fdbc..1c7aff4737 100644 --- a/src/video/qnx/SDL_qnxvideo.c +++ b/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 + 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); - - if (SDL_AddVideoDisplay(&display, false) == 0) { + /* 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 (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; }