From a5077a7e22dcede3d804b95341a589a8060444df Mon Sep 17 00:00:00 2001 From: nichcode Date: Sat, 1 Nov 2025 11:10:20 +0000 Subject: [PATCH 01/51] more feature bits --- include/pal/pal_core.h | 1 + include/pal/pal_video.h | 38 +++++++ src/pal_core.c | 4 +- src/video/pal_video_linux.c | 159 ++++++++---------------------- src/video/pal_video_win32.c | 10 ++ tests/char_event_test.c | 2 +- tests/cursor_test.c | 2 +- tests/icon_test.c | 2 +- tests/input_window_test.c | 2 +- tests/opengl_context_test.c | 2 +- tests/opengl_multi_context_test.c | 2 +- tests/system_cursor_test.c | 2 +- tests/tests_main.c | 46 ++++----- tests/video_test.c | 26 ++++- tests/window_test.c | 13 ++- 15 files changed, 156 insertions(+), 155 deletions(-) diff --git a/include/pal/pal_core.h b/include/pal/pal_core.h index ce82697..b281546 100644 --- a/include/pal/pal_core.h +++ b/include/pal/pal_core.h @@ -76,6 +76,7 @@ typedef _Bool bool; #endif // _PAL_BUILD_DLL #define PAL_BIT(x) 1 << x +#define PAL_BIT64(x) 1ULL << x /** * @brief A signed 8-bit integer diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 87c3d01..26d0311 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -114,6 +114,24 @@ typedef enum { PAL_VIDEO_FEATURE_WINDOW_SET_ICON = PAL_BIT(31), } PalVideoFeatures; +/** + * @enum PalVideoFeatures2 + * @brief Extended Video system features. + * + * All extended video features follow the format `PAL_VIDEO_FEATURE_**` for + * consistency and API use. + * + * @since 1.3 + * @ingroup pal_video + */ +typedef enum { + PAL_VIDEO_FEATURE_TOPMOST_WINDOW = PAL_BIT64(32), + PAL_VIDEO_FEATURE_DECORATED_WINDOW = PAL_BIT64(33), + PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY = PAL_BIT64(34), + PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR = PAL_BIT64(35), + PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY = PAL_BIT64(36) +} PalVideoFeatures2; + /** * @enum PalOrientation * @brief Orientation types for a monitor. @@ -689,6 +707,23 @@ PAL_API void PAL_CALL palUpdateVideo(); */ PAL_API PalVideoFeatures PAL_CALL palGetVideoFeatures(); +/** + * @brief Get the supported features of the video system. + * + * The video system must be initialized before this call. + * This returns the supported features from palGetVideoFeatures() + * and adds additionally supported features. + * + * @return video features on success or `0` on failure. + * + * Thread safety: This function is thread safe. + * + * @since 1.3 + * @ingroup pal_video + * @sa palInitVideo + */ +PAL_API PalVideoFeatures2 PAL_CALL palGetVideoFeaturesEx(); + /** * @brief Set the FBConfig for the video system. * @@ -1126,6 +1161,7 @@ PAL_API PalResult PAL_CALL palGetWindowStyle( * @brief Get the monitor the provided window is currently on. * * The video system must be initialized before this call. + * `PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR` must be supported. * * @param[in] window Pointer to the window. * @param[out] outMonitor Pointer to a PalMonitor to recieve the monitor. @@ -1650,6 +1686,8 @@ PAL_API void PAL_CALL palDestroyCursor(PalCursor* cursor); * @brief Show or hide the cursor. * * The video system must be initialized before this call. + * `PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY` must be supported. + * * This affects all created cursors since the platform (OS) merges all cursors * into a single one on the screen. * diff --git a/src/pal_core.c b/src/pal_core.c index 48a3519..5318913 100644 --- a/src/pal_core.c +++ b/src/pal_core.c @@ -66,9 +66,9 @@ freely, subject to the following restrictions: #define PAL_DEFAULT_ALIGNMENT 16 #define PAL_VERSION_MAJOR 1 -#define PAL_VERSION_MINOR 0 +#define PAL_VERSION_MINOR 3 #define PAL_VERSION_BUILD 0 -#define PAL_VERSION_STRING "1.0.0" +#define PAL_VERSION_STRING "1.3.0" #define PAL_LOG_MSG_SIZE 4096 #ifdef _WIN32 diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 9b58165..e8d13f9 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -125,8 +125,8 @@ typedef struct { int dpi; Uint32 h; PalWindowState state; - PalCursor* cursor; PalWindow* window; + XIC ic; // X11 only } WindowData; @@ -622,7 +622,6 @@ typedef struct { Display* display; Window root; XContext dataID; - Cursor hiddenCursor; const char* className; Visual* visual; @@ -781,6 +780,7 @@ typedef struct { Int32 maxMonitorData; Int32 pixelFormat; PalVideoFeatures features; + PalVideoFeatures2 features2; const PalAllocator* allocator; PalEventDriver* eventDriver; const Backend* backend; @@ -1104,6 +1104,7 @@ static void xCheckFeatures() (unsigned char**)&supportedAtoms); PalVideoFeatures features = 0; + PalVideoFeatures2 features2 = 0; for (unsigned long i = 0; i < count; ++i) { if (supportedAtoms[i] == s_X11Atoms._NET_WM_STATE_MAXIMIZED_VERT) { features |= PAL_VIDEO_FEATURE_WINDOW_SET_STATE; @@ -1161,7 +1162,13 @@ static void xCheckFeatures() features |= PAL_VIDEO_FEATURE_WINDOW_GET_TITLE; features |= PAL_VIDEO_FEATURE_WINDOW_FLASH_TRAY; + // extended features + features2 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; + features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; + features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; + s_Video.features = features; + s_Video.features2 = features2; s_X11.free(supportedAtoms); } @@ -1861,20 +1868,6 @@ static PalResult xInitVideo() "glXGetVisualFromFBConfig"); } - // create a hidden cursor. - // This is used to simulate cursor hide and show - Pixmap map = s_X11.createPixmap(s_X11.display, s_X11.root, 1, 1, 1); - XColor dummy; - s_X11.hiddenCursor = s_X11.createPixmapCursor( - s_X11.display, - map, - map, - &dummy, - &dummy, - 0, - 0); - - s_X11.freePixmap(s_X11.display, map); xCreateScancodeTable(); xCreateKeycodeTable(); @@ -1892,7 +1885,6 @@ static PalResult xInitVideo() return PAL_RESULT_PLATFORM_FAILURE; } - // clang-format on return PAL_RESULT_SUCCESS; } @@ -1902,10 +1894,6 @@ static void xShutdownVideo() s_X11.freeColormap(s_X11.display, s_X11.colormap); } - if (s_X11.hiddenCursor) { - s_X11.freeCursor(s_X11.display, s_X11.hiddenCursor); - } - s_X11.closeIM(s_X11.im); s_X11.closeDisplay(s_X11.display); dlclose(s_X11.handle); @@ -2445,35 +2433,12 @@ static PalResult xEnumerateMonitors( static PalResult xGetPrimaryMonitor(PalMonitor** outMonitor) { RROutput primary = s_X11.getOutputPrimary(s_X11.display, s_X11.root); - - if (!primary) { - // primary monitor is not set, set the first one - // clang-format off - XRRScreenResources* resources = s_X11.getScreenResources(s_X11.display, s_X11.root); - // clang-format on - - for (int i = 0; i < resources->noutput; ++i) { - RROutput output = resources->outputs[i]; - // clang-format off - XRROutputInfo* outputInfo = s_X11.getOutputInfo(s_X11.display, resources, output); - // clang-format on - - if (outputInfo->connection == RR_Connected && - outputInfo->crtc != None) { - primary = resources->outputs[i]; - break; - } - } - } - - // if we still did not get one, we fail if (primary) { *outMonitor = TO_PAL_HANDLE(PalMonitor, primary); return PAL_RESULT_SUCCESS; - - } else { - return PAL_RESULT_PLATFORM_FAILURE; } + + return PAL_RESULT_PLATFORM_FAILURE; } static PalResult xGetMonitorInfo( @@ -2922,33 +2887,41 @@ static PalResult xCreateWindow( } // get monitor + int monitorX = 0; + int monitorY = 0; + Uint32 monitorW = 0; + Uint32 monitorH = 0; if (info->monitor) { monitor = info->monitor; } else { // get primary monitor xGetPrimaryMonitor(&monitor); - if (!monitor) { - return PAL_RESULT_PLATFORM_FAILURE; - } } - // get monitor info - PalResult result = palGetMonitorInfo(monitor, &monitorInfo); - if (result != PAL_RESULT_SUCCESS) { - return result; + if (monitor) { + // get monitor info + PalResult result = palGetMonitorInfo(monitor, &monitorInfo); + if (result != PAL_RESULT_SUCCESS) { + return result; + } + + monitorX = monitorInfo.x; + monitorY = monitorInfo.y; + monitorW = monitorInfo.width; + monitorH = monitorInfo.height; } Int32 x, y = 0; // the position and size must be scaled with the dpi before this call if (info->center) { - x = monitorInfo.x + (monitorInfo.width - info->width) / 2; - y = monitorInfo.y + (monitorInfo.height - info->height) / 2; + x = monitorX + (monitorW - info->width) / 2; + y = monitorY + (monitorH - info->height) / 2; } else { // we set 100 for each axix - x = monitorInfo.x + 100; - y = monitorInfo.y + 100; + x = monitorX + 100; + y = monitorY + 100; } // check and set transparency @@ -3186,7 +3159,6 @@ static PalResult xCreateWindow( data->isAttached = false; // true for attached windows data->dpi = monitorInfo.dpi; // the current window monitor data->window = TO_PAL_HANDLE(PalWindow, window); - data->cursor = nullptr; s_X11.saveContext(s_X11.display, window, s_X11.dataID, (XPointer)data); // create an input context @@ -3380,39 +3352,7 @@ PalResult xGetWindowMonitor( PalWindow* window, PalMonitor** outMonitor) { - Window xWin = FROM_PAL_HANDLE(Window, window); - XWindowAttributes attr; - if (!s_X11.getWindowAttributes(s_X11.display, xWin, &attr)) { - return PAL_RESULT_INVALID_WINDOW; - } - - XRRScreenResources* resources = nullptr; - resources = s_X11.getScreenResources(s_X11.display, s_X11.root); - - for (int i = 0; i < resources->noutput; ++i) { - RROutput output = resources->outputs[i]; - // clang-format off - XRROutputInfo* info = s_X11.getOutputInfo(s_X11.display, resources, output); - // clang-format on - - if (info->connection == RR_Connected && info->crtc != None) { - // clang-format off - XRRCrtcInfo* crtc = s_X11.getCrtcInfo(s_X11.display, resources, info->crtc); - // clang-format on - - // check bounds to see if window is on the monitor - if (attr.x >= crtc->x && attr.x < crtc->x + crtc->width && - attr.y >= crtc->y && attr.y < crtc->y + crtc->height) { - // found monitor - *outMonitor = TO_PAL_HANDLE(PalMonitor, output); - break; - } - s_X11.freeCrtcInfo(crtc); - } - s_X11.freeOutputInfo(info); - } - s_X11.freeScreenResources(resources); - return PAL_RESULT_SUCCESS; + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; } PalResult xGetWindowTitle( @@ -3896,27 +3836,8 @@ void xDestroyCursor(PalCursor* cursor) void xShowCursor(bool show) { - // X11 does not have a single function to show or hide cursor globally - // so we query on windows and set the cursor for each - // The limitation is any window not attached to PAL will not be affected - for (int i = 0; i < s_Video.maxWindowData; i++) { - WindowData* data = &s_Video.windowData[i]; - Window xWin = FROM_PAL_HANDLE(Window, data->window); - if (show) { - // we check if the window has a valid cursor - // if not we use the root windows cursor - Cursor cursor = FROM_PAL_HANDLE(Cursor, data->cursor); - if (cursor) { - s_X11.defineCursor(s_X11.display, xWin, cursor); - } else { - s_X11.undefineCursor(s_X11.display, xWin); - } - - } else { - s_X11.defineCursor(s_X11.display, xWin, s_X11.hiddenCursor); - } - s_X11.flush(s_X11.display); - } + // x11 does not support Hiding and showing cursor + return; } PalResult xClipCursor( @@ -4014,10 +3935,6 @@ PalResult xSetWindowCursor( Window xCursor = FROM_PAL_HANDLE(Cursor, cursor); if (xCursor) { s_X11.defineCursor(s_X11.display, xWin, xCursor); - // cache the cursor. Show or hide cursor needs it - WindowData* data = nullptr; - s_X11.findContext(s_X11.display, xWin, s_X11.dataID, (XPointer*)&data); - data->cursor = cursor; } else { s_X11.undefineCursor(s_X11.display, xWin); @@ -4055,7 +3972,6 @@ PalResult xAttachWindow( data->skipConfigure = true; data->skipState = true; data->skipIfAttached = true; - data->cursor = nullptr; data->window = window; data->w = attr.width; data->h = attr.height; @@ -4275,6 +4191,15 @@ PalVideoFeatures PAL_CALL palGetVideoFeatures() return s_Video.features; } +Uint64 PAL_CALL palGetVideoFeaturesEx() +{ + if (!s_Video.initialized) { + return 0; + } + + return ((Uint64)s_Video.features2) | (Uint64)s_Video.features; +} + PalResult PAL_CALL palSetFBConfig( const int index, PalFBConfigBackend backend) diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index e14bbe6..f5fe153 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -104,6 +104,7 @@ typedef struct { Int32 pixelFormat; Int32 maxWindowData; PalVideoFeatures features; + PalVideoFeatures2 features2; const PalAllocator* allocator; PalEventDriver* eventDriver; HINSTANCE shcore; @@ -1281,6 +1282,15 @@ PalVideoFeatures PAL_CALL palGetVideoFeatures() return s_Video.features; } +Uint64 PAL_CALL palGetVideoFeaturesEx() +{ + if (!s_Video.initialized) { + return 0; + } + + return ((Uint64)s_Video.features2) | (Uint64)s_Video.features; +} + PalResult PAL_CALL palSetFBConfig( const int index, PalFBConfigBackend backend) diff --git a/tests/char_event_test.c b/tests/char_event_test.c index 3f2a106..1bdc5ed 100644 --- a/tests/char_event_test.c +++ b/tests/char_event_test.c @@ -37,7 +37,7 @@ bool charEventTest() } // fill the create info struct - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/cursor_test.c b/tests/cursor_test.c index 710ce75..1b51e39 100644 --- a/tests/cursor_test.c +++ b/tests/cursor_test.c @@ -82,7 +82,7 @@ bool cursorTest() } // fill the create info struct - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/icon_test.c b/tests/icon_test.c index b92dc2a..13590df 100644 --- a/tests/icon_test.c +++ b/tests/icon_test.c @@ -80,7 +80,7 @@ bool iconTest() } // fill the create info struct - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/input_window_test.c b/tests/input_window_test.c index ef83f9f..9f806de 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -436,7 +436,7 @@ bool inputWindowTest() } // fill the create info struct - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/opengl_context_test.c b/tests/opengl_context_test.c index 8d0e568..ba7d685 100644 --- a/tests/opengl_context_test.c +++ b/tests/opengl_context_test.c @@ -148,7 +148,7 @@ bool openglContextTest() return false; } - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/opengl_multi_context_test.c b/tests/opengl_multi_context_test.c index f69d31c..21a319b 100644 --- a/tests/opengl_multi_context_test.c +++ b/tests/opengl_multi_context_test.c @@ -148,7 +148,7 @@ bool openglMultiContextTest() return false; } - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/system_cursor_test.c b/tests/system_cursor_test.c index ce92fa5..6cf0aa3 100644 --- a/tests/system_cursor_test.c +++ b/tests/system_cursor_test.c @@ -53,7 +53,7 @@ bool systemCursorTest() } // fill the create info struct - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; diff --git a/tests/tests_main.c b/tests/tests_main.c index 3a22feb..44aad75 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,49 +9,49 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - registerTest("Logger Test", loggerTest); - registerTest("Time Test", timeTest); - registerTest("User Event Test", userEventTest); - registerTest("Event Test", eventTest); + // registerTest("Logger Test", loggerTest); + // registerTest("Time Test", timeTest); + // registerTest("User Event Test", userEventTest); + // registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - registerTest("System Test", systemTest); + // registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - registerTest("Thread Test", threadTest); - registerTest("TLS Test", tlsTest); - registerTest("Mutex Test", mutexTest); - registerTest("Condvar Test", condvarTest); + // registerTest("Thread Test", threadTest); + // registerTest("TLS Test", tlsTest); + // registerTest("Mutex Test", mutexTest); + // registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO registerTest("Video Test", videoTest); - registerTest("Monitor Test", monitorTest); - registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); - registerTest("Icon Test", iconTest); - registerTest("Cursor Test", cursorTest); - registerTest("Input Window Test", inputWindowTest); - registerTest("System Cursor Test", systemCursorTest); - registerTest("Attach Window Test", attachWindowTest); - registerTest("Character Event Test", charEventTest); + // registerTest("Monitor Test", monitorTest); + // registerTest("Monitor Mode Test", monitorModeTest); + // registerTest("Window Test", windowTest); + // registerTest("Icon Test", iconTest); + // registerTest("Cursor Test", cursorTest); + // registerTest("Input Window Test", inputWindowTest); + // registerTest("System Cursor Test", systemCursorTest); + // registerTest("Attach Window Test", attachWindowTest); + // registerTest("Character Event Test", charEventTest); #endif // PAL_HAS_VIDEO #if PAL_HAS_OPENGL - registerTest("Opengl Test", openglTest); - registerTest("Opengl FBConfig Test", openglFBConfigTest); + // registerTest("Opengl Test", openglTest); + // registerTest("Opengl FBConfig Test", openglFBConfigTest); #endif // PAL_HAS_OPENGL // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - registerTest("Opengl Context Test", openglContextTest); - registerTest("Opengl Multi Context Test", openglMultiContextTest); + // registerTest("Opengl Context Test", openglContextTest); + // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); diff --git a/tests/video_test.c b/tests/video_test.c index 32e8861..dda3e82 100644 --- a/tests/video_test.c +++ b/tests/video_test.c @@ -11,7 +11,7 @@ bool videoTest() palLog(nullptr, ""); PalResult result; - PalVideoFeatures features; + PalVideoFeatures2 features; // initialize the video system result = palInitVideo(nullptr, nullptr); @@ -21,8 +21,8 @@ bool videoTest() return false; } - // get supported features - features = palGetVideoFeatures(); + // get supported features. Now uses extended function + features = palGetVideoFeaturesEx(); palLog(nullptr, "Supported Video Features:"); if (features & PAL_VIDEO_FEATURE_HIGH_DPI) { palLog(nullptr, " High DPI windows"); @@ -148,6 +148,26 @@ bool videoTest() palLog(nullptr, " Getting cursor position"); } + if (features & PAL_VIDEO_FEATURE_TOPMOST_WINDOW) { + palLog(nullptr, " Topmost windows"); + } + + if (features & PAL_VIDEO_FEATURE_DECORATED_WINDOW) { + palLog(nullptr, " Decorated (Normal) windows"); + } + + if (features & PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY) { + palLog(nullptr, " Setting cursor visibility"); + } + + if (features & PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR) { + palLog(nullptr, " Getting window current monitor"); + } + + if (features & PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY) { + palLog(nullptr, " Getting primary monitor"); + } + // shutdown the video system palShutdownVideo(); diff --git a/tests/window_test.c b/tests/window_test.c index eb80724..7f941b5 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -161,7 +161,7 @@ bool windowTest() PalResult result; PalWindow* window = nullptr; PalWindowCreateInfo createInfo = {0}; - PalVideoFeatures features; + PalVideoFeatures2 features; bool running = false; // event driver @@ -193,15 +193,22 @@ bool windowTest() } // get video system features - features = palGetVideoFeatures(); + features = palGetVideoFeaturesEx(); // fill the create info struct - createInfo.monitor = nullptr; // use primary monitor + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; + // check if we support decorated windows (title bar, close etc) + if (!(features & PAL_VIDEO_FEATURE_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style = PAL_WINDOW_STYLE_BORDERLESS; + } + #if UNICODE_NAME createInfo.title = "PAL Test Window Unicode - àà"; #else From f4323a7e75c1ed43516967da4829f0f58c4fec3d Mon Sep 17 00:00:00 2001 From: nichcode Date: Sat, 1 Nov 2025 13:25:00 +0000 Subject: [PATCH 02/51] fix minimize bug x11 --- include/pal/pal_video.h | 8 ++++++-- src/video/pal_video_linux.c | 2 ++ src/video/pal_video_win32.c | 8 ++++++++ tests/attach_window_test.c | 12 ++++++++++++ tests/video_test.c | 4 ++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 26d0311..0d5904a 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -129,7 +129,8 @@ typedef enum { PAL_VIDEO_FEATURE_DECORATED_WINDOW = PAL_BIT64(33), PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY = PAL_BIT64(34), PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR = PAL_BIT64(35), - PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY = PAL_BIT64(36) + PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY = PAL_BIT64(36), + PAL_VIDEO_FEATURE_FOREIGN_WINDOWS = PAL_BIT64(37) } PalVideoFeatures2; /** @@ -611,7 +612,7 @@ typedef struct { */ typedef struct { void* nativeDisplay; /**< The platform (OS) display.*/ - void* nativeWindow; /**< The platform (OS) handle.*/ + void* nativeWindow; /**< The window platform (OS) handle.*/ } PalWindowHandleInfo; /** @@ -797,6 +798,7 @@ PAL_API PalResult PAL_CALL palEnumerateMonitors( * @brief Get the primary connected monitor. * * The video system must be initialized before this call. + * `PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY` must be supported. * * The monitor handle must not be freed by the user, they are managed by the * platform (OS). @@ -1822,6 +1824,7 @@ PAL_API void* PAL_CALL palGetInstance(); * @brief Attach a foreign or native window to PAL video system. * * The video system must be initialized before this call. + * `PAL_VIDEO_FEATURE_FOREIGN_WINDOWS` must be supported. * * This function registers the provided window with PAL video system so it * can manage events and use its functionality/API for the provided window. @@ -1862,6 +1865,7 @@ PAL_API PalResult PAL_CALL palAttachWindow( * @brief Detach a foreign or native window from PAL video system. * * The video system must be initialized before this call. + * `PAL_VIDEO_FEATURE_FOREIGN_WINDOWS` must be supported. * * This function unregisters the provided window from PAL video system. * The window must not be owned by PAL otherwise the function fails diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index e8d13f9..09fb4da 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1071,6 +1071,7 @@ static void xCheckFeatures() X_INTERN(_NET_WM_STATE_ABOVE); X_INTERN(_NET_WM_STATE_MAXIMIZED_VERT); X_INTERN(_NET_WM_STATE_MAXIMIZED_HORZ); + X_INTERN(_NET_WM_STATE_HIDDEN); X_INTERN(_NET_WM_NAME); X_INTERN(UTF8_STRING); X_INTERN(_NET_WM_WINDOW_TYPE_UTILITY); @@ -1166,6 +1167,7 @@ static void xCheckFeatures() features2 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; + features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; s_Video.features = features; s_Video.features2 = features2; diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index f5fe153..95441cd 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -1205,6 +1205,14 @@ PalResult PAL_CALL palInitVideo( s_Video.setProcessAwareness(WIN32_DPI_AWARE); } + // extended features + s_Video.features2 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; + s_Video.features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; + s_Video.features2 |= PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY; + s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR; + s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; + s_Video.features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; + s_Video.initialized = true; s_Video.allocator = allocator; s_Video.eventDriver = eventDriver; diff --git a/tests/attach_window_test.c b/tests/attach_window_test.c index ae18294..e0c238e 100644 --- a/tests/attach_window_test.c +++ b/tests/attach_window_test.c @@ -238,6 +238,18 @@ bool attachWindowTest() return false; } + // check for support + PalVideoFeatures2 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE_FOREIGN_WINDOWS)) { + // clang-format off + palLog(nullptr, "Attaching and detaching foreign windows feature not supported"); + // clang-format on + + palDestroyEventDriver(eventDriver); + palShutdownVideo(); + return false; + } + // we are interested in move and close events palSetEventDispatchMode( eventDriver, diff --git a/tests/video_test.c b/tests/video_test.c index dda3e82..b02caf3 100644 --- a/tests/video_test.c +++ b/tests/video_test.c @@ -168,6 +168,10 @@ bool videoTest() palLog(nullptr, " Getting primary monitor"); } + if (features & PAL_VIDEO_FEATURE_FOREIGN_WINDOWS) { + palLog(nullptr, " Attaching and detaching foreign windows"); + } + // shutdown the video system palShutdownVideo(); From 08cfc584bbc7068b4262481e10c374d21585add7 Mon Sep 17 00:00:00 2001 From: nichcode Date: Sat, 1 Nov 2025 17:53:36 +0000 Subject: [PATCH 03/51] window test --- include/pal/pal_video.h | 7 +- src/video/pal_video_linux.c | 2197 ++++++++++++++++++++++++++++++++++- src/video/pal_video_win32.c | 3 +- tests/tests_main.c | 4 +- tests/video_test.c | 4 + tests/window_test.c | 2 +- 6 files changed, 2155 insertions(+), 62 deletions(-) diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 0d5904a..b2c1226 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -130,7 +130,8 @@ typedef enum { PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY = PAL_BIT64(34), PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR = PAL_BIT64(35), PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY = PAL_BIT64(36), - PAL_VIDEO_FEATURE_FOREIGN_WINDOWS = PAL_BIT64(37) + PAL_VIDEO_FEATURE_FOREIGN_WINDOWS = PAL_BIT64(37), + PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE = PAL_BIT64(38), } PalVideoFeatures2; /** @@ -904,7 +905,8 @@ PAL_API PalResult PAL_CALL palGetCurrentMonitorMode( * * PAL only validates the monitor display mode pointer not the values. To be * safe, users must get the monitor mode from palEnumerateMonitorModes() or call - * palValidateMonitorMode() to validate before switching. + * palValidateMonitorMode() to validate before switching. + * palValidateMonitorMode() is not supported on all platforms. * * If the monitor display mode submitted is invalid, this function might fail * depending on the platform (OS). @@ -929,6 +931,7 @@ PAL_API PalResult PAL_CALL palSetMonitorMode( * @brief Check if a monitor display mode is valid on the provided monitor. * * The video system must be initialized before this call. + * `PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE` must be supported. * * @param[in] monitor The monitor. * @param[in] mode Pointer to a PalMonitorMode to validate. diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 09fb4da..33c8496 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -25,6 +25,8 @@ freely, subject to the following restrictions: // Includes // ================================================== +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 200112L #include "pal/pal_video.h" #include @@ -46,6 +48,11 @@ freely, subject to the following restrictions: // Wayland headers #if PAL_HAS_WAYLAND #include +#include + +#include +#include +#include #endif // PAL_HAS_WAYLAND // ================================================== @@ -126,8 +133,15 @@ typedef struct { Uint32 h; PalWindowState state; PalWindow* window; - - XIC ic; // X11 only + + // X11 only + XIC ic; + + // Wayland only + void* xdgSurface; + void* xdgToplevel; + void* buffer; + void* decoration; } WindowData; typedef struct { @@ -622,7 +636,6 @@ typedef struct { Display* display; Window root; XContext dataID; - const char* className; Visual* visual; Colormap colormap; @@ -719,6 +732,970 @@ static X11Atoms s_X11Atoms = {0}; #pragma endregion +// ================================================== +// Wayland Typedefs, enums and structs +// ================================================== + +#pragma region Wayland Typedefs +#ifdef PAL_HAS_WAYLAND + +typedef struct wl_display* (*wl_display_connect_fn)(const char*); +typedef void (*wl_display_disconnect_fn)(struct wl_display*); +typedef int (*wl_display_roundtrip_fn)(struct wl_display*); +typedef int (*wl_display_dispatch_fn)(struct wl_display*); +typedef void (*wl_proxy_destroy_fn)(struct wl_proxy*); + +typedef int (*wl_proxy_add_listener_fn)( + struct wl_proxy*, + void (**)(void), void*); + +typedef struct wl_proxy* (*wl_proxy_marshal_constructor_v_fn)( + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, ...); + +typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, + uint32_t, ...); + +typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); + +typedef int (*wl_proxy_add_listener_fn)( + struct wl_proxy*, + void (**)(void), void*); + +typedef int (*wl_display_get_error_fn)(struct wl_display*); +typedef int (*wl_display_dispatch_pending_fn)(struct wl_display*); +typedef int (*wl_display_flush_fn)(struct wl_display*); +typedef int (*wl_display_prepare_read_fn)(struct wl_display*); +typedef int (*wl_display_read_events_fn)(struct wl_display*); + +typedef struct { + bool checkFeatures; + bool modesPhase; + int monitorCount; + + void* handle; + struct wl_display* display; + struct wl_registry* registry; + struct xdg_wm_base* xdgBase; + struct wl_compositor* compositor; + struct wl_shm* shm; + struct zxdg_decoration_manager_v1* decorationManager; + struct zwp_pointer_constraints* pointerConstraints; + + const struct wl_interface* outputInterface; + const struct wl_interface* seatInterface; + const struct wl_interface* compositorInterface; + const struct wl_interface* registryInterface; + const struct wl_interface* surfaceInterface; + const struct wl_interface* shmInterface; + const struct wl_interface* bufferInterface; + const struct wl_interface* shmPoolInterface; + const struct wl_interface* regionInterface; + const struct wl_interface* pointerInterface; + + wl_display_connect_fn displayConnect; + wl_display_disconnect_fn displayDisconnect; + wl_display_roundtrip_fn displayRoundtrip; + wl_display_dispatch_fn displayDispatch; + wl_proxy_destroy_fn proxyDestroy; + wl_proxy_add_listener_fn proxyAddListener; + wl_proxy_marshal_constructor_v_fn proxyMarshalCnstructor; + wl_proxy_marshal_flags_fn proxyMarshalFlags; + wl_proxy_get_version_fn proxyGetVersion; + wl_display_get_error_fn getError; + wl_display_dispatch_pending_fn dispatchPending; + wl_display_flush_fn displayFlush; + wl_display_prepare_read_fn prepareRead; + wl_display_read_events_fn readEvents; +} Wayland; + +typedef struct { + int count; + int maxCount; + PalMonitorMode* modes; +} MonitorModesData; + +static Wayland s_Wl = {0}; + +#endif // PAL_HAS_WAYLAND +#pragma endregion + +#pragma region Wayland-Client-Protocol +#ifdef PAL_HAS_WAYLAND + +static inline void* wlRegistryBind( + struct wl_registry *wl_registry, + uint32_t name, + const struct wl_interface *interface, + uint32_t version) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *)wl_registry, + WL_REGISTRY_BIND, + interface, + version, + 0, + name, + interface->name, + version, + NULL); + + return (void *)id; +} + +static inline int wlRegistryAddListener( + struct wl_registry *wl_registry, + const struct wl_registry_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) wl_registry, + (void (**)(void)) listener, data); +} + +static inline struct wl_registry* wlDisplayGetRegistry( + struct wl_display *wl_display) +{ + struct wl_proxy *registry; + registry = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_display, + 1, // WL_DISPLAY_GET_REGISTRY + s_Wl.registryInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_display), + 0, + NULL); + + return (struct wl_registry *)registry; +} + +static inline int wlOutputAddListener( + struct wl_output *wl_output, + const struct wl_output_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) wl_output, + (void (**)(void)) listener, data); +} + +static inline struct wl_surface* wlCompositorCreateSurface( + struct wl_compositor *wl_compositor) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_compositor, + 0, // WL_COMPOSITOR_CREATE_SURFACE, + s_Wl.surfaceInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_compositor), + 0, + NULL); + + return (struct wl_surface*) id; +} + +static inline void wlSurfaceCommit(struct wl_surface *wl_surface) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_surface, + 6, // WL_SURFACE_COMMIT + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_surface), + 0); +} + +static inline void wlSurfaceDestroy(struct wl_surface *wl_surface) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_surface, + 0, // WL_SURFACE_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_surface), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct wl_shm_pool* wlShmCreatePool( + struct wl_shm *wl_shm, + int32_t fd, + int32_t size) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_shm, + 0, // WL_SHM_CREATE_POOL + s_Wl.shmPoolInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_shm), + 0, + NULL, + fd, + size); + + return (struct wl_shm_pool *) id; +} + +static inline void wlShmPoolDestroy(struct wl_shm_pool *wl_shm_pool) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_shm_pool, + WL_SHM_POOL_DESTROY, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_shm_pool), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct wl_buffer* wlShmPoolCreateBuffer( + struct wl_shm_pool *wl_shm_pool, + int32_t offset, + int32_t width, + int32_t height, + int32_t stride, + uint32_t format) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_shm_pool, + WL_SHM_POOL_CREATE_BUFFER, + s_Wl.bufferInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_shm_pool), + 0, + NULL, + offset, + width, + height, + stride, + format); + + return (struct wl_buffer *) id; +} + +static inline void wlBufferDestroy(struct wl_buffer *wl_buffer) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_buffer, + WL_BUFFER_DESTROY, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_buffer), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void wlSurfaceAttach( + struct wl_surface *wl_surface, + struct wl_buffer *buffer, + int32_t x, + int32_t y) +{ + s_Wl.proxyMarshalFlags + ((struct wl_proxy *) wl_surface, + WL_SURFACE_ATTACH, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_surface), + 0, + buffer, + x, + y); +} + +static inline void wlSurfaceDamageBuffer( + struct wl_surface *wl_surface, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_surface, + WL_SURFACE_DAMAGE_BUFFER, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_surface), + 0, + x, + y, + width, + height); +} + +#endif // PAL_HAS_WAYLAND +#pragma endregion + +#pragma region Xdg-Shell-Protocol +#ifdef PAL_HAS_WAYLAND + +struct xdg_wm_base; +struct xdg_surface; +struct xdg_toplevel; + +// forward declare +static struct wl_buffer* createShmBuffer( + int width, + int height); + +const struct wl_interface xdg_popup_interface; +const struct wl_interface xdg_positioner_interface; +const struct wl_interface xdg_surface_interface; +const struct wl_interface xdg_toplevel_interface; + +struct xdg_wm_base_listener { + void (*ping)(void*, struct xdg_wm_base*, uint32_t); +}; + +struct xdg_surface_listener { + void (*configure)(void*, struct xdg_surface*, uint32_t); +}; + +struct xdg_toplevel_listener { + // clang-format off + void (*configure)(void*, struct xdg_toplevel*, int32_t, int32_t, struct wl_array*); + void (*close)(void*, struct xdg_toplevel*); + void (*configure_bounds)(void*, struct xdg_toplevel*, int32_t, int32_t); + void (*wm_capabilities)(void*, struct xdg_toplevel*, struct wl_array*); + // clang-format on +}; + +static inline void xdgWmBasePong( + struct xdg_wm_base *xdg_wm_base, + uint32_t serial) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_wm_base, + 3, // XDG_WM_BASE_PONG + NULL, + s_Wl.proxyGetVersion(( + struct wl_proxy *) xdg_wm_base), + 0, + serial); +} + +static inline int xdgWmBaseAddListener( + struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +static inline struct xdg_surface* xdgWmBaseGetXdgSurface( + struct xdg_wm_base *xdg_wm_base, + struct wl_surface *surface) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_wm_base, + 2, // XDG_WM_BASE_GET_XDG_SURFACE, + &xdg_surface_interface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_wm_base), + 0, + NULL, + surface); + + return (struct xdg_surface *) id; +} + +static inline struct xdg_toplevel* xdgSurfaceGetToplevel( + struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_surface, + 1, // XDG_SURFACE_GET_TOPLEVEL, + &xdg_toplevel_interface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_surface), + 0, + NULL); + + return (struct xdg_toplevel *) id; +} + +static inline void xdgSurfaceAckConfigure( + struct xdg_surface *xdg_surface, + uint32_t serial) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_surface, + 4, // XDG_SURFACE_ACK_CONFIGURE + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_surface), + 0, + serial); +} + +static void wmBasePing( + void* data, + struct xdg_wm_base* base, + uint32_t serial) +{ + xdgWmBasePong(base, serial); +} + +static void xdgSurfaceConfigure( + void* data, + struct xdg_surface* surface, + uint32_t serial) +{ + xdgSurfaceAckConfigure(surface, serial); +} + +static void xdgToplevelConfigure( + void* data, + struct xdg_toplevel* toplevel, + int32_t width, + int32_t height, + struct wl_array* states) +{ + WindowData* winData = (WindowData*)data; + if (width > 0 && height > 0) { + winData->w = width; + winData->h = height; + } +} + +static void xdgToplevelClose( + void* data, + struct xdg_toplevel* toplevel) +{ + printf("hello"); + (void)data; + (void)toplevel; +} + +static inline int xdgSurfaceAddListener( + struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +static inline void xdgWmBaseDestroy(struct xdg_wm_base *xdg_wm_base) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_wm_base, + 0, // XDG_WM_BASE_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_wm_base), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void xdgSurfaceDestroy(struct xdg_surface *xdg_surface) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_surface, + 0, // XDG_SURFACE_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_surface), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void xdgToplevelDestroy(struct xdg_toplevel *xdg_toplevel) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 0, // XDG_TOPLEVEL_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void xdgToplevelSetTitle( + struct xdg_toplevel *xdg_toplevel, + const char *title) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 2, // XDG_TOPLEVEL_SET_TITLE + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0, + title); +} + +static inline void xdgToplevelSetMaximized(struct xdg_toplevel *xdg_toplevel) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 9, //XDG_TOPLEVEL_SET_MAXIMIZED + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0); +} + +static inline void xdgToplevelSetMinimized(struct xdg_toplevel *xdg_toplevel) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 13, // XDG_TOPLEVEL_SET_MINIMIZED + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0); +} + +static inline int xdgToplevelAddListener( + struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +static inline void xdgToplevelSetMinSize( + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 8, //XDG_TOPLEVEL_SET_MIN_SIZE + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0, + width, + height); +} + +static inline void xdgToplevelSetMaxSize( + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 7, //XDG_TOPLEVEL_SET_MAX_SIZE + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0, + width, + height); +} + +static inline void xdgToplevelSetAppId( + struct xdg_toplevel *xdg_toplevel, + const char *app_id) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 3, // XDG_TOPLEVEL_SET_APP_ID + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0, + app_id); +} + +static const struct wl_interface *xdg_shell_types[26]; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 6, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 6, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_surface_interface = { + "xdg_surface", 6, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 6, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_popup_interface = { + "xdg_popup", 6, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + +static void setupXdgShellProtocol() +{ + xdg_shell_types[0] = NULL; + xdg_shell_types[1] = NULL; + xdg_shell_types[2] = NULL; + xdg_shell_types[3] = NULL; + xdg_shell_types[4] = &xdg_positioner_interface; + xdg_shell_types[5] = &xdg_surface_interface; + xdg_shell_types[6] = s_Wl.surfaceInterface; + xdg_shell_types[7] = &xdg_toplevel_interface; + xdg_shell_types[8] = &xdg_popup_interface; + xdg_shell_types[9] = &xdg_surface_interface; + xdg_shell_types[10] = &xdg_positioner_interface; + xdg_shell_types[11] = &xdg_toplevel_interface; + xdg_shell_types[12] = s_Wl.seatInterface; + xdg_shell_types[13] = NULL; + xdg_shell_types[14] = NULL; + xdg_shell_types[15] = NULL; + xdg_shell_types[16] = s_Wl.seatInterface; + xdg_shell_types[17] = NULL; + xdg_shell_types[18] = s_Wl.seatInterface; + xdg_shell_types[19] = NULL; + xdg_shell_types[20] = NULL; + xdg_shell_types[21] = s_Wl.outputInterface; + xdg_shell_types[22] = s_Wl.seatInterface; + xdg_shell_types[23] = NULL; + xdg_shell_types[24] = &xdg_positioner_interface; + xdg_shell_types[25] = NULL; +} + +static const struct xdg_wm_base_listener wmBaseListener = { + .ping = wmBasePing +}; + +static const struct xdg_surface_listener xdgSurfaceListener = { + .configure = xdgSurfaceConfigure +}; + +static const struct xdg_toplevel_listener xdgToplevelListener = { + .configure = xdgToplevelConfigure, + .close = xdgToplevelClose +}; + +#endif // PAL_HAS_WAYLAND +#pragma endregion + +#pragma region Zxdg-Decoraion_Manager-V1 +#ifdef PAL_HAS_WAYLAND + +struct xdg_toplevel; +struct zxdg_decoration_manager_v1; +struct zxdg_toplevel_decoration_v1; + +struct zxdg_toplevel_decoration_v1_listener { + void (*configure)( + void*, + struct zxdg_toplevel_decoration_v1*, + uint32_t); +}; + +const struct wl_interface zxdg_decoration_manager_v1_interface; +const struct wl_interface zxdg_toplevel_decoration_v1_interface; + +static inline void zxdgDecorationManagerV1Destroy( + struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) zxdg_decoration_manager_v1, + 0, // ZXDG_DECORATION_MANAGER_V1_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) zxdg_decoration_manager_v1), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct zxdg_toplevel_decoration_v1* zxdgGetTopleveDecoration( + struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, + struct xdg_toplevel *toplevel) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) zxdg_decoration_manager_v1, + 1, // ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION, + &zxdg_toplevel_decoration_v1_interface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) zxdg_decoration_manager_v1), + 0, + NULL, + toplevel); + + return (struct zxdg_toplevel_decoration_v1 *) id; +} + +static inline int zxdgToplevelDecorationV1AddListener( + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + const struct zxdg_toplevel_decoration_v1_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) zxdg_toplevel_decoration_v1, + (void (**)(void)) listener, data); +} + +static inline void zxdgToplevelDecorationV1Destroy( + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) zxdg_toplevel_decoration_v1, + 0, // ZXDG_TOPLEVEL_DECORATION_V1_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) zxdg_toplevel_decoration_v1), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void zxdgToplevelDecorationV1SetMode( + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + uint32_t mode) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) zxdg_toplevel_decoration_v1, + 1, // ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) zxdg_toplevel_decoration_v1), + 0, + mode); +} + +static const struct wl_interface *xdg_decoration_unstable_v1_types[] = { + NULL, + &zxdg_toplevel_decoration_v1_interface, + &xdg_toplevel_interface, +}; + +static const struct wl_message zxdg_decoration_manager_v1_requests[] = { + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 }, +}; + +const struct wl_interface zxdg_decoration_manager_v1_interface = { + "zxdg_decoration_manager_v1", 1, + 2, zxdg_decoration_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 }, + { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 }, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { + { "configure", "u", xdg_decoration_unstable_v1_types + 0 }, +}; + +const struct wl_interface zxdg_toplevel_decoration_v1_interface = { + "zxdg_toplevel_decoration_v1", 1, + 3, zxdg_toplevel_decoration_v1_requests, + 1, zxdg_toplevel_decoration_v1_events, +}; + +#endif //PAL_HAS_WAYLAND +#pragma endregion + +#pragma region Zwp-Pointer-Constraints +#ifdef PAL_HAS_WAYLAND + +struct zwp_confined_pointer_v1; +struct zwp_pointer_constraints_v1; + +const struct wl_interface zwp_confined_pointer_v1_interface; + +static inline struct zwp_confined_pointer_v1* zwpPointerConstraintsConfine( + struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, + struct wl_surface *surface, + struct wl_pointer *pointer, + struct wl_region *region, + uint32_t lifetime) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) zwp_pointer_constraints_v1, + 2, // ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER + &zwp_confined_pointer_v1_interface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) zwp_pointer_constraints_v1), + 0, + NULL, + surface, + pointer, + region, + lifetime); + + return (struct zwp_confined_pointer_v1 *) id; +} + +static inline void zwpConfinedPointerV1Destroy( + struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) zwp_confined_pointer_v1, + 0, // ZWP_CONFINED_POINTER_V1_DESTROY + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) zwp_confined_pointer_v1), + WL_MARSHAL_FLAG_DESTROY); +} + +static const struct wl_interface *pointer_constraints_unstable_v1_types[14]; + +static const struct wl_message zwp_pointer_constraints_v1_requests[] = { + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "lock_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 2 }, + { "confine_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 7 }, +}; + +const struct wl_interface zwp_pointer_constraints_v1_interface = { + "zwp_pointer_constraints_v1", 1, + 3, zwp_pointer_constraints_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwp_locked_pointer_v1_requests[] = { + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "set_cursor_position_hint", "ff", pointer_constraints_unstable_v1_types + 0 }, + { "set_region", "?o", pointer_constraints_unstable_v1_types + 12 }, +}; + +static const struct wl_message zwp_locked_pointer_v1_events[] = { + { "locked", "", pointer_constraints_unstable_v1_types + 0 }, + { "unlocked", "", pointer_constraints_unstable_v1_types + 0 }, +}; + +const struct wl_interface zwp_locked_pointer_v1_interface = { + "zwp_locked_pointer_v1", 1, + 3, zwp_locked_pointer_v1_requests, + 2, zwp_locked_pointer_v1_events, +}; + +static const struct wl_message zwp_confined_pointer_v1_requests[] = { + { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, + { "set_region", "?o", pointer_constraints_unstable_v1_types + 13 }, +}; + +static const struct wl_message zwp_confined_pointer_v1_events[] = { + { "confined", "", pointer_constraints_unstable_v1_types + 0 }, + { "unconfined", "", pointer_constraints_unstable_v1_types + 0 }, +}; + +const struct wl_interface zwp_confined_pointer_v1_interface = { + "zwp_confined_pointer_v1", 1, + 2, zwp_confined_pointer_v1_requests, + 2, zwp_confined_pointer_v1_events, +}; + +static void setupZwpPointerProtocol() +{ + // clang-format off + pointer_constraints_unstable_v1_types[0] = NULL; + pointer_constraints_unstable_v1_types[1] = NULL; + pointer_constraints_unstable_v1_types[2] = &zwp_locked_pointer_v1_interface; + pointer_constraints_unstable_v1_types[3] = s_Wl.surfaceInterface; + pointer_constraints_unstable_v1_types[4] = s_Wl.pointerInterface; + pointer_constraints_unstable_v1_types[5] = s_Wl.regionInterface; + pointer_constraints_unstable_v1_types[6] = NULL; + pointer_constraints_unstable_v1_types[7] = &zwp_confined_pointer_v1_interface; + pointer_constraints_unstable_v1_types[8] = s_Wl.surfaceInterface; + pointer_constraints_unstable_v1_types[9] = s_Wl.pointerInterface; + pointer_constraints_unstable_v1_types[10] = s_Wl.regionInterface; + pointer_constraints_unstable_v1_types[11] = NULL; + pointer_constraints_unstable_v1_types[12] = s_Wl.regionInterface; + pointer_constraints_unstable_v1_types[13] = s_Wl.regionInterface; + // clang-format on +} + +#endif // PAL_HAS_WAYLAND +#pragma endregion + typedef struct { // clang-format off void (*shutdownVideo)(); @@ -739,7 +1716,7 @@ typedef struct { PalResult (*restoreWindow)(PalWindow*); PalResult (*showWindow)(PalWindow*); PalResult (*hideWindow)(PalWindow*); - PalResult (*xFlashWindow)(PalWindow*, const PalFlashInfo*); + PalResult (*flashWindow)(PalWindow*, const PalFlashInfo*); PalResult (*getWindowStyle)(PalWindow*, PalWindowStyle*); PalResult (*getWindowMonitor)(PalWindow*, PalMonitor**); PalResult (*getWindowTitle)(PalWindow*, Uint64, Uint64*, char*); @@ -786,6 +1763,7 @@ typedef struct { const Backend* backend; WindowData* windowData; MonitorData* monitorData; + const char* className; } VideoLinux; static VideoLinux s_Video = {0}; @@ -854,6 +1832,16 @@ static WindowData* getFreeWindowData() return nullptr; } +static WindowData* findWindowData(PalWindow* window) +{ + for (int i = 0; i < s_Video.maxWindowData; ++i) { + if (s_Video.windowData[i].used && + s_Video.windowData[i].window == window) { + return &s_Video.windowData[i]; + } + } +} + static void resetMonitorData() { memset( @@ -913,6 +1901,12 @@ static void freeMonitorData(PalMonitor* monitor) } } +// ================================================== +// X11 API +// ================================================== + +#pragma region X11 API + static PalResult glxBackend() { // user choose GLX FBConfig backend @@ -920,9 +1914,15 @@ static PalResult glxBackend() // Rare return PAL_RESULT_PLATFORM_FAILURE; } - // clang-format off + // check if we already have a colormap + // if so we delete it and create a new one + if (s_X11.colormap) { + s_X11.freeColormap(s_X11.display, s_X11.colormap); + s_X11.colormap = None; + } + int count = 0; GLXFBConfig* configs = s_X11.glxGetFBConfigs( s_X11.display, @@ -961,7 +1961,7 @@ static PalResult glxBackend() } static PalResult eglXBackend(int index) -{ +{ // user choose EGL FBConfig backend if (!s_Egl.handle) { return PAL_RESULT_PLATFORM_FAILURE; @@ -971,6 +1971,13 @@ static PalResult eglXBackend(int index) return PAL_RESULT_PLATFORM_FAILURE; } + // check if we already have a colormap + // if so we delete it and create a new one + if (s_X11.colormap) { + s_X11.freeColormap(s_X11.display, s_X11.colormap); + s_X11.colormap = None; + } + EGLDisplay display = EGL_NO_DISPLAY; display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_X11.display); @@ -1037,12 +2044,6 @@ static PalResult eglXBackend(int index) return PAL_RESULT_SUCCESS; } -// ================================================== -// X11 API -// ================================================== - -#pragma region X11 API - static RRMode xFindMode( XRRScreenResources* resources, const PalMonitorMode* mode) @@ -1844,7 +2845,6 @@ static PalResult xInitVideo() s_X11.skipNotifyEvent = true; s_X11.dataID = (XContext)s_X11.uniqueContext(); - s_X11.className = "PAL"; resetMonitorData(); xCacheMonitors(true); @@ -2475,6 +3475,8 @@ static PalResult xGetMonitorInfo( if (monitor == TO_PAL_HANDLE(PalMonitor, primary)) { info->primary = true; + } else { + info->primary = false; } // get monitor pos and size @@ -2730,40 +3732,7 @@ static PalResult xValidateMonitorMode( PalMonitor* monitor, PalMonitorMode* mode) { - // clang-format off - XRRScreenResources* resources = s_X11.getScreenResources(s_X11.display, s_X11.root); - // clang-format on - - // get the monitor info - XRROutputInfo* outputInfo = s_X11.getOutputInfo( - s_X11.display, - resources, - FROM_PAL_HANDLE(RROutput, monitor)); - - if (!outputInfo) { - // invalid monitor - s_X11.freeScreenResources(resources); - return PAL_RESULT_INVALID_MONITOR; - } - - if (outputInfo->connection != RR_Connected) { - // invalid monitor - s_X11.freeScreenResources(resources); - return PAL_RESULT_INVALID_MONITOR; - } - - // find the monitor display mode - RRMode displayMode = xFindMode(resources, mode); - if (displayMode == None) { - s_X11.freeOutputInfo(outputInfo); - s_X11.freeScreenResources(resources); - return PAL_RESULT_INVALID_MONITOR_MODE; - } - - s_X11.freeOutputInfo(outputInfo); - s_X11.freeScreenResources(resources); - - return PAL_RESULT_SUCCESS; + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; } static PalResult xSetMonitorOrientation( @@ -2992,7 +3961,7 @@ static PalResult xCreateWindow( } if (!resClass || strlen(resClass) == 0) { - resClass = s_X11.className; + resClass = s_Video.className; } hints->res_name = (char*)resName; @@ -4053,7 +5022,7 @@ static Backend s_XBackend = { .restoreWindow = xRestoreWindow, .showWindow = xShowWindow, .hideWindow = xHideWindow, - .xFlashWindow = xFlashWindow, + .flashWindow = xFlashWindow, .getWindowStyle = xGetWindowStyle, .getWindowMonitor = xGetWindowMonitor, .getWindowTitle = xGetWindowTitle, @@ -4086,13 +5055,1123 @@ static Backend s_XBackend = { .attachWindow = xAttachWindow, .detachWindow = xDetachWindow}; -#pragma endregions +#pragma endregion // ================================================== -// Public API +// Wayland API // ================================================== -PalResult PAL_CALL palInitVideo( +#pragma region Wayland API +#ifdef PAL_HAS_WAYLAND + +static int createShmFile(Uint64 size) +{ + char template[] = "/tmp/pal-shm-XXXXXX"; + int fd = mkstemp(template); + if (fd < 0) { + return -1; + } + + unlink(template); + if (ftruncate(fd, size) < 0) { + return -1; + } + + return fd; +} + +static struct wl_buffer* createShmBuffer( + int width, + int height) +{ + int stride = width * 4; + Uint64 size = stride * height; + struct wl_buffer* buffer = nullptr; + struct wl_shm_pool* pool = nullptr; + void* data = nullptr; + + int fd = createShmFile(size); + if (fd == -1) { + return nullptr; + } + + data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + return nullptr; + } + + memset(data, 0xFF, size); // white + pool = wlShmCreatePool(s_Wl.shm, fd, size); + if (!pool) { + return nullptr; + } + + buffer = wlShmPoolCreateBuffer( + pool, + 0, + width, + height, + stride, + WL_SHM_FORMAT_XRGB8888); + + if (!buffer) { + return nullptr; + } + + wlShmPoolDestroy(pool); + munmap(data, size); + close(fd); + return buffer; +} + +static void wlGlobalHandle( + void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) +{ + if (s_Wl.checkFeatures) { + PalVideoFeatures features = 0; + features |= PAL_VIDEO_FEATURE_HIGH_DPI; + features |= PAL_VIDEO_FEATURE_MONITOR_GET_ORIENTATION; + features |= PAL_VIDEO_FEATURE_MULTI_MONITORS; + features |= PAL_VIDEO_FEATURE_MONITOR_GET_MODE; + features |= PAL_VIDEO_FEATURE_WINDOW_SET_TITLE; + features |= PAL_VIDEO_FEATURE_WINDOW_SET_SIZE; + features |= PAL_VIDEO_FEATURE_WINDOW_SET_STATE; + features |= PAL_VIDEO_FEATURE_BORDERLESS_WINDOW; + s_Video.features = features; + } + + if (strcmp(interface, "wl_compositor") == 0) { + s_Wl.compositor = wlRegistryBind( + registry, + name, + s_Wl.compositorInterface, + 4); + + } else if (strcmp(interface, "xdg_wm_base") == 0) { + s_Wl.xdgBase = wlRegistryBind( + registry, + name, + &xdg_wm_base_interface, + 1); + xdgWmBaseAddListener(s_Wl.xdgBase, &wmBaseListener, nullptr); + + } else if (strcmp(interface, "wl_shm") == 0) { + s_Wl.shm = wlRegistryBind( + registry, + name, + s_Wl.shmInterface, + 1); + + } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { + s_Wl.decorationManager = wlRegistryBind( + registry, + name, + &zxdg_decoration_manager_v1_interface, + 1); + + s_Video.features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; + + } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { + s_Wl.pointerConstraints = wlRegistryBind( + registry, + name, + &zwp_pointer_constraints_v1_interface, + 1); + + + } else if (strcmp(interface, "wl_output") == 0) { + // wayland does not let use query monitors directly + // so we enumerate and store at init and update the + // cache when a monitor is added or removed + + MonitorData* monitorData = getFreeMonitorData(); + if (!monitorData) { + return; + } + + PalMonitor* m = TO_PAL_HANDLE(PalMonitor, name); + monitorData->monitor = m; + s_Wl.monitorCount++; + } +} + +static void wlGlobalRemove( + void* data, + struct wl_registry* registry, + uint32_t name) +{ + PalMonitor* m = TO_PAL_HANDLE(PalMonitor, name); + MonitorData* monitorData = findMonitorData(m); + if (monitorData) { + monitorData->used = false; + } +} + +static void wlOutputGeometry( + void* data, + struct wl_output* output, + int32_t x, + int32_t y, + int32_t, // we dont need physical size + int32_t, // we dont need physical size + int32_t, // we dont need subpixel + const char* make, + const char* model, + int32_t transform) +{ + if (s_Wl.modesPhase) { + return; + } + + PalMonitorInfo* info = (PalMonitorInfo*)data; + info->x = x; + info->y = y; + + switch (transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_180: { + info->orientation = PAL_ORIENTATION_LANDSCAPE; + break; + } + + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: { + info->orientation = PAL_ORIENTATION_PORTRAIT; + break; + } + + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: { + info->orientation = PAL_ORIENTATION_LANDSCAPE_FLIPPED; + break; + } + + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: { + info->orientation = PAL_ORIENTATION_PORTRAIT_FLIPPED; + break; + } + } + + snprintf(info->name, 32, "%s %s", make, model); +} + +static void wlOutputMode( + void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ + PalMonitorInfo* info = (PalMonitorInfo*)data; + if (flags & WL_OUTPUT_MODE_CURRENT) { + info->width = width; + info->height = height; + info->refreshRate = (refresh + 500) / 1000; + } +} + +static void wlOutputMonitorModes( + void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ + MonitorModesData* modesInfo = (MonitorModesData*)data; + if (modesInfo->modes) { + if (modesInfo->count < modesInfo->maxCount) { + PalMonitorMode* mode = &modesInfo->modes[modesInfo->count]; + mode->width = width; + mode->height = height; + mode->refreshRate = (refresh + 500) / 1000; + mode->bpp = 0; // default + } + } + modesInfo->count++; +} + +static void wlOutputScale( + void* data, + struct wl_output* output, + int32_t scale) +{ + if (s_Wl.modesPhase) { + return; + } + + PalMonitorInfo* info = (PalMonitorInfo*)data; + float dpi = (float)scale * 96.0f; + info->dpi = (Uint32)dpi; +} + +static void wlOutputDone( + void* data, + struct wl_output* output) +{ + (void)data; + (void)output; +} + +static const struct wl_registry_listener s_RegistryListener = { + .global = wlGlobalHandle, + .global_remove = nullptr +}; + +static const struct wl_output_listener s_OutputListener = { + .geometry = wlOutputGeometry, + .mode = wlOutputMode, + .done = wlOutputDone, + .scale = wlOutputScale +}; + +static const struct wl_output_listener s_ModesListener = { + .geometry = wlOutputGeometry, + .mode = wlOutputMonitorModes, + .done = wlOutputDone, + .scale = wlOutputScale +}; + +static const struct wl_output_listener s_DefaultModeListener = { + .geometry = wlOutputGeometry, + .mode = wlOutputMode, + .done = wlOutputDone, + .scale = wlOutputScale +}; + +PalResult wlInitVideo() +{ + // load wayland libray + s_Wl.handle = dlopen("libwayland-client.so.0", RTLD_LAZY); + if (!s_Wl.handle) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + // get the exported global variables + s_Wl.outputInterface = dlsym(s_Wl.handle, "wl_output_interface"); + s_Wl.seatInterface = dlsym(s_Wl.handle, "wl_seat_interface"); + s_Wl.compositorInterface = dlsym(s_Wl.handle, "wl_compositor_interface"); + s_Wl.surfaceInterface = dlsym(s_Wl.handle, "wl_surface_interface"); + s_Wl.registryInterface = dlsym(s_Wl.handle, "wl_registry_interface"); + s_Wl.shmInterface = dlsym(s_Wl.handle, "wl_shm_interface"); + s_Wl.bufferInterface = dlsym(s_Wl.handle, "wl_buffer_interface"); + s_Wl.shmPoolInterface = dlsym(s_Wl.handle, "wl_shm_pool_interface"); + s_Wl.regionInterface = dlsym(s_Wl.handle, "wl_region_interface"); + s_Wl.pointerInterface = dlsym(s_Wl.handle, "wl_pointer_interface"); + + // load function procs + s_Wl.displayConnect = (wl_display_connect_fn)dlsym( + s_Wl.handle, + "wl_display_connect"); + + s_Wl.displayDisconnect = (wl_display_disconnect_fn)dlsym( + s_Wl.handle, + "wl_display_disconnect"); + + s_Wl.displayRoundtrip = (wl_display_roundtrip_fn)dlsym( + s_Wl.handle, + "wl_display_roundtrip"); + + s_Wl.displayDispatch = (wl_display_dispatch_fn)dlsym( + s_Wl.handle, + "wl_display_dispatch"); + + s_Wl.proxyAddListener = (wl_proxy_add_listener_fn)dlsym( + s_Wl.handle, + "wl_proxy_add_listener"); + + s_Wl.proxyMarshalCnstructor = (wl_proxy_marshal_constructor_v_fn)dlsym( + s_Wl.handle, + "wl_proxy_marshal_constructor_versioned"); + + s_Wl.proxyDestroy = (wl_proxy_destroy_fn)dlsym( + s_Wl.handle, + "wl_proxy_destroy"); + + s_Wl.proxyMarshalFlags = (wl_proxy_marshal_flags_fn)dlsym( + s_Wl.handle, + "wl_proxy_marshal_flags"); + + s_Wl.proxyGetVersion = (wl_proxy_get_version_fn)dlsym( + s_Wl.handle, + "wl_proxy_get_version"); + + s_Wl.getError = (wl_display_get_error_fn)dlsym( + s_Wl.handle, + "wl_display_get_error"); + + s_Wl.dispatchPending = (wl_display_dispatch_pending_fn)dlsym( + s_Wl.handle, + "wl_display_dispatch_pending"); + + s_Wl.displayFlush = (wl_display_flush_fn)dlsym( + s_Wl.handle, + "wl_display_flush"); + + s_Wl.prepareRead = (wl_display_prepare_read_fn)dlsym( + s_Wl.handle, + "wl_display_prepare_read"); + + s_Wl.readEvents = (wl_display_read_events_fn)dlsym( + s_Wl.handle, + "wl_display_read_events"); + + // initialize wayland + s_Wl.modesPhase = false; + s_Wl.checkFeatures = true; + s_Wl.monitorCount = 0; + setupXdgShellProtocol(); + setupZwpPointerProtocol(); + + s_Wl.display = s_Wl.displayConnect(nullptr); + s_Wl.registry = wlDisplayGetRegistry(s_Wl.display); + wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); + s_Wl.displayRoundtrip(s_Wl.display); + + s_Wl.checkFeatures = false; + return PAL_RESULT_SUCCESS; +} + +void wlShutdownVideo() +{ + if (s_Wl.decorationManager) { + zxdgDecorationManagerV1Destroy(s_Wl.decorationManager); + } + + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.shm); + xdgWmBaseDestroy(s_Wl.xdgBase); + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.compositor); + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.registry); + s_Wl.displayDisconnect(s_Wl.display); + dlclose(s_Wl.handle); +} + +void wlUpdateVideo() +{ + while (s_Wl.prepareRead(s_Wl.display) != 0) { + s_Wl.dispatchPending(s_Wl.display); + s_Wl.displayFlush(s_Wl.display); + if (s_Wl.readEvents(s_Wl.display) == 0) { + s_Wl.dispatchPending(s_Wl.display); + } + } + + + + // s_Wl.dispatchPending(s_Wl.display); + // s_Wl.displayFlush(s_Wl.display); +} + +PalResult wlSetFBConfig( + const int index, + PalFBConfigBackend backend) +{ + // user choose EGL FBConfig backend + if (!s_Egl.handle) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + if (!s_Egl.eglBindAPI(EGL_OPENGL_API)) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + EGLDisplay display = EGL_NO_DISPLAY; + display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_Wl.display); + + if (display == EGL_NO_DISPLAY) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + if (!s_Egl.eglInitialize(display, nullptr, nullptr)) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + EGLint numConfigs = 0; + if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + EGLint configSize = sizeof(EGLConfig) * numConfigs; + EGLConfig* eglConfigs = palAllocate(s_Video.allocator, configSize, 0); + if (!eglConfigs) { + return PAL_RESULT_OUT_OF_MEMORY; + } + + s_Egl.eglGetConfigs(display, eglConfigs, numConfigs, &numConfigs); + EGLConfig config = eglConfigs[index]; + + // FIXME: + + // // we create a visual from the config + // EGLint visualID; + // s_Egl.eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualID); + // if (visualID == 0) { + // return PAL_RESULT_INVALID_GL_FBCONFIG; + // } + + // int numVisuals = 0; + // XVisualInfo tmp; + // tmp.visualid = visualID; + + // // clang-format off + // // get a matching visual info + // XVisualInfo* visualInfo = s_X11.getVisualInfo( + // s_X11.display, + // VisualIDMask, + // &tmp, + // &numVisuals); + // // clang-format on + + // if (!visualInfo) { + // return PAL_RESULT_INVALID_GL_FBCONFIG; + // } + + // s_X11.visual = visualInfo->visual; + // s_X11.depth = visualInfo->depth; + // s_X11.colormap = s_X11.createColormap( + // s_X11.display, + // s_X11.root, + // visualInfo->visual, + // AllocNone); + + // if (!s_X11.colormap) { + // return PAL_RESULT_INVALID_GL_FBCONFIG; + // } + + // s_Egl.eglTerminate(display); + // palFree(s_Video.allocator, eglConfigs); + return PAL_RESULT_SUCCESS; +} + +PalResult wlEnumerateMonitors( + Int32* count, + PalMonitor** outMonitors) +{ + int _count = 0; + int maxCount = outMonitors ? *count : 0; + if (outMonitors) { + if (_count < maxCount) { + for (int i = 0; i < maxCount; i++) { + // we get the monitor from our cache array + PalMonitor* monitor = nullptr; + for (int y = 0; y < s_Video.maxMonitorData; y++) { + if (s_Video.monitorData[y].used) { + monitor = s_Video.monitorData[y].monitor; + break; + } + } + + // write to user provided array + outMonitors[_count] = monitor; + _count++; // index into user array + } + } + } + + if (!outMonitors) { + *count = s_Wl.monitorCount; + } + + return PAL_RESULT_SUCCESS; +} + +PalResult wlGetPrimaryMonitor(PalMonitor** outMonitor) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlGetMonitorInfo( + PalMonitor* monitor, + PalMonitorInfo* info) +{ + uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); + // bind the monitor and get its information + struct wl_output* output = wlRegistryBind( + s_Wl.registry, + name, + s_Wl.outputInterface, + 3); + + if (!output) { + return PAL_RESULT_INVALID_MONITOR; + } + + s_Wl.modesPhase = false; + wlOutputAddListener(output, &s_OutputListener, info); + s_Wl.displayRoundtrip(s_Wl.display); + s_Wl.proxyDestroy((struct wl_proxy*)output); + + PalMonitor* primary = nullptr; + wlGetPrimaryMonitor(&primary); + if (monitor == primary) { + info->primary = true; + } else { + info->primary = false; + } + + return PAL_RESULT_SUCCESS; +} + +PalResult wlEnumerateMonitorModes( + PalMonitor* monitor, + Int32* count, + PalMonitorMode* modes) +{ + uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); + // bind the monitor and get its information + struct wl_output* output = wlRegistryBind( + s_Wl.registry, + name, + s_Wl.outputInterface, + 3); + + if (!output) { + return PAL_RESULT_INVALID_MONITOR; + } + + MonitorModesData data; + data.count = 0; + data.modes = modes; + data.maxCount = modes ? *count : 0; + + // we need only modes information + s_Wl.modesPhase = true; + wlOutputAddListener(output, &s_ModesListener, &data); + s_Wl.displayRoundtrip(s_Wl.display); + s_Wl.proxyDestroy((struct wl_proxy*)output); + + if (!modes) { + *count = data.count; + } + + return PAL_RESULT_SUCCESS; +} + +PalResult wlGetCurrentMonitorMode( + PalMonitor* monitor, + PalMonitorMode* mode) +{ + uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); + // bind the monitor and get its information + struct wl_output* output = wlRegistryBind( + s_Wl.registry, + name, + s_Wl.outputInterface, + 3); + + if (!output) { + return PAL_RESULT_INVALID_MONITOR; + } + + // we dont want to add another listener just to get the + // default monitor display mode + PalMonitorInfo tmpInfo; + s_Wl.modesPhase = true; + wlOutputAddListener(output, &s_OutputListener, &tmpInfo); + s_Wl.displayRoundtrip(s_Wl.display); + s_Wl.proxyDestroy((struct wl_proxy*)output); + + // fill out display mode with the one we got back + mode->bpp = 0; // default + mode->refreshRate = tmpInfo.refreshRate; + mode->width = tmpInfo.width; + mode->height = tmpInfo.height; + + return PAL_RESULT_SUCCESS; +} + +PalResult wlSetMonitorMode( + PalMonitor* monitor, + PalMonitorMode* mode) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlValidateMonitorMode( + PalMonitor* monitor, + PalMonitorMode* mode) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlSetMonitorOrientation( + PalMonitor* monitor, + PalOrientation orientation) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlCreateWindow( + const PalWindowCreateInfo* info, + PalWindow** outWindow) +{ + struct wl_surface* surface = nullptr; + struct xdg_surface* xdgSurface = nullptr; + struct xdg_toplevel* xdgToplevel = nullptr; + + // check if we have xdg and a compositor + if (!s_Wl.xdgBase || !s_Wl.compositor) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + if (info->style & PAL_WINDOW_STYLE_TOPMOST) { + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; + } + + if (info->style & PAL_WINDOW_STYLE_TRANSPARENT) { + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; + } + + if (info->style & PAL_WINDOW_STYLE_TOOL) { + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; + } + + if (!(info->style & PAL_WINDOW_STYLE_BORDERLESS)) { + if (s_Wl.decorationManager) { + // user wants decorated window but its not supported + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; + } + } + + WindowData* data = getFreeWindowData(); + if (!data) { + return PAL_RESULT_OUT_OF_MEMORY; + } + + // create surface + surface = wlCompositorCreateSurface(s_Wl.compositor); + if (!surface) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + xdgSurface = xdgWmBaseGetXdgSurface( + s_Wl.xdgBase, + surface); + + if (!xdgSurface) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + xdgToplevel = xdgSurfaceGetToplevel(xdgSurface); + if (!xdgSurface) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + // set APP id + const char* appID = getenv("RESOURCE_CLASS"); + if (!appID || strlen(appID) == 0) { + appID = s_Video.className; + } + + xdgToplevelSetTitle(xdgToplevel, info->title); + xdgToplevelSetAppId(xdgToplevel, appID); + + xdgToplevelAddListener(xdgToplevel, &xdgToplevelListener, data); + xdgSurfaceAddListener(xdgSurface, &xdgSurfaceListener, data); + wlSurfaceCommit(surface); + s_Wl.displayRoundtrip(s_Wl.display); + + if (info->maximized && info->show) { + // we need the maximized size the compositor will use + // and use that to create the buffer + xdgToplevelSetMaximized(xdgToplevel); + wlSurfaceCommit(surface); + s_Wl.displayRoundtrip(s_Wl.display); + + } else { + data->w = info->width; + data->h = info->height; + } + + data->xdgSurface = xdgSurface; + data->xdgToplevel = xdgToplevel; + data->window = (PalWindow*)surface; + data->buffer = nullptr; + data->isAttached = false; + + // show window + if (info->show == false) { + // we cant do much anymore + *outWindow = (PalWindow*)surface; + return PAL_RESULT_SUCCESS; + } + + // minimize + // This is just a requeest, the compositor might ignore it + if (info->minimized) { + xdgToplevelSetMinimized(xdgToplevel); + wlSurfaceCommit(surface); + } + + // decorated window + if (!(info->style & PAL_WINDOW_STYLE_BORDERLESS)) { + struct zxdg_toplevel_decoration_v1* decoration = nullptr; + decoration = zxdgGetTopleveDecoration( + s_Wl.decorationManager, + xdgToplevel); + + zxdgToplevelDecorationV1SetMode( + decoration, + 2); // SERVER_SIDE_DECORATION + + data->decoration = decoration; + } + + // create a white buffer for the surface + struct wl_buffer* buffer = nullptr; + buffer = createShmBuffer(data->w, data->h); + if (!buffer) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + struct wl_surface* wl_surface = (struct wl_surface*)data->window; + wlSurfaceAttach(wl_surface, buffer, 0, 0); + wlSurfaceDamageBuffer(wl_surface, 0, 0, data->w, data->h); + wlSurfaceCommit(wl_surface); + s_Wl.displayRoundtrip(s_Wl.display); + + // resizable + if (!(info->style & PAL_WINDOW_STYLE_RESIZABLE)) { + xdgToplevelSetMinSize(xdgToplevel, data->w, data->h); + xdgToplevelSetMaxSize(xdgToplevel, data->w, data->h); + wlSurfaceCommit(surface); + } + + *outWindow = data->window; + return PAL_RESULT_SUCCESS; +} + +void wlDestroyWindow(PalWindow* window) +{ + WindowData* data = findWindowData(window); + if (!data || (data && data->isAttached)) { + return; + } + + if (data->decoration) { + zxdgToplevelDecorationV1Destroy(data->decoration); + } + + wlBufferDestroy(data->buffer); + xdgToplevelDestroy(data->xdgToplevel); + xdgSurfaceDestroy(data->xdgSurface); + wlSurfaceDestroy((struct wl_surface*)window); + data->used = false; +} + +PalResult wlMinimizeWindow(PalWindow* window) +{ + WindowData* data = findWindowData(window); + if (!data) { + return PAL_RESULT_INVALID_WINDOW; + } + + xdgToplevelSetMinimized(data->xdgToplevel); + return PAL_RESULT_SUCCESS; +} + +PalResult wlMaximizeWindow(PalWindow* window) +{ + WindowData* data = findWindowData(window); + if (!data) { + return PAL_RESULT_INVALID_WINDOW; + } + + xdgToplevelSetMaximized(data->xdgToplevel); + return PAL_RESULT_SUCCESS; +} + +PalResult wlRestoreWindow(PalWindow* window) +{ + return PAL_RESULT_SUCCESS; +} + +PalResult wlShowWindow(PalWindow* window) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlHideWindow(PalWindow* window) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlFlashWindow( + PalWindow* window, + const PalFlashInfo* info) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlGetWindowStyle( + PalWindow* window, + PalWindowStyle* outStyle) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlGetWindowMonitor( + PalWindow* window, + PalMonitor** outMonitor) +{ + return PAL_RESULT_SUCCESS; +} + +PalResult wlGetWindowTitle( + PalWindow* window, + Uint64 bufferSize, + Uint64* outSize, + char* outBuffer) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlGetWindowPos( + PalWindow* window, + Int32* x, + Int32* y) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlGetWindowSize( + PalWindow* window, + Uint32* width, + Uint32* height) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlGetWindowState( + PalWindow* window, + PalWindowState* outState) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +bool wlIsWindowVisible(PalWindow* window) +{ + return false; +} + +PalWindow* wlGetFocusWindow() +{ + return nullptr; +} + +PalWindowHandleInfo wlGetWindowHandleInfo(PalWindow* window) +{ + PalWindowHandleInfo info; + info.nativeDisplay = (void*)s_Wl.display; + info.nativeWindow = (void*)window; + return info; +} + +PalResult wlSetWindowOpacity( + PalWindow* window, + float opacity) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlSetWindowStyle( + PalWindow* window, + PalWindowStyle style) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlSetWindowTitle( + PalWindow* window, + const char* title) +{ + return PAL_RESULT_SUCCESS; +} + +PalResult wlSetWindowPos( + PalWindow* window, + Int32 x, + Int32 y) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlSetWindowSize( + PalWindow* window, + Uint32 width, + Uint32 height) +{ + return PAL_RESULT_SUCCESS; +} + +PalResult wlSetFocusWindow(PalWindow* window) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlCreateIcon( + const PalIconCreateInfo* info, + PalIcon** outIcon) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +void wlDestroyIcon(PalIcon* icon) +{ + return; +} + +PalResult wlSetWindowIcon( + PalWindow* window, + PalIcon* icon) +{ + return PAL_RESULT_THREAD_FEATURE_NOT_SUPPORTED; +} + +PalResult wlCreateCursor( + const PalCursorCreateInfo* info, + PalCursor** outCursor) +{ + return PAL_RESULT_SUCCESS; +} + +PalResult wlCreateCursorFrom( + PalCursorType type, + PalCursor** outCursor) +{ + return PAL_RESULT_SUCCESS; +} + +void wlDestroyCursor(PalCursor* cursor) +{ + +} + +void wlShowCursor(bool show) +{ + return; +} + +PalResult wlClipCursor( + PalWindow* window, + bool clip) +{ + if (!(s_Video.features & PAL_VIDEO_FEATURE_CLIP_CURSOR)) { + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; + + } + + return PAL_RESULT_SUCCESS; +} + +PalResult wlGetCursorPos( + PalWindow* window, + Int32* x, + Int32* y) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlSetCursorPos( + PalWindow* window, + Int32 x, + Int32 y) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlSetWindowCursor( + PalWindow* window, + PalCursor* cursor) +{ + return PAL_RESULT_SUCCESS; +} + +void* wlGetInstance() +{ + return (void*)s_Wl.display; +} + +PalResult wlAttachWindow( + void* windowHandle, + PalWindow** outWindow) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +PalResult wlDetachWindow( + PalWindow* window, + void** outWindowHandle) +{ + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; +} + +static Backend s_wlBackend = { + .shutdownVideo = wlShutdownVideo, + .updateVideo = wlUpdateVideo, + .enumerateMonitors = wlEnumerateMonitors, + .getMonitorInfo = wlGetMonitorInfo, + .getPrimaryMonitor = wlGetPrimaryMonitor, + .enumerateMonitorModes = wlEnumerateMonitorModes, + .getCurrentMonitorMode = wlGetCurrentMonitorMode, + .setMonitorMode = wlSetMonitorMode, + .validateMonitorMode = wlValidateMonitorMode, + .setMonitorOrientation = wlSetMonitorOrientation, + + .createWindow = wlCreateWindow, + .destroyWindow = wlDestroyWindow, + .maximizeWindow = wlMaximizeWindow, + .minimizeWindow = wlMinimizeWindow, + .restoreWindow = wlRestoreWindow, + .showWindow = wlShowWindow, + .hideWindow = wlHideWindow, + .flashWindow = wlFlashWindow, + .getWindowStyle = wlGetWindowStyle, + .getWindowMonitor = wlGetWindowMonitor, + .getWindowTitle = wlGetWindowTitle, + .getWindowPos = wlGetWindowPos, + .getWindowSize = wlGetWindowSize, + .getWindowState = wlGetWindowState, + .isWindowVisible = wlIsWindowVisible, + .getFocusWindow = wlGetFocusWindow, + .getWindowHandleInfo = wlGetWindowHandleInfo, + .setWindowOpacity = wlSetWindowOpacity, + .setWindowStyle = wlSetWindowStyle, + .setWindowTitle = wlSetWindowTitle, + .setWindowPos = wlSetWindowPos, + .setWindowSize = wlSetWindowSize, + .setFocusWindow = wlSetFocusWindow, + + .createIcon = wlCreateIcon, + .destroyIcon = wlDestroyIcon, + .setWindowIcon = wlSetWindowIcon, + + .createCursor = wlCreateCursor, + .createCursorFrom = wlCreateCursorFrom, + .destroyCursor = wlDestroyCursor, + .showCursor = wlShowCursor, + .clipCursor = wlClipCursor, + .getCursorPos = wlGetCursorPos, + .setCursorPos = wlSetCursorPos, + .setWindowCursor = wlSetWindowCursor, + + .attachWindow = wlAttachWindow, + .detachWindow = wlDetachWindow}; + +#endif // PAL_HAS_WAYLAND +#pragma endregion + +// ================================================== +// Public API +// ================================================== + +PalResult PAL_CALL palInitVideo( const PalAllocator* allocator, PalEventDriver* eventDriver) { @@ -4130,12 +6209,20 @@ PalResult PAL_CALL palInitVideo( return PAL_RESULT_OUT_OF_MEMORY; } + s_Video.className = "PAL"; if (x11) { PalResult ret = xInitVideo(); if (ret != PAL_RESULT_SUCCESS) { return ret; } s_Video.backend = &s_XBackend; + + } else { + PalResult ret = wlInitVideo(); + if (ret != PAL_RESULT_SUCCESS) { + return ret; + } + s_Video.backend = &s_wlBackend; } // we load EGL as well @@ -4193,7 +6280,7 @@ PalVideoFeatures PAL_CALL palGetVideoFeatures() return s_Video.features; } -Uint64 PAL_CALL palGetVideoFeaturesEx() +PalVideoFeatures2 PAL_CALL palGetVideoFeaturesEx() { if (!s_Video.initialized) { return 0; @@ -4223,7 +6310,6 @@ PalResult PAL_CALL palSetFBConfig( backend == PAL_CONFIG_BACKEND_EGL || backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { if (s_X11.display) { - // we are on X11 return eglXBackend(index); } } @@ -4340,7 +6426,6 @@ PalResult PAL_CALL palValidateMonitorMode( PalMonitor* monitor, PalMonitorMode* mode) { - if (!s_Video.initialized) { return PAL_RESULT_VIDEO_NOT_INITIALIZED; } @@ -4478,7 +6563,7 @@ PalResult PAL_CALL palFlashWindow( return PAL_RESULT_NULL_POINTER; } - return s_Video.backend->xFlashWindow(window, info); + return s_Video.backend->flashWindow(window, info); } PalResult PAL_CALL palGetWindowStyle( diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index 95441cd..b51cb9b 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -1212,6 +1212,7 @@ PalResult PAL_CALL palInitVideo( s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR; s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; s_Video.features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; + s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE; s_Video.initialized = true; s_Video.allocator = allocator; @@ -1290,7 +1291,7 @@ PalVideoFeatures PAL_CALL palGetVideoFeatures() return s_Video.features; } -Uint64 PAL_CALL palGetVideoFeaturesEx() +palGetVideoFeaturesEx PAL_CALL palGetVideoFeaturesEx() { if (!s_Video.initialized) { return 0; diff --git a/tests/tests_main.c b/tests/tests_main.c index 44aad75..8e434ab 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -26,10 +26,10 @@ int main(int argc, char** argv) #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO - registerTest("Video Test", videoTest); + // registerTest("Video Test", videoTest); // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); + registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); diff --git a/tests/video_test.c b/tests/video_test.c index b02caf3..5599023 100644 --- a/tests/video_test.c +++ b/tests/video_test.c @@ -172,6 +172,10 @@ bool videoTest() palLog(nullptr, " Attaching and detaching foreign windows"); } + if (features & PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE ) { + palLog(nullptr, " Validate monitor display mode"); + } + // shutdown the video system palShutdownVideo(); diff --git a/tests/window_test.c b/tests/window_test.c index 7f941b5..5e804e0 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -206,7 +206,7 @@ bool windowTest() if (!(features & PAL_VIDEO_FEATURE_DECORATED_WINDOW)) { // if we dont support, we need to create a borderless window // and create the decorations ourselves - createInfo.style = PAL_WINDOW_STYLE_BORDERLESS; + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; } #if UNICODE_NAME From 800a044a40f3a0b3902a8b7668290045ca32a936 Mon Sep 17 00:00:00 2001 From: nichcode Date: Wed, 5 Nov 2025 13:46:24 +0000 Subject: [PATCH 04/51] native test --- CHANGELOG.md | 14 +- include/pal/pal_video.h | 31 +++ src/video/pal_video_linux.c | 61 ++++- src/video/pal_video_win32.c | 1 + tests/attach_window_test.c | 15 ++ tests/char_event_test.c | 15 ++ tests/cursor_test.c | 24 ++ tests/icon_test.c | 24 ++ tests/input_window_test.c | 13 +- tests/native_integration_test.c | 380 ++++++++++++++++++++++++++++++++ tests/system_cursor_test.c | 15 ++ tests/tests.h | 1 + tests/tests.lua | 3 +- tests/tests_main.c | 3 +- tests/window_test.c | 15 ++ 15 files changed, 602 insertions(+), 13 deletions(-) create mode 100644 tests/native_integration_test.c diff --git a/CHANGELOG.md b/CHANGELOG.md index c5c7266..d209ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,4 +68,16 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr ### Notes - No API or ABI changes - existing code remains compatible. -- Safe upgrade from **v1.1.0** - just rebuild your project after updating. \ No newline at end of file +- Safe upgrade from **v1.1.0** - just rebuild your project after updating. + +## [1.3.0] - 2025-11- + +### Features +- **Video:** Added **palGetVideoFeaturesEx()** to check supported features. +- **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. + +### Tests + +### Notes +- No API or ABI changes - existing code remains compatible. +- Safe upgrade from **v1.2.0** - just rebuild your project after updating. \ No newline at end of file diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index b2c1226..c29ed7d 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -132,6 +132,7 @@ typedef enum { PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY = PAL_BIT64(36), PAL_VIDEO_FEATURE_FOREIGN_WINDOWS = PAL_BIT64(37), PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE = PAL_BIT64(38), + PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR = PAL_BIT64(39) } PalVideoFeatures2; /** @@ -616,6 +617,20 @@ typedef struct { void* nativeWindow; /**< The window platform (OS) handle.*/ } PalWindowHandleInfo; +/** + * @struct PalWindowHandleInfoEx + * @brief Extended information about a window handle. + * + * @since 1.3 + * @ingroup pal_video + */ +typedef struct { + void* nativeDisplay; /**< The platform (OS) display.*/ + void* nativeWindow; /**< The window platform (OS) handle.*/ + void* nativeHandle1; /**< Extra window handle.*/ + void* nativeHandle2; /**< Extra window handle.*/ +} PalWindowHandleInfoEx; + /** * @struct PalWindowCreateInfo * @brief Creation parameters for a window. @@ -1425,6 +1440,22 @@ PAL_API PalWindow* PAL_CALL palGetFocusWindow(); */ PAL_API PalWindowHandleInfo PAL_CALL palGetWindowHandleInfo(PalWindow* window); +/** + * @brief Get the native handles of the provided window. + * + * The video system must be initialized before this call. + * + * @param[in] window Pointer to the window. + * + * @return The native handles of the window on success or nullptr on failure. + * + * Thread safety: This function is thread-safe. + * + * @since 1.3 + * @ingroup pal_video + */ +PAL_API PalWindowHandleInfoEx PAL_CALL palGetWindowHandleInfoEx(PalWindow* w); + /** * @brief Set the opacity of the provided window. * diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 33c8496..ccd469e 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -737,7 +737,7 @@ static X11Atoms s_X11Atoms = {0}; // ================================================== #pragma region Wayland Typedefs -#ifdef PAL_HAS_WAYLAND +#if PAL_HAS_WAYLAND typedef struct wl_display* (*wl_display_connect_fn)(const char*); typedef void (*wl_display_disconnect_fn)(struct wl_display*); @@ -827,7 +827,7 @@ static Wayland s_Wl = {0}; #pragma endregion #pragma region Wayland-Client-Protocol -#ifdef PAL_HAS_WAYLAND +#if PAL_HAS_WAYLAND static inline void* wlRegistryBind( struct wl_registry *wl_registry, @@ -1034,7 +1034,7 @@ static inline void wlSurfaceDamageBuffer( #pragma endregion #pragma region Xdg-Shell-Protocol -#ifdef PAL_HAS_WAYLAND +#if PAL_HAS_WAYLAND struct xdg_wm_base; struct xdg_surface; @@ -1464,7 +1464,7 @@ static const struct xdg_toplevel_listener xdgToplevelListener = { #pragma endregion #pragma region Zxdg-Decoraion_Manager-V1 -#ifdef PAL_HAS_WAYLAND +#if PAL_HAS_WAYLAND struct xdg_toplevel; struct zxdg_decoration_manager_v1; @@ -1583,7 +1583,7 @@ const struct wl_interface zxdg_toplevel_decoration_v1_interface = { #pragma endregion #pragma region Zwp-Pointer-Constraints -#ifdef PAL_HAS_WAYLAND +#if PAL_HAS_WAYLAND struct zwp_confined_pointer_v1; struct zwp_pointer_constraints_v1; @@ -1726,6 +1726,7 @@ typedef struct { bool (*isWindowVisible)(PalWindow*); PalWindow* (*getFocusWindow)(); PalWindowHandleInfo (*getWindowHandleInfo)(PalWindow*); + PalWindowHandleInfoEx (*getWindowHandleInfoEx)(PalWindow*); PalResult (*setWindowOpacity)(PalWindow*, float); PalResult (*setWindowStyle)(PalWindow*, PalWindowStyle); PalResult (*setWindowTitle)(PalWindow*, const char*); @@ -2169,6 +2170,7 @@ static void xCheckFeatures() features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; + features2 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; s_Video.features = features; s_Video.features2 = features2; @@ -4511,6 +4513,14 @@ PalWindowHandleInfo xGetWindowHandleInfo(PalWindow* window) return info; } +PalWindowHandleInfoEx xGetWindowHandleInfoEx(PalWindow* w) +{ + PalWindowHandleInfoEx info = {0}; + info.nativeDisplay = (void*)s_X11.display; + info.nativeWindow = (void*)w; + return info; +} + static PalResult xSetWindowOpacity( PalWindow* window, float opacity) @@ -5032,6 +5042,7 @@ static Backend s_XBackend = { .isWindowVisible = xIsWindowVisible, .getFocusWindow = xGetFocusWindow, .getWindowHandleInfo = xGetWindowHandleInfo, + .getWindowHandleInfoEx = xGetWindowHandleInfoEx, .setWindowOpacity = xSetWindowOpacity, .setWindowStyle = xSetWindowStyle, .setWindowTitle = xSetWindowTitle, @@ -5062,7 +5073,7 @@ static Backend s_XBackend = { // ================================================== #pragma region Wayland API -#ifdef PAL_HAS_WAYLAND +#if PAL_HAS_WAYLAND static int createShmFile(Uint64 size) { @@ -5141,6 +5152,8 @@ static void wlGlobalHandle( features |= PAL_VIDEO_FEATURE_WINDOW_SET_SIZE; features |= PAL_VIDEO_FEATURE_WINDOW_SET_STATE; features |= PAL_VIDEO_FEATURE_BORDERLESS_WINDOW; + + s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; s_Video.features = features; } @@ -5973,6 +5986,21 @@ PalWindowHandleInfo wlGetWindowHandleInfo(PalWindow* window) return info; } +PalWindowHandleInfoEx wlGetWindowHandleInfoEx(PalWindow* window) +{ + PalWindowHandleInfoEx info = {0}; + WindowData* data = findWindowData(window); + info.nativeDisplay = (void*)s_Wl.display; + info.nativeWindow = (void*)window; + + if (data) { + info.nativeHandle1 = data->xdgSurface; + info.nativeHandle2 = data->xdgToplevel; + } + + return info; +} + PalResult wlSetWindowOpacity( PalWindow* window, float opacity) @@ -6141,6 +6169,7 @@ static Backend s_wlBackend = { .isWindowVisible = wlIsWindowVisible, .getFocusWindow = wlGetFocusWindow, .getWindowHandleInfo = wlGetWindowHandleInfo, + .getWindowHandleInfoEx = wlGetWindowHandleInfoEx, .setWindowOpacity = wlSetWindowOpacity, .setWindowStyle = wlSetWindowStyle, .setWindowTitle = wlSetWindowTitle, @@ -6218,12 +6247,23 @@ PalResult PAL_CALL palInitVideo( s_Video.backend = &s_XBackend; } else { - PalResult ret = wlInitVideo(); +#if PAL_HAS_WAYLAND + PalResult ret = wlInitVideo(); + if (ret != PAL_RESULT_SUCCESS) { + // fallback to X11 + wlShutdownVideo(); + ret = xInitVideo(); if (ret != PAL_RESULT_SUCCESS) { return ret; } + s_Video.backend = &s_XBackend; + + } else { s_Video.backend = &s_wlBackend; } + +#endif // PAL_HAS_WAYLAND + } // we load EGL as well s_Egl.handle = dlopen("libEGL.so", RTLD_LAZY); @@ -6753,6 +6793,13 @@ PalWindowHandleInfo PAL_CALL palGetWindowHandleInfo(PalWindow* window) } } +PalWindowHandleInfoEx PAL_CALL palGetWindowHandleInfoEx(PalWindow* w) +{ + if (s_Video.initialized) { + return s_Video.backend->getWindowHandleInfoEx(w); + } +} + PalResult PAL_CALL palSetWindowOpacity( PalWindow* window, float opacity) diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index b51cb9b..c61884b 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -1213,6 +1213,7 @@ PalResult PAL_CALL palInitVideo( s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; s_Video.features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE; + s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; s_Video.initialized = true; s_Video.allocator = allocator; diff --git a/tests/attach_window_test.c b/tests/attach_window_test.c index e0c238e..13a3904 100644 --- a/tests/attach_window_test.c +++ b/tests/attach_window_test.c @@ -211,6 +211,7 @@ bool attachWindowTest() palLog(nullptr, "==========================================="); palLog(nullptr, "Attach Window Test"); palLog(nullptr, "Press A to attach and D to detach window"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -261,6 +262,11 @@ bool attachWindowTest() PAL_EVENT_WINDOW_MOVE, PAL_DISPATCH_POLL); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // we listen for key release events to attach and detach the window palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYUP, PAL_DISPATCH_POLL); @@ -324,6 +330,15 @@ bool attachWindowTest() break; } + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } + case PAL_EVENT_KEYUP: { // we detach the window after keyup // since if the window is detached, diff --git a/tests/char_event_test.c b/tests/char_event_test.c index 1bdc5ed..6e8ed38 100644 --- a/tests/char_event_test.c +++ b/tests/char_event_test.c @@ -7,6 +7,7 @@ bool charEventTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Character Event Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -57,6 +58,11 @@ bool charEventTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYCHAR, PAL_DISPATCH_POLL); bool running = true; @@ -88,6 +94,15 @@ bool charEventTest() } break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } diff --git a/tests/cursor_test.c b/tests/cursor_test.c index 1b51e39..5461c3e 100644 --- a/tests/cursor_test.c +++ b/tests/cursor_test.c @@ -7,6 +7,7 @@ bool cursorTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Cursor Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -45,6 +46,15 @@ bool cursorTest() return false; } + // check for support + PalVideoFeatures2 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR)) { + palLog(nullptr, "Seting cursors feature not supported"); + palDestroyEventDriver(eventDriver); + palShutdownVideo(); + return false; + } + // simple checkerboard RGBA pixel buffer // every block contains 64 pixels Uint8 pixels[32 * 32 * 4]; // size is 32 and we have 4 channles @@ -103,6 +113,11 @@ bool cursorTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); // polling + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // set the cursor palSetWindowCursor(window, cursor); @@ -118,6 +133,15 @@ bool cursorTest() running = false; break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } } diff --git a/tests/icon_test.c b/tests/icon_test.c index 13590df..81641c9 100644 --- a/tests/icon_test.c +++ b/tests/icon_test.c @@ -7,6 +7,7 @@ bool iconTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Icon Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -45,6 +46,15 @@ bool iconTest() return false; } + // check for support + PalVideoFeatures2 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE_WINDOW_SET_ICON)) { + palLog(nullptr, "Seting icons feature not supported"); + palDestroyEventDriver(eventDriver); + palShutdownVideo(); + return false; + } + // simple checkerboard RGBA pixel buffer // every block contains 64 pixels Uint8 pixels[32 * 32 * 4]; // size is 32 and we have 4 channles @@ -101,6 +111,11 @@ bool iconTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); // polling + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // set the icon result = palSetWindowIcon(window, icon); if (result != PAL_RESULT_SUCCESS) { @@ -121,6 +136,15 @@ bool iconTest() running = false; break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } } diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 9f806de..588cc0e 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -270,6 +270,8 @@ static const char* dispatchString = "Poll Mode"; static const char* dispatchString = "Callback Mode"; #endif // DISPATCH_MODE_POLL +static bool s_Running = false; + // inline helpers static inline void onKeydown(const PalEvent* event) { @@ -286,6 +288,10 @@ static inline void onKeydown(const PalEvent* event) dispatchString, keyName, scancodeName); + + if (keycode == PAL_KEYCODE_ESCAPE) { + s_Running = false; + } } static inline void onKeyrepeat(const PalEvent* event) @@ -399,6 +405,7 @@ bool inputWindowTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Input Window Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -469,8 +476,8 @@ bool inputWindowTest() palSetEventDispatchMode(eventDriver, e, dispatchMode); } - running = true; - while (running) { + s_Running = true; + while (s_Running) { // update the video system to push video events palUpdateVideo(); @@ -478,7 +485,7 @@ bool inputWindowTest() while (palPollEvent(eventDriver, &event)) { switch (event.type) { case PAL_EVENT_WINDOW_CLOSE: { - running = false; + s_Running = false; break; } diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c new file mode 100644 index 0000000..b6fdc43 --- /dev/null +++ b/tests/native_integration_test.c @@ -0,0 +1,380 @@ + +#include "pal/pal_video.h" +#include "tests.h" + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +// set unicode +#ifndef UNICODE +#define UNICODE +#endif // UNICODE + +#include + +#elif defined(__linux__) +#include +#include +#include +#include +#include + +// wayland is optional and might not be supported +// so we stick to typedefs + +// X11 typedefs +typedef Atom (*XInternAtomFn)( + Display*, + _Xconst char*, + Bool); + +typedef int (*XChangePropertyFn)( + Display*, + Window, + Atom, + Atom, + int, + int, + _Xconst unsigned char*, + int); + +typedef Status (*XGetWMNameFn)( + Display*, + Window, + XTextProperty*); + +typedef int (*XStoreNameFn)( + Display*, + Window, + _Xconst char*); + +typedef int (*XGetWindowPropertyFn)( + Display*, + Window, + Atom, + long, + long, + Bool, + Atom, + Atom*, + int*, + unsigned long*, + unsigned long*, + unsigned char**); + +typedef int (*XFlushFn)(Display*); +typedef int (*XFreeFn)(void*); + +// Wayland typedefs + +static XInternAtomFn s_XInternAtom; +static XChangePropertyFn s_XChangeProperty; +static XGetWMNameFn s_XGetWMName; +static XStoreNameFn s_XStoreName; +static XGetWindowPropertyFn s_XGetWindowProperty; +static XFlushFn s_XFlush; +static XFreeFn s_XFree; + +static Atom s_NET_WM_NAME; +static Atom s_UTF8_STRING; + +static bool s_OnWayland = false; +static void* s_X11Lib; + +static void* s_WaylandLib; + +#endif // _WIN32 + +static char s_TitleBuffer[32]; + +void setWindowTitleX11(PalWindowHandleInfoEx* windowInfo) +{ +#ifdef __linux__ + // load the procs + s_X11Lib = dlopen("libX11.so", RTLD_LAZY); + if (!s_X11Lib) { + return; + } + + // clang-format off + s_XInternAtom = (XInternAtomFn)dlsym( + s_X11Lib, + "XInternAtom"); + + s_XChangeProperty = (XChangePropertyFn)dlsym( + s_X11Lib, + "XChangeProperty"); + + s_XGetWMName = (XGetWMNameFn)dlsym( + s_X11Lib, + "XGetWMName"); + + s_XStoreName = (XStoreNameFn)dlsym( + s_X11Lib, + "XStoreName"); + + s_XGetWindowProperty = (XGetWindowPropertyFn)dlsym( + s_X11Lib, + "XGetWindowProperty"); + + s_XFlush = (XFlushFn)dlsym( + s_X11Lib, + "XFlush"); + + s_XFree = (XFreeFn)dlsym( + s_X11Lib, + "XFree"); + + // clang-format on + + Display* display = (Display*)windowInfo->nativeDisplay; + Window window = (Window)(UintPtr)windowInfo->nativeWindow; + + s_NET_WM_NAME = s_XInternAtom(display, "_NET_WM_NAME", False); + s_UTF8_STRING = s_XInternAtom(display, "UTF8_STRING", False); + + const char* title = "Hello from native X11 API"; + if (s_NET_WM_NAME) { + s_XChangeProperty( + display, + window, + s_NET_WM_NAME, + s_UTF8_STRING, + 8, // unsigned char + PropModeReplace, + title, + strlen(title)); + + } else { + s_XStoreName(display, window, title); + } + + s_XFlush(display); +#endif // __linux__ +} + +void getWindowTitleX11(PalWindowHandleInfoEx* windowInfo) +{ +#ifdef __linux__ + Display* display = (Display*)windowInfo->nativeDisplay; + Window window = (Window)(UintPtr)windowInfo->nativeWindow; + + if (s_NET_WM_NAME) { + Atom type; + int format; + unsigned long count, bytesAfter; + unsigned char* prop = nullptr; + s_XGetWindowProperty( + display, + window, + s_NET_WM_NAME, + 0, + (~0L), + False, + s_UTF8_STRING, + &type, + &format, + &count, + &bytesAfter, + &prop); + + strncpy(s_TitleBuffer, (const char*)prop, sizeof(s_TitleBuffer)); + s_XFree(prop); + + } else { + XTextProperty text; + s_XGetWMName(display, window, &text); + strncpy(s_TitleBuffer, (const char*)text.value, sizeof(s_TitleBuffer)); + s_XFree(text.value); + } + + // free Xlib since we loaded dynamically + dlclose(s_X11Lib); + +#endif // __linux__ +} + +void setWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) +{ + #ifdef __linux__ + + #endif // __linux__ +} + +void getWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) +{ + #ifdef __linux__ + + #endif // __linux__ +} + +void setWindowTitleWin32(PalWindowHandleInfoEx* windowInfo); +void getWindowTitleWin32(PalWindowHandleInfoEx* windowInfo); + +void setWindowTitle(PalWindowHandleInfoEx* windowInfo) +{ +#ifdef _WIN32 +#elif defined(__linux__) + // get the active session + const char* session = getenv("XDG_SESSION_TYPE"); + if (session) { + if (strcmp(session, "wayland") == 0) { + s_OnWayland = true; + } else { + s_OnWayland = false; + } + } + + if (s_OnWayland) { + setWindowTitleWayland(windowInfo); + } else { + setWindowTitleX11(windowInfo); + } +#endif // _WIN32 +} + +void getWindowTitle(PalWindowHandleInfoEx* windowInfo) +{ +#ifdef _WIN32 +#elif defined(__linux__) + if (s_OnWayland) { + getWindowTitleWayland(windowInfo); + } else { + getWindowTitleX11(windowInfo); + } +#endif // _WIN32 +} + +bool nativeIntegrationTest() +{ + palLog(nullptr, ""); + palLog(nullptr, "==========================================="); + palLog(nullptr, "Native Integration Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); + palLog(nullptr, "==========================================="); + palLog(nullptr, ""); + + PalResult result; + + // event driver + PalEventDriver* eventDriver = nullptr; + PalEventDriverCreateInfo eventDriverCreateInfo = {0}; + + // create the event driver + result = palCreateEventDriver(&eventDriverCreateInfo, &eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to create event driver: %s", error); + return false; + } + + // initialize the video system. We pass the event driver to recieve video + // related events the video system does not copy the event driver, it must + // be valid till the video system is shutdown + result = palInitVideo(nullptr, eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + // check for support + PalVideoFeatures2 features = palGetVideoFeaturesEx(); + + PalWindow* window = nullptr; + PalWindowCreateInfo createInfo = {0}; + createInfo.monitor = nullptr; // use default monitor + createInfo.height = 480; + createInfo.width = 640; + createInfo.show = true; + createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; + createInfo.title = "Native Integration Test"; + + // check if we support decorated windows (title bar, close etc) + if (!(features & PAL_VIDEO_FEATURE_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + + result = palCreateWindow(&createInfo, &window); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to create window: %s", error); + return false; + } + + // we set window close to poll + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_CLOSE, + PAL_DISPATCH_POLL); + + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + + // set the window title using native APIs + PalWindowHandleInfoEx windowInfo = {0}; + windowInfo = palGetWindowHandleInfoEx(window); + + palLog(nullptr, "Window title: %s", createInfo.title); + palLog(nullptr, "Setting window title with native API"); + setWindowTitle(&windowInfo); + + palLog(nullptr, "Getting window title with PAL API"); + palGetWindowTitle(window, sizeof(s_TitleBuffer), nullptr, s_TitleBuffer); + palLog(nullptr, "Window title: %s", s_TitleBuffer); + + // set the title with PAL and retreive it with the native API + palLog(nullptr, "Setting window title with PAL API"); + palSetWindowTitle(window, "Hello from PAL API"); + + palLog(nullptr, "Getting window title with native API"); + getWindowTitle(&windowInfo); + palLog(nullptr, "Window title: %s", s_TitleBuffer); + + bool running = true; + while (running) { + // update the video system to push video events + palUpdateVideo(); + + PalEvent event; + while (palPollEvent(eventDriver, &event)) { + switch (event.type) { + case PAL_EVENT_WINDOW_CLOSE: { + running = false; + break; + } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } + } + } + } + + // destroy the window + palDestroyWindow(window); + + // shutdown the video system + palShutdownVideo(); + + // destroy the event driver + palDestroyEventDriver(eventDriver); + + return true; + +} \ No newline at end of file diff --git a/tests/system_cursor_test.c b/tests/system_cursor_test.c index 6cf0aa3..cda5991 100644 --- a/tests/system_cursor_test.c +++ b/tests/system_cursor_test.c @@ -7,6 +7,7 @@ bool systemCursorTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "System Cursor Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -74,6 +75,11 @@ bool systemCursorTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); // polling + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // set the cursor palSetWindowCursor(window, cursor); @@ -89,6 +95,15 @@ bool systemCursorTest() running = false; break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } } diff --git a/tests/tests.h b/tests/tests.h index 867fbee..3711336 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -38,6 +38,7 @@ bool inputWindowTest(); bool systemCursorTest(); bool attachWindowTest(); bool charEventTest(); +bool nativeIntegrationTest(); // opengl test bool openglTest(); diff --git a/tests/tests.lua b/tests/tests.lua index 608f5f6..e3fe863 100644 --- a/tests/tests.lua +++ b/tests/tests.lua @@ -41,7 +41,8 @@ project "tests" "input_window_test.c", "system_cursor_test.c", "attach_window_test.c", - "char_event_test.c" + "char_event_test.c", + "native_integration_test.c" } end diff --git a/tests/tests_main.c b/tests/tests_main.c index 8e434ab..f9fa8b4 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -29,13 +29,14 @@ int main(int argc, char** argv) // registerTest("Video Test", videoTest); // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); + // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); + registerTest("Native Integration Test", nativeIntegrationTest); #endif // PAL_HAS_VIDEO #if PAL_HAS_OPENGL diff --git a/tests/window_test.c b/tests/window_test.c index 5e804e0..6a6111b 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -155,6 +155,7 @@ bool windowTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Window Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -281,6 +282,11 @@ bool windowTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // we set callback mode for modal begin and end. Since we want to capture // that instantly palSetEventDispatchMode( @@ -340,6 +346,15 @@ bool windowTest() onMonitorList(&event); break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } From 0a71db10a08179aeaf2bc77eedba8cfad329934b Mon Sep 17 00:00:00 2001 From: nichcode Date: Wed, 5 Nov 2025 14:31:21 +0000 Subject: [PATCH 05/51] native integration test walyand --- CHANGELOG.md | 2 + include/pal/pal_video.h | 4 ++ src/video/pal_video_linux.c | 11 ++++- tests/native_integration_test.c | 71 +++++++++++++++++++++++++++++---- 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d209ecd..4937b4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,10 +73,12 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr ## [1.3.0] - 2025-11- ### Features +- **Video:** Added Wayland-based backend support. - **Video:** Added **palGetVideoFeaturesEx()** to check supported features. - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. ### Tests +- Added native integration example: demonstrating **Native API Integration with PAL API**. see **native_integration_test.c**. ### Notes - No API or ABI changes - existing code remains compatible. diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index c29ed7d..7535505 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -1444,6 +1444,10 @@ PAL_API PalWindowHandleInfo PAL_CALL palGetWindowHandleInfo(PalWindow* window); * @brief Get the native handles of the provided window. * * The video system must be initialized before this call. + * + * On Wayland: `PalWindowHandleInfoEx::nativeHandle1` and + * `PalWindowHandleInfoEx::nativeHandle2` are xdg_surface and + * xdg_toplevel respectively. * * @param[in] window Pointer to the window. * diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index ccd469e..00d076a 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1173,7 +1173,6 @@ static void xdgToplevelClose( void* data, struct xdg_toplevel* toplevel) { - printf("hello"); (void)data; (void)toplevel; } @@ -5856,6 +5855,9 @@ PalResult wlCreateWindow( wlSurfaceCommit(surface); } + data->buffer = buffer; + s_Wl.displayFlush(s_Wl.display); + *outWindow = data->window; return PAL_RESULT_SUCCESS; } @@ -6019,6 +6021,13 @@ PalResult wlSetWindowTitle( PalWindow* window, const char* title) { + WindowData* data = findWindowData(window); + if (!data) { + return PAL_RESULT_INVALID_WINDOW; + } + + xdgToplevelSetTitle(data->xdgToplevel, title); + s_Wl.displayFlush(s_Wl.display); return PAL_RESULT_SUCCESS; } diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index b6fdc43..23bd50f 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -72,6 +72,37 @@ typedef int (*XFlushFn)(Display*); typedef int (*XFreeFn)(void*); // Wayland typedefs +struct wl_display; +struct wl_interface; +struct xdg_toplevel; + +typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, + uint32_t, ...); + +typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); +typedef int (*wl_display_flush_fn)(struct wl_display*); + +static wl_proxy_marshal_flags_fn s_wl_proxy_marshal_flags; +static wl_proxy_get_version_fn s_wl_proxy_get_version; +static wl_display_flush_fn s_wl_display_flush; + +static inline void xdgToplevelSetTitle( + struct xdg_toplevel *xdg_toplevel, + const char *title) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) xdg_toplevel, + 2, // XDG_TOPLEVEL_SET_TITLE + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) xdg_toplevel), + 0, + title); +} static XInternAtomFn s_XInternAtom; static XChangePropertyFn s_XChangeProperty; @@ -184,13 +215,13 @@ void getWindowTitleX11(PalWindowHandleInfoEx* windowInfo) &bytesAfter, &prop); - strncpy(s_TitleBuffer, (const char*)prop, sizeof(s_TitleBuffer)); + strcpy(s_TitleBuffer, (const char*)prop); s_XFree(prop); } else { XTextProperty text; s_XGetWMName(display, window, &text); - strncpy(s_TitleBuffer, (const char*)text.value, sizeof(s_TitleBuffer)); + strcpy(s_TitleBuffer, (const char*)text.value); s_XFree(text.value); } @@ -202,16 +233,42 @@ void getWindowTitleX11(PalWindowHandleInfoEx* windowInfo) void setWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) { - #ifdef __linux__ +#ifdef __linux__ + s_WaylandLib = dlopen("libwayland-client.so.0", RTLD_LAZY); + if (!s_WaylandLib) { + return; + } + + s_wl_proxy_marshal_flags = (wl_proxy_marshal_flags_fn)dlsym( + s_WaylandLib, + "wl_proxy_marshal_flags"); + + s_wl_proxy_get_version = (wl_proxy_get_version_fn)dlsym( + s_WaylandLib, + "wl_proxy_get_version"); - #endif // __linux__ + s_wl_display_flush = (wl_display_flush_fn)dlsym( + s_WaylandLib, + "wl_display_flush"); + + struct xdg_toplevel* toplevel = nullptr; + struct wl_display* display = nullptr; + display = (struct wl_display*)windowInfo->nativeDisplay; + toplevel = (struct xdg_toplevel*)windowInfo->nativeHandle2; + + xdgToplevelSetTitle(toplevel, "Hello from native Wayland API"); + s_wl_display_flush(display); + +#endif // __linux__ } void getWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) { - #ifdef __linux__ - - #endif // __linux__ +#ifdef __linux__ + // wayland does not support getting window title + // so we just return the title we set through wayland + dlclose(s_WaylandLib); +#endif // __linux__ } void setWindowTitleWin32(PalWindowHandleInfoEx* windowInfo); From 70f4ed3859221cedfbdf53f7dcfef1efb935c5d6 Mon Sep 17 00:00:00 2001 From: nichcode Date: Wed, 5 Nov 2025 18:31:18 +0000 Subject: [PATCH 06/51] wayaland events --- include/pal/pal_core.h | 2 +- include/pal/pal_event.h | 2 +- include/pal/pal_opengl.h | 2 +- include/pal/pal_system.h | 2 +- include/pal/pal_thread.h | 2 +- include/pal/pal_video.h | 2 +- src/opengl/pal_opengl_linux.c | 2 +- src/opengl/pal_opengl_win32.c | 2 +- src/pal_core.c | 2 +- src/pal_event.c | 2 +- src/system/pal_system_linux.c | 2 +- src/system/pal_system_win32.c | 2 +- src/thread/pal_thread_linux.c | 2 +- src/thread/pal_thread_win32.c | 2 +- src/video/pal_video_linux.c | 58 +++++++++++++++++++++-------------- src/video/pal_video_win32.c | 2 +- 16 files changed, 50 insertions(+), 38 deletions(-) diff --git a/include/pal/pal_core.h b/include/pal/pal_core.h index b281546..bc02a81 100644 --- a/include/pal/pal_core.h +++ b/include/pal/pal_core.h @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/pal/pal_event.h b/include/pal/pal_event.h index ff422c0..b2edc93 100644 --- a/include/pal/pal_event.h +++ b/include/pal/pal_event.h @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/pal/pal_opengl.h b/include/pal/pal_opengl.h index e31ca92..08183ea 100644 --- a/include/pal/pal_opengl.h +++ b/include/pal/pal_opengl.h @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/pal/pal_system.h b/include/pal/pal_system.h index c8d71bf..9e67153 100644 --- a/include/pal/pal_system.h +++ b/include/pal/pal_system.h @@ -1,6 +1,6 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/pal/pal_thread.h b/include/pal/pal_thread.h index c18d933..40000a7 100644 --- a/include/pal/pal_thread.h +++ b/include/pal/pal_thread.h @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 7535505..9a26c00 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index 46953f4..65c3b27 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/opengl/pal_opengl_win32.c b/src/opengl/pal_opengl_win32.c index 91e9626..94f43e6 100644 --- a/src/opengl/pal_opengl_win32.c +++ b/src/opengl/pal_opengl_win32.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/pal_core.c b/src/pal_core.c index 5318913..ed2015b 100644 --- a/src/pal_core.c +++ b/src/pal_core.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/pal_event.c b/src/pal_event.c index ddae41d..b608dff 100644 --- a/src/pal_event.c +++ b/src/pal_event.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/system/pal_system_linux.c b/src/system/pal_system_linux.c index 6d9e731..c108f71 100644 --- a/src/system/pal_system_linux.c +++ b/src/system/pal_system_linux.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/system/pal_system_win32.c b/src/system/pal_system_win32.c index 58c5a9f..2e2a66e 100644 --- a/src/system/pal_system_win32.c +++ b/src/system/pal_system_win32.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/thread/pal_thread_linux.c b/src/thread/pal_thread_linux.c index b20a669..c3e5fbe 100644 --- a/src/thread/pal_thread_linux.c +++ b/src/thread/pal_thread_linux.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/thread/pal_thread_win32.c b/src/thread/pal_thread_win32.c index e55a3c2..53de896 100644 --- a/src/thread/pal_thread_win32.c +++ b/src/thread/pal_thread_win32.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 00d076a..9434410 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -53,6 +53,7 @@ freely, subject to the following restrictions: #include #include #include +#include #endif // PAL_HAS_WAYLAND // ================================================== @@ -773,6 +774,7 @@ typedef int (*wl_display_dispatch_pending_fn)(struct wl_display*); typedef int (*wl_display_flush_fn)(struct wl_display*); typedef int (*wl_display_prepare_read_fn)(struct wl_display*); typedef int (*wl_display_read_events_fn)(struct wl_display*); +typedef int (*wl_display_get_fd_fn)(struct wl_display*); typedef struct { bool checkFeatures; @@ -813,6 +815,7 @@ typedef struct { wl_display_flush_fn displayFlush; wl_display_prepare_read_fn prepareRead; wl_display_read_events_fn readEvents; + wl_display_get_fd_fn displayGetFd; } Wayland; typedef struct { @@ -1173,6 +1176,8 @@ static void xdgToplevelClose( void* data, struct xdg_toplevel* toplevel) { + // TODO: push window closee event + (void)data; (void)toplevel; } @@ -1456,7 +1461,9 @@ static const struct xdg_surface_listener xdgSurfaceListener = { static const struct xdg_toplevel_listener xdgToplevelListener = { .configure = xdgToplevelConfigure, - .close = xdgToplevelClose + .close = xdgToplevelClose, + .configure_bounds = nullptr, + .wm_capabilities = nullptr }; #endif // PAL_HAS_WAYLAND @@ -5171,6 +5178,8 @@ static void wlGlobalHandle( 1); xdgWmBaseAddListener(s_Wl.xdgBase, &wmBaseListener, nullptr); + s_Wl.displayFlush(s_Wl.display); + } else if (strcmp(interface, "wl_shm") == 0) { s_Wl.shm = wlRegistryBind( registry, @@ -5434,6 +5443,10 @@ PalResult wlInitVideo() s_Wl.handle, "wl_display_read_events"); + s_Wl.displayGetFd = (wl_display_get_fd_fn)dlsym( + s_Wl.handle, + "wl_display_get_fd"); + // initialize wayland s_Wl.modesPhase = false; s_Wl.checkFeatures = true; @@ -5446,6 +5459,12 @@ PalResult wlInitVideo() wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); s_Wl.displayRoundtrip(s_Wl.display); + // s_Wl.displayRoundtrip(s_Wl.display); + + if (!s_Wl.compositor || !s_Wl.xdgBase || !s_Wl.shm) { + return PAL_RESULT_PLATFORM_FAILURE; + } + s_Wl.checkFeatures = false; return PAL_RESULT_SUCCESS; } @@ -5465,19 +5484,18 @@ void wlShutdownVideo() } void wlUpdateVideo() -{ - while (s_Wl.prepareRead(s_Wl.display) != 0) { - s_Wl.dispatchPending(s_Wl.display); - s_Wl.displayFlush(s_Wl.display); - if (s_Wl.readEvents(s_Wl.display) == 0) { - s_Wl.dispatchPending(s_Wl.display); - } - } - - +{ + s_Wl.dispatchPending(s_Wl.display); + s_Wl.displayFlush(s_Wl.display); - // s_Wl.dispatchPending(s_Wl.display); - // s_Wl.displayFlush(s_Wl.display); + // check for new messages + int fd = s_Wl.displayGetFd(s_Wl.display); + struct pollfd pfd = { fd, POLLIN, 0 }; + poll(&pfd, 1, 0); + + if (pfd.revents & POLLIN) { + s_Wl.displayDispatch(s_Wl.display); + } } PalResult wlSetFBConfig( @@ -5727,11 +5745,6 @@ PalResult wlCreateWindow( struct xdg_surface* xdgSurface = nullptr; struct xdg_toplevel* xdgToplevel = nullptr; - // check if we have xdg and a compositor - if (!s_Wl.xdgBase || !s_Wl.compositor) { - return PAL_RESULT_PLATFORM_FAILURE; - } - if (info->style & PAL_WINDOW_STYLE_TOPMOST) { return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; } @@ -5842,10 +5855,9 @@ PalResult wlCreateWindow( return PAL_RESULT_PLATFORM_FAILURE; } - struct wl_surface* wl_surface = (struct wl_surface*)data->window; - wlSurfaceAttach(wl_surface, buffer, 0, 0); - wlSurfaceDamageBuffer(wl_surface, 0, 0, data->w, data->h); - wlSurfaceCommit(wl_surface); + wlSurfaceAttach(surface, buffer, 0, 0); + wlSurfaceDamageBuffer(surface, 0, 0, data->w, data->h); + wlSurfaceCommit(surface); s_Wl.displayRoundtrip(s_Wl.display); // resizable diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index c61884b..4585685 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -1,7 +1,7 @@ /** -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages From 7bb2db02c12b97785c2d25e8c6a58f1a8a26151e Mon Sep 17 00:00:00 2001 From: nichcode Date: Wed, 5 Nov 2025 21:31:09 +0000 Subject: [PATCH 07/51] window close event --- src/video/pal_video_linux.c | 201 ++++++++++++++++++++---------------- tests/tests_main.c | 4 +- 2 files changed, 112 insertions(+), 93 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 9434410..268478c 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -184,6 +184,82 @@ typedef struct { eglGetConfigsFn eglGetConfigs; } EGL; +typedef struct { + // clang-format off + void (*shutdownVideo)(); + void (*updateVideo)(); + PalResult (*enumerateMonitors)(Int32*, PalMonitor**); + PalResult (*getPrimaryMonitor)(PalMonitor**); + PalResult (*getMonitorInfo)(PalMonitor*, PalMonitorInfo*); + PalResult (*enumerateMonitorModes)(PalMonitor*, Int32*, PalMonitorMode*); + PalResult (*getCurrentMonitorMode)(PalMonitor*, PalMonitorMode*); + PalResult (*setMonitorMode)(PalMonitor*, PalMonitorMode*); + PalResult (*validateMonitorMode)(PalMonitor*, PalMonitorMode*); + PalResult (*setMonitorOrientation)(PalMonitor*, PalOrientation); + + PalResult (*createWindow)(const PalWindowCreateInfo*, PalWindow**); + void (*destroyWindow)(PalWindow*); + PalResult (*maximizeWindow)(PalWindow*); + PalResult (*minimizeWindow)(PalWindow*); + PalResult (*restoreWindow)(PalWindow*); + PalResult (*showWindow)(PalWindow*); + PalResult (*hideWindow)(PalWindow*); + PalResult (*flashWindow)(PalWindow*, const PalFlashInfo*); + PalResult (*getWindowStyle)(PalWindow*, PalWindowStyle*); + PalResult (*getWindowMonitor)(PalWindow*, PalMonitor**); + PalResult (*getWindowTitle)(PalWindow*, Uint64, Uint64*, char*); + PalResult (*getWindowPos)(PalWindow*, Int32*, Int32*); + PalResult (*getWindowSize)(PalWindow*, Uint32*, Uint32*); + PalResult (*getWindowState)(PalWindow*, PalWindowState*); + bool (*isWindowVisible)(PalWindow*); + PalWindow* (*getFocusWindow)(); + PalWindowHandleInfo (*getWindowHandleInfo)(PalWindow*); + PalWindowHandleInfoEx (*getWindowHandleInfoEx)(PalWindow*); + PalResult (*setWindowOpacity)(PalWindow*, float); + PalResult (*setWindowStyle)(PalWindow*, PalWindowStyle); + PalResult (*setWindowTitle)(PalWindow*, const char*); + PalResult (*setWindowPos)(PalWindow*, Int32, Int32); + PalResult (*setWindowSize)(PalWindow*, Uint32, Uint32); + PalResult (*setFocusWindow)(PalWindow*); + + PalResult (*createIcon)(const PalIconCreateInfo*, PalIcon**); + void (*destroyIcon)(PalIcon*); + PalResult (*setWindowIcon)(PalWindow*, PalIcon*); + + PalResult (*createCursor)(const PalCursorCreateInfo*, PalCursor**); + PalResult (*createCursorFrom)(PalCursorType, PalCursor**); + void (*destroyCursor)(PalCursor*); + void (*showCursor)(bool); + PalResult (*clipCursor)(PalWindow*, bool); + PalResult (*getCursorPos)(PalWindow*, Int32*, Int32*); + PalResult (*setCursorPos)(PalWindow*, Int32, Int32); + PalResult (*setWindowCursor)(PalWindow*, PalCursor*); + + PalResult (*attachWindow)(void*, PalWindow**); + PalResult (*detachWindow)(PalWindow*, void**); + // clang-format off +} Backend; + +typedef struct { + bool initialized; + Int32 maxWindowData; + Int32 maxMonitorData; + Int32 pixelFormat; + PalVideoFeatures features; + PalVideoFeatures2 features2; + const PalAllocator* allocator; + PalEventDriver* eventDriver; + const Backend* backend; + WindowData* windowData; + MonitorData* monitorData; + const char* className; +} VideoLinux; + +static VideoLinux s_Video = {0}; +static Mouse s_Mouse = {0}; +static Keyboard s_Keyboard = {0}; +static EGL s_Egl; + // ================================================== // X11 Typedefs, enums and structs // ================================================== @@ -775,6 +851,7 @@ typedef int (*wl_display_flush_fn)(struct wl_display*); typedef int (*wl_display_prepare_read_fn)(struct wl_display*); typedef int (*wl_display_read_events_fn)(struct wl_display*); typedef int (*wl_display_get_fd_fn)(struct wl_display*); +typedef void (*wl_display_cancel_read_fn)(struct wl_display*); typedef struct { bool checkFeatures; @@ -816,6 +893,7 @@ typedef struct { wl_display_prepare_read_fn prepareRead; wl_display_read_events_fn readEvents; wl_display_get_fd_fn displayGetFd; + wl_display_cancel_read_fn cancelRead; } Wayland; typedef struct { @@ -1176,10 +1254,17 @@ static void xdgToplevelClose( void* data, struct xdg_toplevel* toplevel) { - // TODO: push window closee event + WindowData* winData = (WindowData*)data; - (void)data; - (void)toplevel; + // TODO: push window close event + PalEventType type = PAL_EVENT_WINDOW_CLOSE; + PalDispatchMode mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } } static inline int xdgSurfaceAddListener( @@ -1700,83 +1785,7 @@ static void setupZwpPointerProtocol() } #endif // PAL_HAS_WAYLAND -#pragma endregion - -typedef struct { - // clang-format off - void (*shutdownVideo)(); - void (*updateVideo)(); - PalResult (*enumerateMonitors)(Int32*, PalMonitor**); - PalResult (*getPrimaryMonitor)(PalMonitor**); - PalResult (*getMonitorInfo)(PalMonitor*, PalMonitorInfo*); - PalResult (*enumerateMonitorModes)(PalMonitor*, Int32*, PalMonitorMode*); - PalResult (*getCurrentMonitorMode)(PalMonitor*, PalMonitorMode*); - PalResult (*setMonitorMode)(PalMonitor*, PalMonitorMode*); - PalResult (*validateMonitorMode)(PalMonitor*, PalMonitorMode*); - PalResult (*setMonitorOrientation)(PalMonitor*, PalOrientation); - - PalResult (*createWindow)(const PalWindowCreateInfo*, PalWindow**); - void (*destroyWindow)(PalWindow*); - PalResult (*maximizeWindow)(PalWindow*); - PalResult (*minimizeWindow)(PalWindow*); - PalResult (*restoreWindow)(PalWindow*); - PalResult (*showWindow)(PalWindow*); - PalResult (*hideWindow)(PalWindow*); - PalResult (*flashWindow)(PalWindow*, const PalFlashInfo*); - PalResult (*getWindowStyle)(PalWindow*, PalWindowStyle*); - PalResult (*getWindowMonitor)(PalWindow*, PalMonitor**); - PalResult (*getWindowTitle)(PalWindow*, Uint64, Uint64*, char*); - PalResult (*getWindowPos)(PalWindow*, Int32*, Int32*); - PalResult (*getWindowSize)(PalWindow*, Uint32*, Uint32*); - PalResult (*getWindowState)(PalWindow*, PalWindowState*); - bool (*isWindowVisible)(PalWindow*); - PalWindow* (*getFocusWindow)(); - PalWindowHandleInfo (*getWindowHandleInfo)(PalWindow*); - PalWindowHandleInfoEx (*getWindowHandleInfoEx)(PalWindow*); - PalResult (*setWindowOpacity)(PalWindow*, float); - PalResult (*setWindowStyle)(PalWindow*, PalWindowStyle); - PalResult (*setWindowTitle)(PalWindow*, const char*); - PalResult (*setWindowPos)(PalWindow*, Int32, Int32); - PalResult (*setWindowSize)(PalWindow*, Uint32, Uint32); - PalResult (*setFocusWindow)(PalWindow*); - - PalResult (*createIcon)(const PalIconCreateInfo*, PalIcon**); - void (*destroyIcon)(PalIcon*); - PalResult (*setWindowIcon)(PalWindow*, PalIcon*); - - PalResult (*createCursor)(const PalCursorCreateInfo*, PalCursor**); - PalResult (*createCursorFrom)(PalCursorType, PalCursor**); - void (*destroyCursor)(PalCursor*); - void (*showCursor)(bool); - PalResult (*clipCursor)(PalWindow*, bool); - PalResult (*getCursorPos)(PalWindow*, Int32*, Int32*); - PalResult (*setCursorPos)(PalWindow*, Int32, Int32); - PalResult (*setWindowCursor)(PalWindow*, PalCursor*); - - PalResult (*attachWindow)(void*, PalWindow**); - PalResult (*detachWindow)(PalWindow*, void**); - // clang-format off -} Backend; - -typedef struct { - bool initialized; - Int32 maxWindowData; - Int32 maxMonitorData; - Int32 pixelFormat; - PalVideoFeatures features; - PalVideoFeatures2 features2; - const PalAllocator* allocator; - PalEventDriver* eventDriver; - const Backend* backend; - WindowData* windowData; - MonitorData* monitorData; - const char* className; -} VideoLinux; - -static VideoLinux s_Video = {0}; -static Mouse s_Mouse = {0}; -static Keyboard s_Keyboard = {0}; -static EGL s_Egl; +#pragma endregions // ================================================== // Internal API @@ -5336,8 +5345,7 @@ static void wlOutputDone( void* data, struct wl_output* output) { - (void)data; - (void)output; + } static const struct wl_registry_listener s_RegistryListener = { @@ -5447,6 +5455,10 @@ PalResult wlInitVideo() s_Wl.handle, "wl_display_get_fd"); + s_Wl.cancelRead = (wl_display_cancel_read_fn)dlsym( + s_Wl.handle, + "wl_display_cancel_read"); + // initialize wayland s_Wl.modesPhase = false; s_Wl.checkFeatures = true; @@ -5459,8 +5471,6 @@ PalResult wlInitVideo() wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); s_Wl.displayRoundtrip(s_Wl.display); - // s_Wl.displayRoundtrip(s_Wl.display); - if (!s_Wl.compositor || !s_Wl.xdgBase || !s_Wl.shm) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -5484,18 +5494,27 @@ void wlShutdownVideo() } void wlUpdateVideo() -{ - s_Wl.dispatchPending(s_Wl.display); +{ + // flush pending requests s_Wl.displayFlush(s_Wl.display); - // check for new messages + while (s_Wl.prepareRead(s_Wl.display) != 0) { + s_Wl.dispatchPending(s_Wl.display); + } + int fd = s_Wl.displayGetFd(s_Wl.display); struct pollfd pfd = { fd, POLLIN, 0 }; - poll(&pfd, 1, 0); - - if (pfd.revents & POLLIN) { - s_Wl.displayDispatch(s_Wl.display); + if (poll(&pfd, 1, 0) > 0) { + // there are events ready to be read + s_Wl.readEvents(s_Wl.display); + + } else { + s_Wl.cancelRead(s_Wl.display); } + + // dispatch events that were read + s_Wl.dispatchPending(s_Wl.display); + s_Wl.displayFlush(s_Wl.display); } PalResult wlSetFBConfig( diff --git a/tests/tests_main.c b/tests/tests_main.c index f9fa8b4..0d4830c 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -29,14 +29,14 @@ int main(int argc, char** argv) // registerTest("Video Test", videoTest); // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); + registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); - registerTest("Native Integration Test", nativeIntegrationTest); + // registerTest("Native Integration Test", nativeIntegrationTest); #endif // PAL_HAS_VIDEO #if PAL_HAS_OPENGL From dede09b7796c8f3e97d648079467e91b871da685 Mon Sep 17 00:00:00 2001 From: nichcode Date: Sat, 8 Nov 2025 14:28:16 +0000 Subject: [PATCH 08/51] add features64 --- CHANGELOG.md | 2 +- include/pal/pal_video.h | 67 ++++++-- src/video/pal_video_linux.c | 266 +++++++++++++++++++++++++++----- src/video/pal_video_win32.c | 21 +-- tests/attach_window_test.c | 5 +- tests/cursor_test.c | 4 +- tests/icon_test.c | 6 +- tests/native_integration_test.c | 4 +- tests/tests_main.c | 4 +- tests/video_test.c | 79 +++++----- tests/window_test.c | 5 +- 11 files changed, 346 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4937b4c..5fc159a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,7 +74,7 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr ### Features - **Video:** Added Wayland-based backend support. -- **Video:** Added **palGetVideoFeaturesEx()** to check supported features. +- **Video:** Added **palGetVideoFeaturesEx()** to check old and extended supported features. - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. ### Tests diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 9a26c00..d7488bd 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -111,29 +111,61 @@ typedef enum { PAL_VIDEO_FEATURE_WINDOW_GET_STYLE = PAL_BIT(28), PAL_VIDEO_FEATURE_CURSOR_SET_POS = PAL_BIT(29), PAL_VIDEO_FEATURE_CURSOR_GET_POS = PAL_BIT(30), - PAL_VIDEO_FEATURE_WINDOW_SET_ICON = PAL_BIT(31), + PAL_VIDEO_FEATURE_WINDOW_SET_ICON = PAL_BIT(31) } PalVideoFeatures; /** - * @enum PalVideoFeatures2 + * @enum PalVideoFeatures64 * @brief Extended Video system features. * - * All extended video features follow the format `PAL_VIDEO_FEATURE_**` for + * All extended video features follow the format `PAL_VIDEO_FEATURE64_**` for * consistency and API use. * * @since 1.3 * @ingroup pal_video */ typedef enum { - PAL_VIDEO_FEATURE_TOPMOST_WINDOW = PAL_BIT64(32), - PAL_VIDEO_FEATURE_DECORATED_WINDOW = PAL_BIT64(33), - PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY = PAL_BIT64(34), - PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR = PAL_BIT64(35), - PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY = PAL_BIT64(36), - PAL_VIDEO_FEATURE_FOREIGN_WINDOWS = PAL_BIT64(37), - PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE = PAL_BIT64(38), - PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR = PAL_BIT64(39) -} PalVideoFeatures2; + PAL_VIDEO_FEATURE64_HIGH_DPI = PAL_BIT64(0), + PAL_VIDEO_FEATURE64_MONITOR_SET_ORIENTATION = PAL_BIT64(1), + PAL_VIDEO_FEATURE64_MONITOR_GET_ORIENTATION = PAL_BIT64(2), + PAL_VIDEO_FEATURE64_BORDERLESS_WINDOW = PAL_BIT64(3), + PAL_VIDEO_FEATURE64_TRANSPARENT_WINDOW = PAL_BIT64(4), + PAL_VIDEO_FEATURE64_TOOL_WINDOW = PAL_BIT64(5), + PAL_VIDEO_FEATURE64_MONITOR_SET_MODE = PAL_BIT64(6), + PAL_VIDEO_FEATURE64_MONITOR_GET_MODE = PAL_BIT64(7), + PAL_VIDEO_FEATURE64_MULTI_MONITORS = PAL_BIT64(8), + PAL_VIDEO_FEATURE64_WINDOW_SET_SIZE = PAL_BIT64(9), + PAL_VIDEO_FEATURE64_WINDOW_GET_SIZE = PAL_BIT64(10), + PAL_VIDEO_FEATURE64_WINDOW_SET_POS = PAL_BIT64(11), + PAL_VIDEO_FEATURE64_WINDOW_GET_POS = PAL_BIT64(12), + PAL_VIDEO_FEATURE64_WINDOW_SET_STATE = PAL_BIT64(13), + PAL_VIDEO_FEATURE64_WINDOW_GET_STATE = PAL_BIT64(14), + PAL_VIDEO_FEATURE64_WINDOW_SET_VISIBILITY = PAL_BIT64(15), + PAL_VIDEO_FEATURE64_WINDOW_GET_VISIBILITY = PAL_BIT64(16), + PAL_VIDEO_FEATURE64_WINDOW_SET_TITLE = PAL_BIT64(17), + PAL_VIDEO_FEATURE64_WINDOW_GET_TITLE = PAL_BIT64(18), + PAL_VIDEO_FEATURE64_NO_MAXIMIZEBOX = PAL_BIT64(19), + PAL_VIDEO_FEATURE64_NO_MINIMIZEBOX = PAL_BIT64(20), + PAL_VIDEO_FEATURE64_CLIP_CURSOR = PAL_BIT64(21), + PAL_VIDEO_FEATURE64_WINDOW_FLASH_CAPTION = PAL_BIT64(22), + PAL_VIDEO_FEATURE64_WINDOW_FLASH_TRAY = PAL_BIT64(23), + PAL_VIDEO_FEATURE64_WINDOW_FLASH_INTERVAL = PAL_BIT64(24), + PAL_VIDEO_FEATURE64_WINDOW_SET_INPUT_FOCUS = PAL_BIT64(25), + PAL_VIDEO_FEATURE64_WINDOW_GET_INPUT_FOCUS = PAL_BIT64(26), + PAL_VIDEO_FEATURE64_WINDOW_SET_STYLE = PAL_BIT64(27), + PAL_VIDEO_FEATURE64_WINDOW_GET_STYLE = PAL_BIT64(28), + PAL_VIDEO_FEATURE64_CURSOR_SET_POS = PAL_BIT64(29), + PAL_VIDEO_FEATURE64_CURSOR_GET_POS = PAL_BIT64(30), + PAL_VIDEO_FEATURE64_WINDOW_SET_ICON = PAL_BIT64(31), + PAL_VIDEO_FEATURE64_TOPMOST_WINDOW = PAL_BIT64(32), + PAL_VIDEO_FEATURE64_DECORATED_WINDOW = PAL_BIT64(33), + PAL_VIDEO_FEATURE64_CURSOR_SET_VISIBILITY = PAL_BIT64(34), + PAL_VIDEO_FEATURE64_WINDOW_GET_MONITOR = PAL_BIT64(35), + PAL_VIDEO_FEATURE64_MONITOR_GET_PRIMARY = PAL_BIT64(36), + PAL_VIDEO_FEATURE64_FOREIGN_WINDOWS = PAL_BIT64(37), + PAL_VIDEO_FEATURE64_MONITOR_VALIDATE_MODE = PAL_BIT64(38), + PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR = PAL_BIT64(39) +} PalVideoFeatures64; /** * @enum PalOrientation @@ -739,7 +771,7 @@ PAL_API PalVideoFeatures PAL_CALL palGetVideoFeatures(); * @ingroup pal_video * @sa palInitVideo */ -PAL_API PalVideoFeatures2 PAL_CALL palGetVideoFeaturesEx(); +PAL_API PalVideoFeatures64 PAL_CALL palGetVideoFeaturesEx(); /** * @brief Set the FBConfig for the video system. @@ -1000,6 +1032,13 @@ PAL_API PalResult PAL_CALL palSetMonitorOrientation( * failure. Call palFormatResult() for more information. * * Thread safety: This function must only be called from the main thread. + * + * @note On Wayland + * + * - creating non resizable windows is not supported. + * PAL will always creating resizable windows. + * + * * - Creating windows on a specific monitor is not supported. * * @since 1.0 * @ingroup pal_video @@ -1080,6 +1119,8 @@ PAL_API PalResult PAL_CALL palMaximizeWindow(PalWindow* window); * failure. Call palFormatResult() for more information. * * Thread safety: This function must only be called from the main thread. + * + * @note Wayland does not support restoring a minimized windows. * * @since 1.0 * @ingroup pal_video diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 268478c..324f636 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -246,7 +246,7 @@ typedef struct { Int32 maxMonitorData; Int32 pixelFormat; PalVideoFeatures features; - PalVideoFeatures2 features2; + PalVideoFeatures64 features64; const PalAllocator* allocator; PalEventDriver* eventDriver; const Backend* backend; @@ -1111,6 +1111,38 @@ static inline void wlSurfaceDamageBuffer( height); } +static inline int wlSurfaceAddListener( + struct wl_surface *wl_surface, + const struct wl_surface_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) wl_surface, + (void (**)(void)) listener, data); +} + +static void surfaceEnter( + void* userData, + struct wl_surface* surface, + struct wl_output* output) +{ + // TODO: + +} + +static void surfaceLeave( + void* userData, + struct wl_surface* surface, + struct wl_output* output) +{ + // TODO: +} + +static struct wl_surface_listener surfaceListener = { + .enter = surfaceEnter, + .leave = surfaceLeave +}; + #endif // PAL_HAS_WAYLAND #pragma endregion @@ -1233,7 +1265,62 @@ static void xdgSurfaceConfigure( struct xdg_surface* surface, uint32_t serial) { + WindowData* winData = (WindowData*)data; xdgSurfaceAckConfigure(surface, serial); + + // push and resolve any pending events + if (!winData->skipConfigure) { + winData->skipConfigure = true; + + // create a new buffer with the new size + struct wl_buffer* buffer = nullptr; + buffer = createShmBuffer(winData->w, winData->h); + if (!buffer) { + return; + } + + struct wl_surface* _surface = nullptr; + _surface = (struct wl_surface*)winData->window; + + wlSurfaceAttach(_surface, buffer, 0, 0); + wlSurfaceDamageBuffer(_surface, 0, 0, winData->w, winData->h); + wlSurfaceCommit(_surface); + + // destroy old buffer + wlBufferDestroy(winData->buffer); + winData->buffer = buffer; + + // push a window resize event + PalEventType type = PAL_EVENT_WINDOW_SIZE; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackUint32(winData->w, winData->h); + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } + } + + // pending state + if (!winData->skipState) { + winData->skipState = true; + + // push a window state event + // we dont recreate buffers over here + // since we already create the buffer with the new size + PalEventType type = PAL_EVENT_WINDOW_STATE; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = winData->state; + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } + } } static void xdgToplevelConfigure( @@ -1244,10 +1331,28 @@ static void xdgToplevelConfigure( struct wl_array* states) { WindowData* winData = (WindowData*)data; - if (width > 0 && height > 0) { - winData->w = width; - winData->h = height; + + if (!winData->skipState) { + uint32_t* state; + wl_array_for_each(state, states) { + // we need only maximized + if (*state == 1) { // XDG_TOPLEVEL_STATE_MAXIMIZED + if (winData->state != PAL_WINDOW_STATE_MAXIMIZED) { + winData->state = PAL_WINDOW_STATE_MAXIMIZED; + winData->skipState = false; + } + } + } } + + if (!winData->skipConfigure) { + if (width > 0 && height > 0) { + winData->w = width; + winData->h = height; + winData->skipConfigure = false; + } + } + } static void xdgToplevelClose( @@ -1256,7 +1361,6 @@ static void xdgToplevelClose( { WindowData* winData = (WindowData*)data; - // TODO: push window close event PalEventType type = PAL_EVENT_WINDOW_CLOSE; PalDispatchMode mode = palGetEventDispatchMode(s_Video.eventDriver, type); if (mode != PAL_DISPATCH_NONE) { @@ -1402,6 +1506,17 @@ static inline void xdgToplevelSetAppId( app_id); } +static inline void xdgToplevelUnsetMaximized(struct xdg_toplevel *xdg_toplevel) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) xdg_toplevel, + 10, // XDG_TOPLEVEL_UNSET_MAXIMIZED + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) xdg_toplevel), + 0); +} + static const struct wl_interface *xdg_shell_types[26]; static const struct wl_message xdg_wm_base_requests[] = { @@ -2122,7 +2237,7 @@ static void xCheckFeatures() (unsigned char**)&supportedAtoms); PalVideoFeatures features = 0; - PalVideoFeatures2 features2 = 0; + PalVideoFeatures64 features64 = 0; for (unsigned long i = 0; i < count; ++i) { if (supportedAtoms[i] == s_X11Atoms._NET_WM_STATE_MAXIMIZED_VERT) { features |= PAL_VIDEO_FEATURE_WINDOW_SET_STATE; @@ -2181,14 +2296,33 @@ static void xCheckFeatures() features |= PAL_VIDEO_FEATURE_WINDOW_FLASH_TRAY; // extended features - features2 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; - features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; - features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; - features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; - features2 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; + // old features + features64 |= PAL_VIDEO_FEATURE64_MULTI_MONITORS; + features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_ORIENTATION; + features64 |= PAL_VIDEO_FEATURE64_MONITOR_SET_MODE; + features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_MODE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_SIZE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_SIZE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_VISIBILITY; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_VISIBILITY; + + features64 |= PAL_VIDEO_FEATURE64_CLIP_CURSOR; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_INPUT_FOCUS; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_INPUT_FOCUS; + features64 |= PAL_VIDEO_FEATURE64_CURSOR_SET_POS; + features64 |= PAL_VIDEO_FEATURE64_CURSOR_GET_POS; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_TITLE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_TITLE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_FLASH_TRAY; + + features64 |= PAL_VIDEO_FEATURE64_TOPMOST_WINDOW; + features64 |= PAL_VIDEO_FEATURE64_DECORATED_WINDOW; + features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_PRIMARY; + features64 |= PAL_VIDEO_FEATURE64_FOREIGN_WINDOWS; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR; s_Video.features = features; - s_Video.features2 = features2; + s_Video.features64 = features64; s_X11.free(supportedAtoms); } @@ -3898,6 +4032,44 @@ static PalResult xCreateWindow( monitorY = monitorInfo.y; monitorW = monitorInfo.width; monitorH = monitorInfo.height; + + } else { + // primary monitor is not set + XRRScreenResources* resources = nullptr; + resources = s_X11.getScreenResources(s_X11.display, s_X11.root); + for (int i = 0; i < resources->noutput; ++i) { + RROutput output = resources->outputs[i]; + XRROutputInfo* outputInfo = s_X11.getOutputInfo( + s_X11.display, + resources, + FROM_PAL_HANDLE(RROutput, monitor)); + + // check if its a monitor + if (outputInfo->connection != RR_Connected || + outputInfo->crtc == None) { + s_X11.freeOutputInfo(outputInfo); + continue; + } + + // clang-format off + XRRCrtcInfo* crtc = s_X11.getCrtcInfo( + s_X11.display, + resources, + outputInfo->crtc); + // clang-format on + + monitorX = crtc->x; + + monitorX = crtc->x; + monitorY = crtc->y; + monitorW = crtc->width; + monitorH = crtc->height; + + s_X11.freeCrtcInfo(crtc); + s_X11.freeOutputInfo(outputInfo); + break; + } + s_X11.freeScreenResources(resources); } Int32 x, y = 0; @@ -5159,6 +5331,7 @@ static void wlGlobalHandle( { if (s_Wl.checkFeatures) { PalVideoFeatures features = 0; + PalVideoFeatures64 features64 = 0; features |= PAL_VIDEO_FEATURE_HIGH_DPI; features |= PAL_VIDEO_FEATURE_MONITOR_GET_ORIENTATION; features |= PAL_VIDEO_FEATURE_MULTI_MONITORS; @@ -5168,8 +5341,20 @@ static void wlGlobalHandle( features |= PAL_VIDEO_FEATURE_WINDOW_SET_STATE; features |= PAL_VIDEO_FEATURE_BORDERLESS_WINDOW; - s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; + features64 |= PAL_VIDEO_FEATURE64_HIGH_DPI; + features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_ORIENTATION; + features64 |= PAL_VIDEO_FEATURE64_MULTI_MONITORS; + features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_MODE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_TITLE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_SIZE; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_STATE; + features64 |= PAL_VIDEO_FEATURE64_BORDERLESS_WINDOW; + + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR; + s_Video.features = features; + s_Video.features64 = features64; + s_Wl.checkFeatures = false; } if (strcmp(interface, "wl_compositor") == 0) { @@ -5203,7 +5388,7 @@ static void wlGlobalHandle( &zxdg_decoration_manager_v1_interface, 1); - s_Video.features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE64_DECORATED_WINDOW; } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { s_Wl.pointerConstraints = wlRegistryBind( @@ -5475,7 +5660,6 @@ PalResult wlInitVideo() return PAL_RESULT_PLATFORM_FAILURE; } - s_Wl.checkFeatures = false; return PAL_RESULT_SUCCESS; } @@ -5816,9 +6000,13 @@ PalResult wlCreateWindow( xdgToplevelSetTitle(xdgToplevel, info->title); xdgToplevelSetAppId(xdgToplevel, appID); + wlSurfaceAddListener(surface, &surfaceListener, data); xdgToplevelAddListener(xdgToplevel, &xdgToplevelListener, data); xdgSurfaceAddListener(xdgSurface, &xdgSurfaceListener, data); wlSurfaceCommit(surface); + + data->skipState = true; + data->skipConfigure = true; s_Wl.displayRoundtrip(s_Wl.display); if (info->maximized && info->show) { @@ -5827,6 +6015,7 @@ PalResult wlCreateWindow( xdgToplevelSetMaximized(xdgToplevel); wlSurfaceCommit(surface); s_Wl.displayRoundtrip(s_Wl.display); + data->state = PAL_WINDOW_STATE_MAXIMIZED; } else { data->w = info->width; @@ -5838,6 +6027,7 @@ PalResult wlCreateWindow( data->window = (PalWindow*)surface; data->buffer = nullptr; data->isAttached = false; + data->state = PAL_WINDOW_STATE_RESTORED; // show window if (info->show == false) { @@ -5851,6 +6041,7 @@ PalResult wlCreateWindow( if (info->minimized) { xdgToplevelSetMinimized(xdgToplevel); wlSurfaceCommit(surface); + data->state = PAL_WINDOW_STATE_MINIMIZED; } // decorated window @@ -5879,16 +6070,7 @@ PalResult wlCreateWindow( wlSurfaceCommit(surface); s_Wl.displayRoundtrip(s_Wl.display); - // resizable - if (!(info->style & PAL_WINDOW_STYLE_RESIZABLE)) { - xdgToplevelSetMinSize(xdgToplevel, data->w, data->h); - xdgToplevelSetMaxSize(xdgToplevel, data->w, data->h); - wlSurfaceCommit(surface); - } - data->buffer = buffer; - s_Wl.displayFlush(s_Wl.display); - *outWindow = data->window; return PAL_RESULT_SUCCESS; } @@ -5935,6 +6117,13 @@ PalResult wlMaximizeWindow(PalWindow* window) PalResult wlRestoreWindow(PalWindow* window) { + WindowData* data = findWindowData(window); + if (!data) { + return PAL_RESULT_INVALID_WINDOW; + } + + // we can only restore from a maximized state + xdgToplevelUnsetMaximized(data->xdgToplevel); return PAL_RESULT_SUCCESS; } @@ -5966,7 +6155,7 @@ PalResult wlGetWindowMonitor( PalWindow* window, PalMonitor** outMonitor) { - return PAL_RESULT_SUCCESS; + return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; } PalResult wlGetWindowTitle( @@ -6008,6 +6197,7 @@ bool wlIsWindowVisible(PalWindow* window) PalWindow* wlGetFocusWindow() { + // Wayland does not let client query focused window return nullptr; } @@ -6075,6 +6265,14 @@ PalResult wlSetWindowSize( Uint32 width, Uint32 height) { + WindowData* data = findWindowData(window); + if (!data) { + return PAL_RESULT_INVALID_WINDOW; + } + + xdgToplevelSetMinSize(data->xdgToplevel, width, height); + xdgToplevelSetMaxSize(data->xdgToplevel, width, height); + wlSurfaceCommit((struct wl_surface*)window); return PAL_RESULT_SUCCESS; } @@ -6290,17 +6488,9 @@ PalResult PAL_CALL palInitVideo( #if PAL_HAS_WAYLAND PalResult ret = wlInitVideo(); if (ret != PAL_RESULT_SUCCESS) { - // fallback to X11 - wlShutdownVideo(); - ret = xInitVideo(); - if (ret != PAL_RESULT_SUCCESS) { - return ret; - } - s_Video.backend = &s_XBackend; - - } else { - s_Video.backend = &s_wlBackend; + return ret; } + s_Video.backend = &s_wlBackend; #endif // PAL_HAS_WAYLAND } @@ -6360,13 +6550,13 @@ PalVideoFeatures PAL_CALL palGetVideoFeatures() return s_Video.features; } -PalVideoFeatures2 PAL_CALL palGetVideoFeaturesEx() +PalVideoFeatures64 PAL_CALL palGetVideoFeaturesEx() { if (!s_Video.initialized) { return 0; } - - return ((Uint64)s_Video.features2) | (Uint64)s_Video.features; + + return s_Video.features64; } PalResult PAL_CALL palSetFBConfig( diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index 4585685..49488bf 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -104,7 +104,7 @@ typedef struct { Int32 pixelFormat; Int32 maxWindowData; PalVideoFeatures features; - PalVideoFeatures2 features2; + PalVideoFeatures64 features64; const PalAllocator* allocator; PalEventDriver* eventDriver; HINSTANCE shcore; @@ -1167,6 +1167,7 @@ PalResult PAL_CALL palInitVideo( // clang-format on + // TODO:: // set features s_Video.features |= PAL_VIDEO_FEATURE_MONITOR_SET_ORIENTATION; s_Video.features |= PAL_VIDEO_FEATURE_MONITOR_GET_ORIENTATION; @@ -1206,14 +1207,14 @@ PalResult PAL_CALL palInitVideo( } // extended features - s_Video.features2 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; - s_Video.features2 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; - s_Video.features2 |= PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY; - s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR; - s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; - s_Video.features2 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; - s_Video.features2 |= PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE; - s_Video.features2 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; + s_Video.features64 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY; + s_Video.features64 |= PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR; + s_Video.features64 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; + s_Video.features64 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; + s_Video.features64 |= PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE; + s_Video.features64 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; s_Video.initialized = true; s_Video.allocator = allocator; @@ -1298,7 +1299,7 @@ palGetVideoFeaturesEx PAL_CALL palGetVideoFeaturesEx() return 0; } - return ((Uint64)s_Video.features2) | (Uint64)s_Video.features; + return ((Uint64)s_Video.features64) | (Uint64)s_Video.features; } PalResult PAL_CALL palSetFBConfig( diff --git a/tests/attach_window_test.c b/tests/attach_window_test.c index 13a3904..396da8f 100644 --- a/tests/attach_window_test.c +++ b/tests/attach_window_test.c @@ -240,8 +240,8 @@ bool attachWindowTest() } // check for support - PalVideoFeatures2 features = palGetVideoFeaturesEx(); - if (!(features & PAL_VIDEO_FEATURE_FOREIGN_WINDOWS)) { + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_FOREIGN_WINDOWS)) { // clang-format off palLog(nullptr, "Attaching and detaching foreign windows feature not supported"); // clang-format on @@ -295,7 +295,6 @@ bool attachWindowTest() // now that the window is attached, we can use PAL video API // to manager it - // TODO: check features before result = palSetWindowTitle(myWindow, WINDOW_TITLE); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); diff --git a/tests/cursor_test.c b/tests/cursor_test.c index 5461c3e..656e105 100644 --- a/tests/cursor_test.c +++ b/tests/cursor_test.c @@ -47,8 +47,8 @@ bool cursorTest() } // check for support - PalVideoFeatures2 features = palGetVideoFeaturesEx(); - if (!(features & PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR)) { + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR)) { palLog(nullptr, "Seting cursors feature not supported"); palDestroyEventDriver(eventDriver); palShutdownVideo(); diff --git a/tests/icon_test.c b/tests/icon_test.c index 81641c9..ba32679 100644 --- a/tests/icon_test.c +++ b/tests/icon_test.c @@ -47,9 +47,9 @@ bool iconTest() } // check for support - PalVideoFeatures2 features = palGetVideoFeaturesEx(); - if (!(features & PAL_VIDEO_FEATURE_WINDOW_SET_ICON)) { - palLog(nullptr, "Seting icons feature not supported"); + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_WINDOW_SET_ICON)) { + palLog(nullptr, "Setting icons feature not supported"); palDestroyEventDriver(eventDriver); palShutdownVideo(); return false; diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 23bd50f..688bd9d 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -342,7 +342,7 @@ bool nativeIntegrationTest() } // check for support - PalVideoFeatures2 features = palGetVideoFeaturesEx(); + PalVideoFeatures64 features = palGetVideoFeaturesEx(); PalWindow* window = nullptr; PalWindowCreateInfo createInfo = {0}; @@ -354,7 +354,7 @@ bool nativeIntegrationTest() createInfo.title = "Native Integration Test"; // check if we support decorated windows (title bar, close etc) - if (!(features & PAL_VIDEO_FEATURE_DECORATED_WINDOW)) { + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { // if we dont support, we need to create a borderless window // and create the decorations ourselves createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; diff --git a/tests/tests_main.c b/tests/tests_main.c index 0d4830c..c0c4254 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -29,9 +29,9 @@ int main(int argc, char** argv) // registerTest("Video Test", videoTest); // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); + // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); - // registerTest("Cursor Test", cursorTest); + registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); diff --git a/tests/video_test.c b/tests/video_test.c index 5599023..8535e13 100644 --- a/tests/video_test.c +++ b/tests/video_test.c @@ -11,7 +11,6 @@ bool videoTest() palLog(nullptr, ""); PalResult result; - PalVideoFeatures2 features; // initialize the video system result = palInitVideo(nullptr, nullptr); @@ -22,157 +21,157 @@ bool videoTest() } // get supported features. Now uses extended function - features = palGetVideoFeaturesEx(); + PalVideoFeatures64 features = palGetVideoFeaturesEx(); palLog(nullptr, "Supported Video Features:"); - if (features & PAL_VIDEO_FEATURE_HIGH_DPI) { + if (features & PAL_VIDEO_FEATURE64_HIGH_DPI) { palLog(nullptr, " High DPI windows"); } - if (features & PAL_VIDEO_FEATURE_MONITOR_SET_ORIENTATION) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_SET_ORIENTATION) { palLog(nullptr, " Setting monitor orientation"); } - if (features & PAL_VIDEO_FEATURE_MONITOR_GET_ORIENTATION) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_GET_ORIENTATION) { palLog(nullptr, " Getting monitor orientation"); } - if (features & PAL_VIDEO_FEATURE_BORDERLESS_WINDOW) { + if (features & PAL_VIDEO_FEATURE64_BORDERLESS_WINDOW) { palLog(nullptr, " Borderless windows"); } - if (features & PAL_VIDEO_FEATURE_TRANSPARENT_WINDOW) { + if (features & PAL_VIDEO_FEATURE64_TRANSPARENT_WINDOW) { palLog(nullptr, " Transparent windows"); } - if (features & PAL_VIDEO_FEATURE_TOOL_WINDOW) { + if (features & PAL_VIDEO_FEATURE64_TOOL_WINDOW) { palLog(nullptr, " Tool windows"); } - if (features & PAL_VIDEO_FEATURE_MONITOR_SET_MODE) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_SET_MODE) { palLog(nullptr, " Setting monitor display mode"); } - if (features & PAL_VIDEO_FEATURE_MONITOR_GET_MODE) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_GET_MODE) { palLog(nullptr, " Getting monitor display mode"); } - if (features & PAL_VIDEO_FEATURE_MULTI_MONITORS) { + if (features & PAL_VIDEO_FEATURE64_MULTI_MONITORS) { palLog(nullptr, " Multi monitors"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_SIZE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_SIZE) { palLog(nullptr, " Setting window size"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_SIZE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_SIZE) { palLog(nullptr, " Getting window size"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_POS) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_POS) { palLog(nullptr, " Setting window position"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_POS) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_POS) { palLog(nullptr, " Getting window position"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_STATE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_STATE) { palLog(nullptr, " Setting window state"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_STATE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_STATE) { palLog(nullptr, " Getting window state"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_VISIBILITY) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_VISIBILITY) { palLog(nullptr, " Setting window visibility"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_VISIBILITY) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_VISIBILITY) { palLog(nullptr, " Getting window visibility"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_TITLE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_TITLE) { palLog(nullptr, " Setting window title"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_TITLE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_TITLE) { palLog(nullptr, " Getting window title"); } - if (features & PAL_VIDEO_FEATURE_NO_MINIMIZEBOX) { + if (features & PAL_VIDEO_FEATURE64_NO_MINIMIZEBOX) { palLog(nullptr, " No minimize box for windows"); } - if (features & PAL_VIDEO_FEATURE_NO_MAXIMIZEBOX) { + if (features & PAL_VIDEO_FEATURE64_NO_MAXIMIZEBOX) { palLog(nullptr, " No maximize box for windows"); } - if (features & PAL_VIDEO_FEATURE_CLIP_CURSOR) { + if (features & PAL_VIDEO_FEATURE64_CLIP_CURSOR) { palLog(nullptr, " Clipping cursor (mouse)"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_FLASH_CAPTION) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_FLASH_CAPTION) { palLog(nullptr, " Window titlebar flashing"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_FLASH_TRAY) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_FLASH_TRAY) { palLog(nullptr, " Window icon on taskbar flashing"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_FLASH_INTERVAL) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_FLASH_INTERVAL) { palLog(nullptr, " Setting window flash interval"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_INPUT_FOCUS) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_INPUT_FOCUS) { palLog(nullptr, " Setting input window"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_INPUT_FOCUS) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_INPUT_FOCUS) { palLog(nullptr, " Getting input window"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_SET_STYLE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_STYLE) { palLog(nullptr, " Setting window style"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_STYLE) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_STYLE) { palLog(nullptr, " Getting window style"); } - if (features & PAL_VIDEO_FEATURE_CURSOR_SET_POS) { + if (features & PAL_VIDEO_FEATURE64_CURSOR_SET_POS) { palLog(nullptr, " Setting cursor position"); } - if (features & PAL_VIDEO_FEATURE_CURSOR_GET_POS) { + if (features & PAL_VIDEO_FEATURE64_CURSOR_GET_POS) { palLog(nullptr, " Getting cursor position"); } - if (features & PAL_VIDEO_FEATURE_TOPMOST_WINDOW) { + if (features & PAL_VIDEO_FEATURE64_TOPMOST_WINDOW) { palLog(nullptr, " Topmost windows"); } - if (features & PAL_VIDEO_FEATURE_DECORATED_WINDOW) { + if (features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW) { palLog(nullptr, " Decorated (Normal) windows"); } - if (features & PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY) { + if (features & PAL_VIDEO_FEATURE64_CURSOR_SET_VISIBILITY) { palLog(nullptr, " Setting cursor visibility"); } - if (features & PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR) { + if (features & PAL_VIDEO_FEATURE64_WINDOW_GET_MONITOR) { palLog(nullptr, " Getting window current monitor"); } - if (features & PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_GET_PRIMARY) { palLog(nullptr, " Getting primary monitor"); } - if (features & PAL_VIDEO_FEATURE_FOREIGN_WINDOWS) { + if (features & PAL_VIDEO_FEATURE64_FOREIGN_WINDOWS) { palLog(nullptr, " Attaching and detaching foreign windows"); } - if (features & PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE ) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_VALIDATE_MODE ) { palLog(nullptr, " Validate monitor display mode"); } diff --git a/tests/window_test.c b/tests/window_test.c index 6a6111b..82488f4 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -162,7 +162,6 @@ bool windowTest() PalResult result; PalWindow* window = nullptr; PalWindowCreateInfo createInfo = {0}; - PalVideoFeatures2 features; bool running = false; // event driver @@ -194,7 +193,7 @@ bool windowTest() } // get video system features - features = palGetVideoFeaturesEx(); + PalVideoFeatures64 features = palGetVideoFeaturesEx(); // fill the create info struct createInfo.monitor = nullptr; // use default monitor @@ -204,7 +203,7 @@ bool windowTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; // check if we support decorated windows (title bar, close etc) - if (!(features & PAL_VIDEO_FEATURE_DECORATED_WINDOW)) { + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { // if we dont support, we need to create a borderless window // and create the decorations ourselves createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; From 4ab16532fd29301830d5fd671fedf7f783a42621 Mon Sep 17 00:00:00 2001 From: nichcode Date: Sat, 8 Nov 2025 16:26:02 +0000 Subject: [PATCH 09/51] cursor test wayland --- src/video/pal_video_linux.c | 292 ++++++++++++++++++++++++++++++++++-- tests/cursor_test.c | 7 + 2 files changed, 288 insertions(+), 11 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 324f636..c237632 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -143,6 +143,7 @@ typedef struct { void* xdgToplevel; void* buffer; void* decoration; + void* cursor; } WindowData; typedef struct { @@ -864,6 +865,9 @@ typedef struct { struct xdg_wm_base* xdgBase; struct wl_compositor* compositor; struct wl_shm* shm; + struct wl_seat* seat; + struct wl_pointer* pointer; + struct wl_keyboard* keyboard; struct zxdg_decoration_manager_v1* decorationManager; struct zwp_pointer_constraints* pointerConstraints; @@ -877,6 +881,7 @@ typedef struct { const struct wl_interface* shmPoolInterface; const struct wl_interface* regionInterface; const struct wl_interface* pointerInterface; + const struct wl_interface* keyboardInterface; wl_display_connect_fn displayConnect; wl_display_disconnect_fn displayDisconnect; @@ -902,6 +907,13 @@ typedef struct { PalMonitorMode* modes; } MonitorModesData; +typedef struct { + struct wl_buffer* buffer; + struct wl_surface* surface; + int hotspotX; + int hotspotY; +} WaylandCursor; + static Wayland s_Wl = {0}; #endif // PAL_HAS_WAYLAND @@ -910,6 +922,8 @@ static Wayland s_Wl = {0}; #pragma region Wayland-Client-Protocol #if PAL_HAS_WAYLAND +static WindowData* findWindowData(PalWindow* window); + static inline void* wlRegistryBind( struct wl_registry *wl_registry, uint32_t name, @@ -1121,6 +1135,86 @@ static inline int wlSurfaceAddListener( (void (**)(void)) listener, data); } +static inline int wlSeatAddListener( + struct wl_seat *wl_seat, + const struct wl_seat_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) wl_seat, + (void (**)(void)) listener, data); +} + +static inline struct wl_pointer* wlSeatGetPointer(struct wl_seat *wl_seat) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_seat, + WL_SEAT_GET_POINTER, + s_Wl.pointerInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_seat), + 0, + NULL); + + return (struct wl_pointer *) id; +} + +static inline struct wl_keyboard* wlSeatGetKeyboard(struct wl_seat *wl_seat) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_seat, + WL_SEAT_GET_KEYBOARD, + s_Wl.keyboardInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_seat), + 0, + NULL); + + return (struct wl_keyboard *) id; +} + +static inline int wlPointerAddListener( + struct wl_pointer *wl_pointer, + const struct wl_pointer_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) wl_pointer, + (void (**)(void)) listener, data); +} + +static inline void wlPointerSetCursor( + struct wl_pointer *wl_pointer, + uint32_t serial, + struct wl_surface *surface, + int32_t hotspot_x, + int32_t hotspot_y) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_pointer, + WL_POINTER_SET_CURSOR, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_pointer), + 0, + serial, + surface, + hotspot_x, + hotspot_y); +} + +static inline int wlKeyboardAddListener( + struct wl_keyboard *wl_keyboard, + const struct wl_keyboard_listener *listener, + void *data) +{ + return s_Wl.proxyAddListener( + (struct wl_proxy *) wl_keyboard, + (void (**)(void)) listener, data); +} + static void surfaceEnter( void* userData, struct wl_surface* surface, @@ -1143,6 +1237,105 @@ static struct wl_surface_listener surfaceListener = { .leave = surfaceLeave }; +static void pointerEnter( + void* userData, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + WindowData* data = findWindowData((PalWindow*)surface); + if (data && data->cursor) { + // our window + WaylandCursor* cursor = data->cursor; + wlPointerSetCursor( + pointer, + serial, + cursor->surface, + cursor->hotspotX, + cursor->hotspotY); + } +} + +static void pointerLeave( + void* userData, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface) +{ + +} + +static void pointerMotion( + void* userData, + struct wl_pointer* pointer, + uint32_t time, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + +} + +static void pointerButton( + void* userData, + struct wl_pointer* pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + +} + +static void pointerAxis( + void* userData, + struct wl_pointer* pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + +} + +static struct wl_pointer_listener pointerListener = { + .enter = pointerEnter, + .leave = pointerLeave, + .motion = pointerMotion, + .button = pointerButton, + .axis = pointerAxis +}; + +static void seatCapabilities( + void* userData, + struct wl_seat* seat, + enum wl_seat_capability caps) +{ + if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { + s_Wl.keyboard = wlSeatGetKeyboard(seat); + + // TODO: add keyboard listener + } + + if (caps & WL_SEAT_CAPABILITY_POINTER) { + s_Wl.pointer = wlSeatGetPointer(seat); + wlPointerAddListener(s_Wl.pointer, &pointerListener, nullptr); + } +} + +static void seatName( + void* userData, + struct wl_seat* seat, + const char* name) +{ + +} + +static struct wl_seat_listener seatListener = { + .capabilities = seatCapabilities, + .name = seatName +}; + #endif // PAL_HAS_WAYLAND #pragma endregion @@ -1156,7 +1349,9 @@ struct xdg_toplevel; // forward declare static struct wl_buffer* createShmBuffer( int width, - int height); + int height, + const Uint8* pixels, + bool cursor); const struct wl_interface xdg_popup_interface; const struct wl_interface xdg_positioner_interface; @@ -1274,7 +1469,7 @@ static void xdgSurfaceConfigure( // create a new buffer with the new size struct wl_buffer* buffer = nullptr; - buffer = createShmBuffer(winData->w, winData->h); + buffer = createShmBuffer(winData->w, winData->h, nullptr, false); if (!buffer) { return; } @@ -5280,7 +5475,9 @@ static int createShmFile(Uint64 size) static struct wl_buffer* createShmBuffer( int width, - int height) + int height, + const Uint8* pixels, + bool cursor) { int stride = width * 4; Uint64 size = stride * height; @@ -5298,7 +5495,30 @@ static struct wl_buffer* createShmBuffer( return nullptr; } - memset(data, 0xFF, size); // white + enum wl_shm_format format = WL_SHM_FORMAT_XRGB8888; + if (cursor) { + format = WL_SHM_FORMAT_ARGB8888; + Uint32* dataPixels = (Uint32*)data; + + // convert from RGBA8 to ARGB32 + for (int i = 0; i < width * height; i++) { + Uint8 r = pixels[i * 4 + 0]; // Red + Uint8 g = pixels[i * 4 + 1]; // Green + Uint8 b = pixels[i * 4 + 2]; // Blue + Uint8 a = pixels[i * 4 + 3]; // Alpha + + // clang-format off + dataPixels[i] = ((unsigned long)a << 24) | + ((unsigned long)r << 16) | + ((unsigned long)g << 8) | + ((unsigned long)b); + // clang-format on + } + + } else { + memset(data, 0xFF, size); // white + } + pool = wlShmCreatePool(s_Wl.shm, fd, size); if (!pool) { return nullptr; @@ -5310,7 +5530,7 @@ static struct wl_buffer* createShmBuffer( width, height, stride, - WL_SHM_FORMAT_XRGB8888); + format); if (!buffer) { return nullptr; @@ -5370,9 +5590,8 @@ static void wlGlobalHandle( name, &xdg_wm_base_interface, 1); - xdgWmBaseAddListener(s_Wl.xdgBase, &wmBaseListener, nullptr); - s_Wl.displayFlush(s_Wl.display); + xdgWmBaseAddListener(s_Wl.xdgBase, &wmBaseListener, nullptr); } else if (strcmp(interface, "wl_shm") == 0) { s_Wl.shm = wlRegistryBind( @@ -5381,6 +5600,15 @@ static void wlGlobalHandle( s_Wl.shmInterface, 1); + } else if (strcmp(interface, "wl_seat") == 0) { + s_Wl.seat = wlRegistryBind( + registry, + name, + s_Wl.seatInterface, + 4); + + wlSeatAddListener(s_Wl.seat , &seatListener, nullptr); + } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { s_Wl.decorationManager = wlRegistryBind( registry, @@ -5578,6 +5806,7 @@ PalResult wlInitVideo() s_Wl.shmPoolInterface = dlsym(s_Wl.handle, "wl_shm_pool_interface"); s_Wl.regionInterface = dlsym(s_Wl.handle, "wl_region_interface"); s_Wl.pointerInterface = dlsym(s_Wl.handle, "wl_pointer_interface"); + s_Wl.keyboardInterface = dlsym(s_Wl.handle, "wl_keyboard_interface"); // load function procs s_Wl.displayConnect = (wl_display_connect_fn)dlsym( @@ -5656,6 +5885,9 @@ PalResult wlInitVideo() wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); s_Wl.displayRoundtrip(s_Wl.display); + // do a roundtrip again to get remaining handles + s_Wl.displayRoundtrip(s_Wl.display); + if (!s_Wl.compositor || !s_Wl.xdgBase || !s_Wl.shm) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -5961,7 +6193,7 @@ PalResult wlCreateWindow( } if (!(info->style & PAL_WINDOW_STYLE_BORDERLESS)) { - if (s_Wl.decorationManager) { + if (!s_Wl.decorationManager) { // user wants decorated window but its not supported return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; } @@ -6060,7 +6292,7 @@ PalResult wlCreateWindow( // create a white buffer for the surface struct wl_buffer* buffer = nullptr; - buffer = createShmBuffer(data->w, data->h); + buffer = createShmBuffer(data->w, data->h, nullptr, false); if (!buffer) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -6304,6 +6536,35 @@ PalResult wlCreateCursor( const PalCursorCreateInfo* info, PalCursor** outCursor) { + WaylandCursor* cursor = nullptr; + cursor = palAllocate(s_Video.allocator, sizeof(WaylandCursor), 0); + if (!cursor) { + return PAL_RESULT_OUT_OF_MEMORY; + } + + cursor->surface = wlCompositorCreateSurface(s_Wl.compositor); + if (!cursor->surface) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + cursor->buffer = createShmBuffer( + info->width, + info->height, + info->pixels, + true); + + if (!cursor->buffer) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + wlSurfaceAttach(cursor->surface, cursor->buffer, 0, 0); + wlSurfaceDamageBuffer(cursor->surface, 0, 0, info->width, info->height); + wlSurfaceCommit(cursor->surface); + + cursor->hotspotX = info->xHotspot; + cursor->hotspotY = info->yHotspot; + + *outCursor = (PalCursor*)cursor; return PAL_RESULT_SUCCESS; } @@ -6316,11 +6577,15 @@ PalResult wlCreateCursorFrom( void wlDestroyCursor(PalCursor* cursor) { - + WaylandCursor* waylandCursor = (WaylandCursor*)cursor; + wlBufferDestroy(waylandCursor->buffer); + wlSurfaceDestroy(waylandCursor->surface); + palFree(s_Video.allocator, waylandCursor); } void wlShowCursor(bool show) { + // not supported return; } @@ -6330,7 +6595,6 @@ PalResult wlClipCursor( { if (!(s_Video.features & PAL_VIDEO_FEATURE_CLIP_CURSOR)) { return PAL_RESULT_VIDEO_FEATURE_NOT_SUPPORTED; - } return PAL_RESULT_SUCCESS; @@ -6356,6 +6620,12 @@ PalResult wlSetWindowCursor( PalWindow* window, PalCursor* cursor) { + WindowData* data = findWindowData(window); + if (!data) { + return PAL_RESULT_INVALID_WINDOW; + } + + data->cursor = cursor; return PAL_RESULT_SUCCESS; } diff --git a/tests/cursor_test.c b/tests/cursor_test.c index 656e105..6781cfe 100644 --- a/tests/cursor_test.c +++ b/tests/cursor_test.c @@ -99,6 +99,13 @@ bool cursorTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; createInfo.title = "PAL cursor Window"; + // check if we support decorated windows (title bar, close etc) + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + // create the window with the create info struct result = palCreateWindow(&createInfo, &window); if (result != PAL_RESULT_SUCCESS) { From 153ae5eb197d794a0d6f260cbed074ac49940c7d Mon Sep 17 00:00:00 2001 From: nichcode Date: Sun, 9 Nov 2025 11:53:38 +0000 Subject: [PATCH 10/51] mouse events --- CHANGELOG.md | 3 + include/pal/pal_core.h | 72 ++++++++ include/pal/pal_video.h | 21 ++- src/video/pal_video_linux.c | 332 +++++++++++++++++++++++++++++------- src/video/pal_video_win32.c | 8 + tests/input_window_test.c | 20 +++ tests/monitor_mode_test.c | 1 + tests/tests_main.c | 4 +- 8 files changed, 395 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fc159a..4ae38f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,9 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Video:** Added Wayland-based backend support. - **Video:** Added **palGetVideoFeaturesEx()** to check old and extended supported features. - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. +- **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. +- **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. +- **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. ### Tests - Added native integration example: demonstrating **Native API Integration with PAL API**. see **native_integration_test.c**. diff --git a/include/pal/pal_core.h b/include/pal/pal_core.h index bc02a81..309d12f 100644 --- a/include/pal/pal_core.h +++ b/include/pal/pal_core.h @@ -33,6 +33,7 @@ freely, subject to the following restrictions: #define _PAL_CORE_H #include +#include #ifdef __cplusplus #define PAL_EXTERN_C extern "C" @@ -75,6 +76,12 @@ typedef _Bool bool; #define PAL_API PAL_EXTERN_C #endif // _PAL_BUILD_DLL +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define PAL_BIG_ENDIAN 1 +#else +#define PAL_BIG_ENDIAN 0 +#endif // __ORDER_BIG_ENDIAN__ + #define PAL_BIT(x) 1 << x #define PAL_BIT64(x) 1ULL << x @@ -463,6 +470,33 @@ static inline Int64 PAL_CALL palPackPointer(void* ptr) return (Int64)(UintPtr)ptr; } +/** + * @brief Combine two floats into a single 64-bit signed integer. + * + * @return The combined 64-bit signed integer. + * + * Thread safety: This function is thread safe. + * + * @since 1.3 + * @ingroup pal_core + * @sa palUnpackFloat + */ +static inline Int64 PAL_CALL palPackFloat( + float low, + float high) +{ + Int64 combined = 0; +#if PAL_BIG_ENDIAN + memcpy(&((Uint32*)&combined)[0], &high, sizeof(float)); + memcpy(&((Uint32*)&combined)[1], &low, sizeof(float)); +#else + memcpy(&((Uint32*)&combined)[0], &low, sizeof(float)); + memcpy(&((Uint32*)&combined)[1], &high, sizeof(float)); +#endif // PAL_BIG_ENDIAN + + return combined; +} + /** * @brief Retrieve two 32-bit unsigned integers from a 64-bit signed integer. * @@ -532,6 +566,44 @@ static inline void* PAL_CALL palUnpackPointer(Int64 data) return (void*)(UintPtr)data; } +/** + * @brief Retrieve two floats from a 64-bit signed integer. + * + * @param[out] outLow Low value of the 64-bit signed integer. + * @param[out] outHigh High value of the 64-bit signed integer. + * + * Thread safety: This function is thread-safe if `outLow` and `outHigh` are + * thread local. + * + * @since 1.3 + * @ingroup pal_core + * @sa palPackFloat + */ +static inline void PAL_CALL palUnpackFloat( + Int64 data, + float* low, + float* high) +{ +#if PAL_BIG_ENDIAN + if (low) { + memcpy(low, &((Uint32*)&data)[1], sizeof(float)); + } + + if (high) { + memcpy(high, &((Uint32*)&data)[0], sizeof(float)); + } +#else + if (low) { + memcpy(low, &((Uint32*)&data)[0], sizeof(float)); + } + + if (high) { + memcpy(high, &((Uint32*)&data)[1], sizeof(float)); + } + +#endif // PAL_BIG_ENDIAN +} + /** @} */ // end of pal_core group #endif // _PAL_CORE_H \ No newline at end of file diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index d7488bd..10c6b9f 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -1428,10 +1428,29 @@ PAL_API void PAL_CALL palGetMouseDelta( * @since 1.0 * @ingroup pal_video */ -void PAL_CALL palGetMouseWheelDelta( +PAL_API void PAL_CALL palGetMouseWheelDelta( Int32* dx, Int32* dy); +/** + * @brief Get the raw wheel delta of the mouse in floats. + * + * The video system must be initialized before this call. + * The wheel delta will be updated when palUpdateVideo() is called. + * + * @param[in] dx Pointer to recieve the mouse wheel delta x in floats. Can be nullptr. + * @param[in] dy Pointer to recieve the mouse wheel delta y in floats. Can be nullptr. + * + * Thread safety: This function is thread-safe if `dx` and `dy` are thread + * local. + * + * @since 1.3 + * @ingroup pal_video + */ +PAL_API void PAL_CALL palGetRawMouseWheelDelta( + float* dx, + float* dy); + /** * @brief Check if the provided window is visible. * diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index c237632..a7c1b1f 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -54,6 +54,7 @@ freely, subject to the following restrictions: #include #include #include +#include #endif // PAL_HAS_WAYLAND // ================================================== @@ -69,13 +70,7 @@ typedef void* EGLContext; typedef void* EGLDisplay; typedef void* EGLNativeDisplayType; -/* C++ / C typecast macros for special EGL handle values */ -#if defined(__cplusplus) -#define EGL_CAST(type, value) (static_cast(value)) -#else #define EGL_CAST(type, value) ((type)(value)) -#endif - #define EGL_OPENGL_API 0x30A2 #define EGL_OPENGL_BIT 0x0008 #define EGL_NO_CONTEXT EGL_CAST(EGLContext, 0) @@ -157,13 +152,20 @@ typedef struct { } MonitorData; typedef struct { + bool pendingScroll; Int32 lastX; Int32 lastY; Int32 dx; Int32 dy; Int32 WheelX; Int32 WheelY; + float WheelXf; + float WheelYf; bool state[PAL_MOUSE_BUTTON_MAX]; + double tmpScrollX; + double tmpScrollY; + double accumScrollX; + double accumScrollY; } Mouse; typedef struct { @@ -870,6 +872,7 @@ typedef struct { struct wl_keyboard* keyboard; struct zxdg_decoration_manager_v1* decorationManager; struct zwp_pointer_constraints* pointerConstraints; + struct wl_surface* pointersurface; const struct wl_interface* outputInterface; const struct wl_interface* seatInterface; @@ -1215,7 +1218,7 @@ static inline int wlKeyboardAddListener( (void (**)(void)) listener, data); } -static void surfaceEnter( +static void surfaceHandleEnter( void* userData, struct wl_surface* surface, struct wl_output* output) @@ -1224,7 +1227,7 @@ static void surfaceEnter( } -static void surfaceLeave( +static void surfaceHandleLeave( void* userData, struct wl_surface* surface, struct wl_output* output) @@ -1233,11 +1236,11 @@ static void surfaceLeave( } static struct wl_surface_listener surfaceListener = { - .enter = surfaceEnter, - .leave = surfaceLeave + .enter = surfaceHandleEnter, + .leave = surfaceHandleLeave }; -static void pointerEnter( +static void pointerHandleEnter( void* userData, struct wl_pointer* pointer, uint32_t serial, @@ -1256,28 +1259,68 @@ static void pointerEnter( cursor->hotspotX, cursor->hotspotY); } + + // cache the surface the pointer is currently on + s_Wl.pointersurface = surface; } -static void pointerLeave( +static void pointerHandleLeave( void* userData, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface) { - + if (s_Wl.pointersurface == surface) { + s_Wl.pointersurface == nullptr; + } } -static void pointerMotion( +static void pointerHandleMotion( void* userData, struct wl_pointer* pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - + int x = wl_fixed_to_int(surface_x); + int y = wl_fixed_to_int(surface_y); + const int dx = x - s_Mouse.lastX; + const int dy = y - s_Mouse.lastY; + + PalDispatchMode mode = PAL_DISPATCH_NONE; + PalWindow* window = (PalWindow*)s_Wl.pointersurface; + if (s_Video.eventDriver && window) { + // we only push a mouse move only if we are on a window + PalEventDriver* driver = s_Video.eventDriver; + PalEventType type = PAL_EVENT_MOUSE_MOVE; + mode = palGetEventDispatchMode(driver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackInt32(x, y); + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + } + + // push a mouse delta event + type = PAL_EVENT_MOUSE_DELTA; + mode = palGetEventDispatchMode(driver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackInt32(dx, dy); + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + } + } + + s_Mouse.lastX = x; + s_Mouse.lastY = y; + s_Mouse.dx = dx; + s_Mouse.dy = dy; } -static void pointerButton( +static void pointerHandleButton( void* userData, struct wl_pointer* pointer, uint32_t serial, @@ -1285,28 +1328,163 @@ static void pointerButton( uint32_t button, uint32_t state) { + PalWindow* window = nullptr; + if (s_Wl.pointersurface) { + window = (PalWindow*)s_Wl.pointersurface; + + } else { + // cannot recieve events without a focused surface + return; + } + bool pressed = state == WL_POINTER_BUTTON_STATE_PRESSED; + PalMouseButton _button = 0; + PalEventType type = PAL_EVENT_MOUSE_BUTTONUP; + + if (button == BTN_LEFT) { + _button = PAL_MOUSE_BUTTON_LEFT; + + } else if (button == BTN_RIGHT) { + _button = PAL_MOUSE_BUTTON_RIGHT; + + } else if (button == BTN_MIDDLE) { + _button = PAL_MOUSE_BUTTON_MIDDLE; + + } else if (button == BTN_SIDE) { + _button = PAL_MOUSE_BUTTON_X1; + + } else if (button == BTN_EXTRA) { + _button = PAL_MOUSE_BUTTON_X2; + } + + if (pressed) { + type = PAL_EVENT_MOUSE_BUTTONDOWN; + } + + s_Mouse.state[_button] = pressed; + if (s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(driver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = _button; + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + } + } } -static void pointerAxis( +static void pointerHandleAxis( void* userData, struct wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + double delta = wl_fixed_to_double(value); + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + s_Mouse.tmpScrollX += delta; + s_Mouse.accumScrollX += delta; + + } else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + s_Mouse.tmpScrollY += delta; + s_Mouse.accumScrollY += delta; + } + + s_Mouse.pendingScroll = true; +} + +static void pointerHandleAxisDiscrete( + void* userData, + struct wl_pointer* pointer, + uint32_t axis, + int32_t discrete) +{ + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { + s_Mouse.tmpScrollX += discrete; + s_Mouse.accumScrollX += discrete; + + } else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + s_Mouse.tmpScrollY += discrete; + s_Mouse.accumScrollY += discrete; + } + + s_Mouse.pendingScroll = true; +} + +static void pointerHandleFrame( + void* userData, + struct wl_pointer* pointer) +{ + if (!s_Mouse.pendingScroll) { + // no wheel event + return; + } + + PalWindow* window = nullptr; + if (s_Wl.pointersurface) { + window = (PalWindow*)s_Wl.pointersurface; + + } else { + // cannot recieve events without a focused surface + return; + } + + const int dx = (int)s_Mouse.accumScrollX; + const int dy = (int)s_Mouse.accumScrollY; + if (s_Video.eventDriver) { + PalEventType type = PAL_EVENT_MOUSE_WHEEL; + PalEventDriver* driver = s_Video.eventDriver; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(driver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackUint32(dx, dy); + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + } + } + + s_Mouse.WheelX = dx; + s_Mouse.WheelY = dy; + s_Mouse.accumScrollX -= dx; + s_Mouse.accumScrollY -= dy; + s_Mouse.pendingScroll = false; +} + +static void pointerHandleAxisSource( + void* userData, + struct wl_pointer* pointer, + uint32_t axis_source) +{ + +} + +static void pointerHandleAxisStop( + void* userData, + struct wl_pointer* pointer, + uint32_t time, + uint32_t axis) +{ } static struct wl_pointer_listener pointerListener = { - .enter = pointerEnter, - .leave = pointerLeave, - .motion = pointerMotion, - .button = pointerButton, - .axis = pointerAxis + .enter = pointerHandleEnter, + .leave = pointerHandleLeave, + .motion = pointerHandleMotion, + .button = pointerHandleButton, + .axis = pointerHandleAxis, + .axis_discrete = pointerHandleAxisDiscrete, + .frame = pointerHandleFrame, + .axis_source = pointerHandleAxisSource, + .axis_stop = pointerHandleAxisStop }; -static void seatCapabilities( +static void seatHandleCapabilities( void* userData, struct wl_seat* seat, enum wl_seat_capability caps) @@ -1323,7 +1501,7 @@ static void seatCapabilities( } } -static void seatName( +static void seatHandleName( void* userData, struct wl_seat* seat, const char* name) @@ -1332,8 +1510,8 @@ static void seatName( } static struct wl_seat_listener seatListener = { - .capabilities = seatCapabilities, - .name = seatName + .capabilities = seatHandleCapabilities, + .name = seatHandleName }; #endif // PAL_HAS_WAYLAND @@ -1447,7 +1625,7 @@ static inline void xdgSurfaceAckConfigure( serial); } -static void wmBasePing( +static void wmBaseHandlePing( void* data, struct xdg_wm_base* base, uint32_t serial) @@ -1455,7 +1633,7 @@ static void wmBasePing( xdgWmBasePong(base, serial); } -static void xdgSurfaceConfigure( +static void xdgSurfaceHandleConfigure( void* data, struct xdg_surface* surface, uint32_t serial) @@ -1486,15 +1664,17 @@ static void xdgSurfaceConfigure( winData->buffer = buffer; // push a window resize event - PalEventType type = PAL_EVENT_WINDOW_SIZE; - PalDispatchMode mode = PAL_DISPATCH_NONE; - mode = palGetEventDispatchMode(s_Video.eventDriver, type); - if (mode != PAL_DISPATCH_NONE) { - PalEvent event = {0}; - event.type = type; - event.data = palPackUint32(winData->w, winData->h); - event.data2 = palPackPointer(winData->window); - palPushEvent(s_Video.eventDriver, &event); + if (s_Video.eventDriver) { + PalEventType type = PAL_EVENT_WINDOW_SIZE; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackUint32(winData->w, winData->h); + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } } } @@ -1505,20 +1685,22 @@ static void xdgSurfaceConfigure( // push a window state event // we dont recreate buffers over here // since we already create the buffer with the new size - PalEventType type = PAL_EVENT_WINDOW_STATE; - PalDispatchMode mode = PAL_DISPATCH_NONE; - mode = palGetEventDispatchMode(s_Video.eventDriver, type); - if (mode != PAL_DISPATCH_NONE) { - PalEvent event = {0}; - event.type = type; - event.data = winData->state; - event.data2 = palPackPointer(winData->window); - palPushEvent(s_Video.eventDriver, &event); + if (s_Video.eventDriver) { + PalEventType type = PAL_EVENT_WINDOW_STATE; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = winData->state; + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } } } } -static void xdgToplevelConfigure( +static void xdgToplevelHandleConfigure( void* data, struct xdg_toplevel* toplevel, int32_t width, @@ -1550,19 +1732,22 @@ static void xdgToplevelConfigure( } -static void xdgToplevelClose( +static void xdgToplevelHandleClose( void* data, struct xdg_toplevel* toplevel) { WindowData* winData = (WindowData*)data; - PalEventType type = PAL_EVENT_WINDOW_CLOSE; - PalDispatchMode mode = palGetEventDispatchMode(s_Video.eventDriver, type); - if (mode != PAL_DISPATCH_NONE) { - PalEvent event = {0}; - event.type = type; - event.data2 = palPackPointer(winData->window); - palPushEvent(s_Video.eventDriver, &event); + if (s_Video.eventDriver) { + PalEventType type = PAL_EVENT_WINDOW_CLOSE; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } } } @@ -1847,16 +2032,16 @@ static void setupXdgShellProtocol() } static const struct xdg_wm_base_listener wmBaseListener = { - .ping = wmBasePing + .ping = wmBaseHandlePing }; static const struct xdg_surface_listener xdgSurfaceListener = { - .configure = xdgSurfaceConfigure + .configure = xdgSurfaceHandleConfigure }; static const struct xdg_toplevel_listener xdgToplevelListener = { - .configure = xdgToplevelConfigure, - .close = xdgToplevelClose, + .configure = xdgToplevelHandleConfigure, + .close = xdgToplevelHandleClose, .configure_bounds = nullptr, .wm_capabilities = nullptr }; @@ -5605,7 +5790,7 @@ static void wlGlobalHandle( registry, name, s_Wl.seatInterface, - 4); + 5); wlSeatAddListener(s_Wl.seat , &seatListener, nullptr); @@ -5912,6 +6097,8 @@ void wlShutdownVideo() void wlUpdateVideo() { // flush pending requests + s_Mouse.tmpScrollX = 0; + s_Mouse.tmpScrollY = 0; s_Wl.displayFlush(s_Wl.display); while (s_Wl.prepareRead(s_Wl.display) != 0) { @@ -6019,15 +6206,16 @@ PalResult wlEnumerateMonitors( PalMonitor** outMonitors) { int _count = 0; + int index = 0; int maxCount = outMonitors ? *count : 0; if (outMonitors) { if (_count < maxCount) { for (int i = 0; i < maxCount; i++) { // we get the monitor from our cache array PalMonitor* monitor = nullptr; - for (int y = 0; y < s_Video.maxMonitorData; y++) { - if (s_Video.monitorData[y].used) { - monitor = s_Video.monitorData[y].monitor; + for (;index < s_Video.maxMonitorData;) { + if (s_Video.monitorData[index].used) { + monitor = s_Video.monitorData[index].monitor; break; } } @@ -6035,6 +6223,7 @@ PalResult wlEnumerateMonitors( // write to user provided array outMonitors[_count] = monitor; _count++; // index into user array + index++; } } } @@ -7264,6 +7453,23 @@ void PAL_CALL palGetMouseWheelDelta( } } +void PAL_CALL palGetRawMouseWheelDelta( + float* dx, + float* dy) +{ + if (!s_Video.initialized) { + return; + } + + if (dx) { + *dx = (float)s_Mouse.tmpScrollX; + } + + if (dy) { + *dy = (float)s_Mouse.tmpScrollY; + } +} + bool PAL_CALL palIsWindowVisible(PalWindow* window) { if (!s_Video.initialized) { diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index 49488bf..b6d9e9d 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -2257,6 +2257,14 @@ void PAL_CALL palGetMouseWheelDelta( } } +void PAL_CALL palGetRawMouseWheelDelta( + float* dx, + float* dy) +{ + // TODO: + +} + bool PAL_CALL palIsWindowVisible(PalWindow* window) { if (!s_Video.initialized) { diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 588cc0e..a7b8d00 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -366,8 +366,19 @@ static inline void onMouseWheel(const PalEvent* event) { Int32 dx, dy; // dx == low, dy == high palUnpackInt32(event->data, &dx, &dy); + + // get the raw wheel delta (float) + float fdx, fdy; + palGetRawMouseWheelDelta(&fdx, &fdy); + PalWindow* window = palUnpackPointer(event->data2); palLog(nullptr, "%s: Mouse Wheel: (%d, %d)", dispatchString, dx, dy); + palLog( + nullptr, + "%s: Mouse Wheel Float: (%.2f, %.2f)", + dispatchString, + fdx, + fdy); } static void PAL_CALL onEvent( @@ -450,6 +461,15 @@ bool inputWindowTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; createInfo.title = "PAL Input Window"; + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + + // check if we support decorated windows (title bar, close etc) + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + // create the window with the create info struct result = palCreateWindow(&createInfo, &window); if (result != PAL_RESULT_SUCCESS) { diff --git a/tests/monitor_mode_test.c b/tests/monitor_mode_test.c index 61a1bdb..e67db81 100644 --- a/tests/monitor_mode_test.c +++ b/tests/monitor_mode_test.c @@ -121,6 +121,7 @@ bool monitorModeTest() palLog(nullptr, " Size: (%d, %d)", current.width, current.height); palLog(nullptr, " RefreshRate: %d", current.refreshRate); palLog(nullptr, " Bits Per Pixel: %d", current.bpp); + palLog(nullptr, ""); } // shutdown the video system diff --git a/tests/tests_main.c b/tests/tests_main.c index c0c4254..99cdf0d 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -31,8 +31,8 @@ int main(int argc, char** argv) // registerTest("Monitor Mode Test", monitorModeTest); // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); - registerTest("Cursor Test", cursorTest); - // registerTest("Input Window Test", inputWindowTest); + // registerTest("Cursor Test", cursorTest); + registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); From 4f2a1c880a057bdf70d5c1ab21e131ac9f58795e Mon Sep 17 00:00:00 2001 From: nichcode Date: Sun, 9 Nov 2025 18:02:20 +0000 Subject: [PATCH 11/51] key events --- src/video/pal_video_linux.c | 607 ++++++++++++++++++++++++++++-------- 1 file changed, 471 insertions(+), 136 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index a7c1b1f..e79e907 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -54,7 +54,11 @@ freely, subject to the following restrictions: #include #include #include +#include + #include +#include +#include #endif // PAL_HAS_WAYLAND // ================================================== @@ -856,12 +860,31 @@ typedef int (*wl_display_read_events_fn)(struct wl_display*); typedef int (*wl_display_get_fd_fn)(struct wl_display*); typedef void (*wl_display_cancel_read_fn)(struct wl_display*); +// xkb +typedef void (*xkb_keymap_unref_fn)(struct xkb_keymap*); +typedef struct xkb_state* (*xkb_state_new_fn)(struct xkb_keymap*); +typedef void (*xkb_state_unref_fn)(struct xkb_state*); +typedef void (*xkb_context_unref_fn)(struct xkb_context*); +typedef struct xkb_context* (*xkb_context_new_fn)(enum xkb_context_flags); +typedef uint32_t (*xkb_keysym_to_utf32_fn)(xkb_keysym_t); + +typedef xkb_keysym_t (*xkb_state_key_get_one_sym_fn)( + struct xkb_state*, + xkb_keycode_t); + +typedef struct xkb_keymap* (*xkb_keymap_new_from_string_fn)( + struct xkb_context*, + const char*, + enum xkb_keymap_format, + enum xkb_keymap_compile_flags); + typedef struct { bool checkFeatures; bool modesPhase; int monitorCount; void* handle; + void* xkbCommon; struct wl_display* display; struct wl_registry* registry; struct xdg_wm_base* xdgBase; @@ -872,7 +895,12 @@ typedef struct { struct wl_keyboard* keyboard; struct zxdg_decoration_manager_v1* decorationManager; struct zwp_pointer_constraints* pointerConstraints; - struct wl_surface* pointersurface; + struct wl_surface* pointerSurface; + struct wl_surface* keyboardSurface; + + struct xkb_context* inputContext; + struct xkb_keymap* keymap; + struct xkb_state* state; const struct wl_interface* outputInterface; const struct wl_interface* seatInterface; @@ -902,6 +930,15 @@ typedef struct { wl_display_read_events_fn readEvents; wl_display_get_fd_fn displayGetFd; wl_display_cancel_read_fn cancelRead; + + xkb_keymap_unref_fn xkbKeymapUnref; + xkb_state_new_fn xkbStateNew; + xkb_state_unref_fn xkbStateUnref; + xkb_context_unref_fn xkbContextUnref; + xkb_context_new_fn xkbContextNew; + xkb_state_key_get_one_sym_fn xkbStateKeyGetOneSym; + xkb_keymap_new_from_string_fn xkbKeymapNewFromString; + xkb_keysym_to_utf32_fn xkbKeysymToUtf32; } Wayland; typedef struct { @@ -1261,7 +1298,7 @@ static void pointerHandleEnter( } // cache the surface the pointer is currently on - s_Wl.pointersurface = surface; + s_Wl.pointerSurface = surface; } static void pointerHandleLeave( @@ -1270,8 +1307,8 @@ static void pointerHandleLeave( uint32_t serial, struct wl_surface* surface) { - if (s_Wl.pointersurface == surface) { - s_Wl.pointersurface == nullptr; + if (s_Wl.pointerSurface == surface) { + s_Wl.pointerSurface == nullptr; } } @@ -1288,7 +1325,7 @@ static void pointerHandleMotion( const int dy = y - s_Mouse.lastY; PalDispatchMode mode = PAL_DISPATCH_NONE; - PalWindow* window = (PalWindow*)s_Wl.pointersurface; + PalWindow* window = (PalWindow*)s_Wl.pointerSurface; if (s_Video.eventDriver && window) { // we only push a mouse move only if we are on a window PalEventDriver* driver = s_Video.eventDriver; @@ -1329,8 +1366,8 @@ static void pointerHandleButton( uint32_t state) { PalWindow* window = nullptr; - if (s_Wl.pointersurface) { - window = (PalWindow*)s_Wl.pointersurface; + if (s_Wl.pointerSurface) { + window = (PalWindow*)s_Wl.pointerSurface; } else { // cannot recieve events without a focused surface @@ -1424,8 +1461,8 @@ static void pointerHandleFrame( } PalWindow* window = nullptr; - if (s_Wl.pointersurface) { - window = (PalWindow*)s_Wl.pointersurface; + if (s_Wl.pointerSurface) { + window = (PalWindow*)s_Wl.pointerSurface; } else { // cannot recieve events without a focused surface @@ -1472,6 +1509,197 @@ static void pointerHandleAxisStop( } +static void keyboardHandleEnter( + void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface, + struct wl_array* keys) +{ + // cache the surface the keyboard is currently on + s_Wl.keyboardSurface = surface; +} + +static void keyboardHandleLeave( + void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface) +{ + if (s_Wl.keyboardSurface == surface) { + s_Wl.keyboardSurface == nullptr; + } +} + +static void keyboardHandleRemap( + void* userData, + struct wl_keyboard* keyboard, + uint32_t format, + int32_t fd, + uint32_t size) +{ + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + + struct xkb_keymap* keymap = nullptr; + struct xkb_state* state = nullptr; + + char* keymapStr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0); + if (keymapStr == MAP_FAILED) { + close(fd); + return; + } + + keymap = s_Wl.xkbKeymapNewFromString( + s_Wl.inputContext, + keymapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + XKB_KEYMAP_COMPILE_NO_FLAGS); + + if (!keymap) { + return; + } + + munmap(keymapStr, size); + close(fd); + + state = s_Wl.xkbStateNew(keymap); + if (!state) { + return; + } + + // check if we have old keymap and state + if (s_Wl.state) { + s_Wl.xkbStateUnref(s_Wl.state); + s_Wl.xkbKeymapUnref(s_Wl.keymap); + } + + s_Wl.state = state; + s_Wl.keymap = keymap; +} + +static void keyboardHandleKey( + void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + PalWindow* window = nullptr; + if (s_Wl.keyboardSurface) { + window = (PalWindow*)s_Wl.keyboardSurface; + + } else { + // cannot recieve events without a focused surface + return; + } + + PalScancode scancode = 0; + PalKeycode keycode = 0; + bool pressed = (state == WL_KEYBOARD_KEY_STATE_PRESSED); + PalEventType type = PAL_EVENT_KEYUP; + PalDispatchMode mode = PAL_DISPATCH_NONE; + xkb_keysym_t keySym = s_Wl.xkbStateKeyGetOneSym(s_Wl.state, key + 8); + + // special handling + if (key == 119) { + scancode = PAL_SCANCODE_PAUSE; + } else if (key == 107) { + scancode = PAL_SCANCODE_END; + } else if (key == 103) { + scancode = PAL_SCANCODE_UP; + } else if (key == 102) { + scancode = PAL_SCANCODE_HOME; + + } else { + scancode = s_Keyboard.scancodes[key]; + } + + // printable and text input keys are from the range + // 39 (PAL_KEYCODE_APOSTROPHE) and 122 (PAL_KEYCODE_Z) + // The rest are almost the same as their scancode + // Maybe there will be a layout that makes this wrong + // but for now this works + if (keySym >= XKB_KEY_apostrophe && keySym <= XKB_KEY_z) { + // a printable or input key + keycode = s_Keyboard.keycodes[keySym]; + + } else { + // Since PalKeycode and PalScancode have the same integers + // we can make a direct cast without a table + // Examle: PAL_KEYCODE_A(int 0) == PAL_SCANCODE_A(int 0) + keycode = (PalKeycode)(Uint32)scancode; + } + + // If we got a keySym but its not mapped into our keycode array + // we do a direct cast as well + if (keycode == PAL_KEYCODE_UNKNOWN) { + keycode = (PalKeycode)(Uint32)scancode; + } + + bool repeat = s_Keyboard.keycodeState[keycode]; + s_Keyboard.scancodeState[scancode] = pressed; + s_Keyboard.keycodeState[keycode] = pressed; + + if (pressed) { + if (repeat) { + type = PAL_EVENT_KEYREPEAT; + } else { + type = PAL_EVENT_KEYDOWN; + } + } + + if (s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + mode = palGetEventDispatchMode(driver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackUint32(keycode, scancode); + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + } + + // check for char event if enabled + type = PAL_EVENT_KEYCHAR; + mode = palGetEventDispatchMode(driver, type); + if (mode == PAL_DISPATCH_NONE) { + return; + } + + Uint32 codepoint = s_Wl.xkbKeysymToUtf32(keySym); + PalEvent event = {0}; + event.type = type; + event.data = codepoint; + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + } +} + +static void keyboardHandleRepeatInfo( + void* userData, + struct wl_keyboard* keyboard, + int32_t rate, + int32_t delay) +{ + +} + +static void keyboardHandleModifiers( + void* userData, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ + +} + static struct wl_pointer_listener pointerListener = { .enter = pointerHandleEnter, .leave = pointerHandleLeave, @@ -1484,6 +1712,15 @@ static struct wl_pointer_listener pointerListener = { .axis_stop = pointerHandleAxisStop }; +static struct wl_keyboard_listener keyboardListener = { + .enter = keyboardHandleEnter, + .leave = keyboardHandleLeave, + .keymap = keyboardHandleRemap, + .key = keyboardHandleKey, + .repeat_info = keyboardHandleRepeatInfo, + .modifiers = keyboardHandleModifiers +}; + static void seatHandleCapabilities( void* userData, struct wl_seat* seat, @@ -1491,8 +1728,7 @@ static void seatHandleCapabilities( { if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { s_Wl.keyboard = wlSeatGetKeyboard(seat); - - // TODO: add keyboard listener + wlKeyboardAddListener(s_Wl.keyboard, &keyboardListener, nullptr); } if (caps & WL_SEAT_CAPABILITY_POINTER) { @@ -2412,6 +2648,129 @@ static void freeMonitorData(PalMonitor* monitor) } } +static void createScancodeTable() +{ + // Letters + s_Keyboard.scancodes[0x01E] = PAL_SCANCODE_A; + s_Keyboard.scancodes[0x030] = PAL_SCANCODE_B; + s_Keyboard.scancodes[0x02E] = PAL_SCANCODE_C; + s_Keyboard.scancodes[0x020] = PAL_SCANCODE_D; + s_Keyboard.scancodes[0x012] = PAL_SCANCODE_E; + s_Keyboard.scancodes[0x021] = PAL_SCANCODE_F; + s_Keyboard.scancodes[0x022] = PAL_SCANCODE_G; + s_Keyboard.scancodes[0x023] = PAL_SCANCODE_H; + s_Keyboard.scancodes[0x017] = PAL_SCANCODE_I; + s_Keyboard.scancodes[0x024] = PAL_SCANCODE_J; + s_Keyboard.scancodes[0x025] = PAL_SCANCODE_K; + s_Keyboard.scancodes[0x026] = PAL_SCANCODE_L; + s_Keyboard.scancodes[0x032] = PAL_SCANCODE_M; + s_Keyboard.scancodes[0x031] = PAL_SCANCODE_N; + s_Keyboard.scancodes[0x018] = PAL_SCANCODE_O; + s_Keyboard.scancodes[0x019] = PAL_SCANCODE_P; + s_Keyboard.scancodes[0x010] = PAL_SCANCODE_Q; + s_Keyboard.scancodes[0x013] = PAL_SCANCODE_R; + s_Keyboard.scancodes[0x01F] = PAL_SCANCODE_S; + s_Keyboard.scancodes[0x014] = PAL_SCANCODE_T; + s_Keyboard.scancodes[0x016] = PAL_SCANCODE_U; + s_Keyboard.scancodes[0x02F] = PAL_SCANCODE_V; + s_Keyboard.scancodes[0x011] = PAL_SCANCODE_W; + s_Keyboard.scancodes[0x02D] = PAL_SCANCODE_X; + s_Keyboard.scancodes[0x015] = PAL_SCANCODE_Y; + s_Keyboard.scancodes[0x02C] = PAL_SCANCODE_Z; + + // Numbers (top row) + s_Keyboard.scancodes[0x00B] = PAL_SCANCODE_0; + s_Keyboard.scancodes[0x002] = PAL_SCANCODE_1; + s_Keyboard.scancodes[0x003] = PAL_SCANCODE_2; + s_Keyboard.scancodes[0x004] = PAL_SCANCODE_3; + s_Keyboard.scancodes[0x005] = PAL_SCANCODE_4; + s_Keyboard.scancodes[0x006] = PAL_SCANCODE_5; + s_Keyboard.scancodes[0x007] = PAL_SCANCODE_6; + s_Keyboard.scancodes[0x008] = PAL_SCANCODE_7; + s_Keyboard.scancodes[0x009] = PAL_SCANCODE_8; + s_Keyboard.scancodes[0x00A] = PAL_SCANCODE_9; + + // Function + s_Keyboard.scancodes[0x03B] = PAL_SCANCODE_F1; + s_Keyboard.scancodes[0x03C] = PAL_SCANCODE_F2; + s_Keyboard.scancodes[0x03D] = PAL_SCANCODE_F3; + s_Keyboard.scancodes[0x03E] = PAL_SCANCODE_F4; + s_Keyboard.scancodes[0x03F] = PAL_SCANCODE_F5; + s_Keyboard.scancodes[0x040] = PAL_SCANCODE_F6; + s_Keyboard.scancodes[0x041] = PAL_SCANCODE_F7; + s_Keyboard.scancodes[0x042] = PAL_SCANCODE_F8; + s_Keyboard.scancodes[0x043] = PAL_SCANCODE_F9; + s_Keyboard.scancodes[0x044] = PAL_SCANCODE_F10; + s_Keyboard.scancodes[0x057] = PAL_SCANCODE_F11; + s_Keyboard.scancodes[0x058] = PAL_SCANCODE_F12; + + // Control + s_Keyboard.scancodes[0x001] = PAL_SCANCODE_ESCAPE; + s_Keyboard.scancodes[0x01C] = PAL_SCANCODE_ENTER; + s_Keyboard.scancodes[0x00F] = PAL_SCANCODE_TAB; + s_Keyboard.scancodes[0x00E] = PAL_SCANCODE_BACKSPACE; + s_Keyboard.scancodes[0x039] = PAL_SCANCODE_SPACE; + s_Keyboard.scancodes[0x03A] = PAL_SCANCODE_CAPSLOCK; + s_Keyboard.scancodes[0x045] = PAL_SCANCODE_NUMLOCK; + s_Keyboard.scancodes[0x046] = PAL_SCANCODE_SCROLLLOCK; + s_Keyboard.scancodes[0x02A] = PAL_SCANCODE_LSHIFT; + s_Keyboard.scancodes[0x036] = PAL_SCANCODE_RSHIFT; + s_Keyboard.scancodes[0x01D] = PAL_SCANCODE_LCTRL; + s_Keyboard.scancodes[0x061] = PAL_SCANCODE_RCTRL; + s_Keyboard.scancodes[0x038] = PAL_SCANCODE_LALT; + s_Keyboard.scancodes[0x064] = PAL_SCANCODE_RALT; + + // Arrows + s_Keyboard.scancodes[0x069] = PAL_SCANCODE_LEFT; + s_Keyboard.scancodes[0x06A] = PAL_SCANCODE_RIGHT; + s_Keyboard.scancodes[0x067] = PAL_SCANCODE_UP; + s_Keyboard.scancodes[0x06C] = PAL_SCANCODE_DOWN; + + // Navigation + s_Keyboard.scancodes[0x06E] = PAL_SCANCODE_INSERT; + s_Keyboard.scancodes[0x06F] = PAL_SCANCODE_DELETE; + s_Keyboard.scancodes[0x066] = PAL_SCANCODE_HOME; + s_Keyboard.scancodes[0x067] = PAL_SCANCODE_END; + s_Keyboard.scancodes[0x068] = PAL_SCANCODE_PAGEUP; + s_Keyboard.scancodes[0x06D] = PAL_SCANCODE_PAGEDOWN; + + // Keypad + s_Keyboard.scancodes[0x052] = PAL_SCANCODE_KP_0; + s_Keyboard.scancodes[0x04F] = PAL_SCANCODE_KP_1; + s_Keyboard.scancodes[0x050] = PAL_SCANCODE_KP_2; + s_Keyboard.scancodes[0x051] = PAL_SCANCODE_KP_3; + s_Keyboard.scancodes[0x04B] = PAL_SCANCODE_KP_4; + s_Keyboard.scancodes[0x04C] = PAL_SCANCODE_KP_5; + s_Keyboard.scancodes[0x04D] = PAL_SCANCODE_KP_6; + s_Keyboard.scancodes[0x047] = PAL_SCANCODE_KP_7; + s_Keyboard.scancodes[0x048] = PAL_SCANCODE_KP_8; + s_Keyboard.scancodes[0x049] = PAL_SCANCODE_KP_9; + s_Keyboard.scancodes[0x060] = PAL_SCANCODE_KP_ENTER; + s_Keyboard.scancodes[0x04E] = PAL_SCANCODE_KP_ADD; + s_Keyboard.scancodes[0x04A] = PAL_SCANCODE_KP_SUBTRACT; + s_Keyboard.scancodes[0x037] = PAL_SCANCODE_KP_MULTIPLY; + s_Keyboard.scancodes[0x062] = PAL_SCANCODE_KP_DIVIDE; + s_Keyboard.scancodes[0x053] = PAL_SCANCODE_KP_DECIMAL; + + // Misc + s_Keyboard.scancodes[0x063] = PAL_SCANCODE_PRINTSCREEN; + s_Keyboard.scancodes[0x066] = PAL_SCANCODE_PAUSE; + s_Keyboard.scancodes[0x07F] = PAL_SCANCODE_MENU; + s_Keyboard.scancodes[0x028] = PAL_SCANCODE_APOSTROPHE; + s_Keyboard.scancodes[0x02B] = PAL_SCANCODE_BACKSLASH; + s_Keyboard.scancodes[0x033] = PAL_SCANCODE_COMMA; + s_Keyboard.scancodes[0x00D] = PAL_SCANCODE_EQUAL; + s_Keyboard.scancodes[0x029] = PAL_SCANCODE_GRAVEACCENT; + s_Keyboard.scancodes[0x00C] = PAL_SCANCODE_SUBTRACT; + s_Keyboard.scancodes[0x034] = PAL_SCANCODE_PERIOD; + s_Keyboard.scancodes[0x027] = PAL_SCANCODE_SEMICOLON; + s_Keyboard.scancodes[0x035] = PAL_SCANCODE_SLASH; + s_Keyboard.scancodes[0x01A] = PAL_SCANCODE_LBRACKET; + s_Keyboard.scancodes[0x01B] = PAL_SCANCODE_RBRACKET; + s_Keyboard.scancodes[0x07D] = PAL_SCANCODE_LSUPER; + s_Keyboard.scancodes[0x07E] = PAL_SCANCODE_RSUPER; +} + // ================================================== // X11 API // ================================================== @@ -2859,129 +3218,6 @@ static void xSendWMEvent( &e); } -static void xCreateScancodeTable() -{ - // Letters - s_Keyboard.scancodes[0x01E] = PAL_SCANCODE_A; - s_Keyboard.scancodes[0x030] = PAL_SCANCODE_B; - s_Keyboard.scancodes[0x02E] = PAL_SCANCODE_C; - s_Keyboard.scancodes[0x020] = PAL_SCANCODE_D; - s_Keyboard.scancodes[0x012] = PAL_SCANCODE_E; - s_Keyboard.scancodes[0x021] = PAL_SCANCODE_F; - s_Keyboard.scancodes[0x022] = PAL_SCANCODE_G; - s_Keyboard.scancodes[0x023] = PAL_SCANCODE_H; - s_Keyboard.scancodes[0x017] = PAL_SCANCODE_I; - s_Keyboard.scancodes[0x024] = PAL_SCANCODE_J; - s_Keyboard.scancodes[0x025] = PAL_SCANCODE_K; - s_Keyboard.scancodes[0x026] = PAL_SCANCODE_L; - s_Keyboard.scancodes[0x032] = PAL_SCANCODE_M; - s_Keyboard.scancodes[0x031] = PAL_SCANCODE_N; - s_Keyboard.scancodes[0x018] = PAL_SCANCODE_O; - s_Keyboard.scancodes[0x019] = PAL_SCANCODE_P; - s_Keyboard.scancodes[0x010] = PAL_SCANCODE_Q; - s_Keyboard.scancodes[0x013] = PAL_SCANCODE_R; - s_Keyboard.scancodes[0x01F] = PAL_SCANCODE_S; - s_Keyboard.scancodes[0x014] = PAL_SCANCODE_T; - s_Keyboard.scancodes[0x016] = PAL_SCANCODE_U; - s_Keyboard.scancodes[0x02F] = PAL_SCANCODE_V; - s_Keyboard.scancodes[0x011] = PAL_SCANCODE_W; - s_Keyboard.scancodes[0x02D] = PAL_SCANCODE_X; - s_Keyboard.scancodes[0x015] = PAL_SCANCODE_Y; - s_Keyboard.scancodes[0x02C] = PAL_SCANCODE_Z; - - // Numbers (top row) - s_Keyboard.scancodes[0x00B] = PAL_SCANCODE_0; - s_Keyboard.scancodes[0x002] = PAL_SCANCODE_1; - s_Keyboard.scancodes[0x003] = PAL_SCANCODE_2; - s_Keyboard.scancodes[0x004] = PAL_SCANCODE_3; - s_Keyboard.scancodes[0x005] = PAL_SCANCODE_4; - s_Keyboard.scancodes[0x006] = PAL_SCANCODE_5; - s_Keyboard.scancodes[0x007] = PAL_SCANCODE_6; - s_Keyboard.scancodes[0x008] = PAL_SCANCODE_7; - s_Keyboard.scancodes[0x009] = PAL_SCANCODE_8; - s_Keyboard.scancodes[0x00A] = PAL_SCANCODE_9; - - // Function - s_Keyboard.scancodes[0x03B] = PAL_SCANCODE_F1; - s_Keyboard.scancodes[0x03C] = PAL_SCANCODE_F2; - s_Keyboard.scancodes[0x03D] = PAL_SCANCODE_F3; - s_Keyboard.scancodes[0x03E] = PAL_SCANCODE_F4; - s_Keyboard.scancodes[0x03F] = PAL_SCANCODE_F5; - s_Keyboard.scancodes[0x040] = PAL_SCANCODE_F6; - s_Keyboard.scancodes[0x041] = PAL_SCANCODE_F7; - s_Keyboard.scancodes[0x042] = PAL_SCANCODE_F8; - s_Keyboard.scancodes[0x043] = PAL_SCANCODE_F9; - s_Keyboard.scancodes[0x044] = PAL_SCANCODE_F10; - s_Keyboard.scancodes[0x057] = PAL_SCANCODE_F11; - s_Keyboard.scancodes[0x058] = PAL_SCANCODE_F12; - - // Control - s_Keyboard.scancodes[0x001] = PAL_SCANCODE_ESCAPE; - s_Keyboard.scancodes[0x01C] = PAL_SCANCODE_ENTER; - s_Keyboard.scancodes[0x00F] = PAL_SCANCODE_TAB; - s_Keyboard.scancodes[0x00E] = PAL_SCANCODE_BACKSPACE; - s_Keyboard.scancodes[0x039] = PAL_SCANCODE_SPACE; - s_Keyboard.scancodes[0x03A] = PAL_SCANCODE_CAPSLOCK; - s_Keyboard.scancodes[0x045] = PAL_SCANCODE_NUMLOCK; - s_Keyboard.scancodes[0x046] = PAL_SCANCODE_SCROLLLOCK; - s_Keyboard.scancodes[0x02A] = PAL_SCANCODE_LSHIFT; - s_Keyboard.scancodes[0x036] = PAL_SCANCODE_RSHIFT; - s_Keyboard.scancodes[0x01D] = PAL_SCANCODE_LCTRL; - s_Keyboard.scancodes[0x061] = PAL_SCANCODE_RCTRL; - s_Keyboard.scancodes[0x038] = PAL_SCANCODE_LALT; - s_Keyboard.scancodes[0x064] = PAL_SCANCODE_RALT; - - // Arrows - s_Keyboard.scancodes[0x069] = PAL_SCANCODE_LEFT; - s_Keyboard.scancodes[0x06A] = PAL_SCANCODE_RIGHT; - s_Keyboard.scancodes[0x067] = PAL_SCANCODE_UP; - s_Keyboard.scancodes[0x06C] = PAL_SCANCODE_DOWN; - - // Navigation - s_Keyboard.scancodes[0x06E] = PAL_SCANCODE_INSERT; - s_Keyboard.scancodes[0x06F] = PAL_SCANCODE_DELETE; - s_Keyboard.scancodes[0x066] = PAL_SCANCODE_HOME; - s_Keyboard.scancodes[0x067] = PAL_SCANCODE_END; - s_Keyboard.scancodes[0x068] = PAL_SCANCODE_PAGEUP; - s_Keyboard.scancodes[0x06D] = PAL_SCANCODE_PAGEDOWN; - - // Keypad - s_Keyboard.scancodes[0x052] = PAL_SCANCODE_KP_0; - s_Keyboard.scancodes[0x04F] = PAL_SCANCODE_KP_1; - s_Keyboard.scancodes[0x050] = PAL_SCANCODE_KP_2; - s_Keyboard.scancodes[0x051] = PAL_SCANCODE_KP_3; - s_Keyboard.scancodes[0x04B] = PAL_SCANCODE_KP_4; - s_Keyboard.scancodes[0x04C] = PAL_SCANCODE_KP_5; - s_Keyboard.scancodes[0x04D] = PAL_SCANCODE_KP_6; - s_Keyboard.scancodes[0x047] = PAL_SCANCODE_KP_7; - s_Keyboard.scancodes[0x048] = PAL_SCANCODE_KP_8; - s_Keyboard.scancodes[0x049] = PAL_SCANCODE_KP_9; - s_Keyboard.scancodes[0x060] = PAL_SCANCODE_KP_ENTER; - s_Keyboard.scancodes[0x04E] = PAL_SCANCODE_KP_ADD; - s_Keyboard.scancodes[0x04A] = PAL_SCANCODE_KP_SUBTRACT; - s_Keyboard.scancodes[0x037] = PAL_SCANCODE_KP_MULTIPLY; - s_Keyboard.scancodes[0x062] = PAL_SCANCODE_KP_DIVIDE; - s_Keyboard.scancodes[0x053] = PAL_SCANCODE_KP_DECIMAL; - - // Misc - s_Keyboard.scancodes[0x063] = PAL_SCANCODE_PRINTSCREEN; - s_Keyboard.scancodes[0x066] = PAL_SCANCODE_PAUSE; - s_Keyboard.scancodes[0x07F] = PAL_SCANCODE_MENU; - s_Keyboard.scancodes[0x028] = PAL_SCANCODE_APOSTROPHE; - s_Keyboard.scancodes[0x02B] = PAL_SCANCODE_BACKSLASH; - s_Keyboard.scancodes[0x033] = PAL_SCANCODE_COMMA; - s_Keyboard.scancodes[0x00D] = PAL_SCANCODE_EQUAL; - s_Keyboard.scancodes[0x029] = PAL_SCANCODE_GRAVEACCENT; - s_Keyboard.scancodes[0x00C] = PAL_SCANCODE_SUBTRACT; - s_Keyboard.scancodes[0x034] = PAL_SCANCODE_PERIOD; - s_Keyboard.scancodes[0x027] = PAL_SCANCODE_SEMICOLON; - s_Keyboard.scancodes[0x035] = PAL_SCANCODE_SLASH; - s_Keyboard.scancodes[0x01A] = PAL_SCANCODE_LBRACKET; - s_Keyboard.scancodes[0x01B] = PAL_SCANCODE_RBRACKET; - s_Keyboard.scancodes[0x07D] = PAL_SCANCODE_LSUPER; - s_Keyboard.scancodes[0x07E] = PAL_SCANCODE_RSUPER; -} - static void xCreateKeycodeTable() { // Tis is for only printable and text input keys @@ -3401,7 +3637,6 @@ static PalResult xInitVideo() "glXGetVisualFromFBConfig"); } - xCreateScancodeTable(); xCreateKeycodeTable(); // disable auto key repeats @@ -5642,6 +5877,55 @@ static Backend s_XBackend = { #pragma region Wayland API #if PAL_HAS_WAYLAND +static void wlCreateKeycodeTable() +{ + // Tis is for only printable and text input keys + + // Letters + s_Keyboard.keycodes[XKB_KEY_a] = PAL_KEYCODE_A; + s_Keyboard.keycodes[XKB_KEY_b] = PAL_KEYCODE_B; + s_Keyboard.keycodes[XKB_KEY_c] = PAL_KEYCODE_C; + s_Keyboard.keycodes[XKB_KEY_d] = PAL_KEYCODE_D; + s_Keyboard.keycodes[XKB_KEY_e] = PAL_KEYCODE_E; + s_Keyboard.keycodes[XKB_KEY_f] = PAL_KEYCODE_F; + s_Keyboard.keycodes[XKB_KEY_g] = PAL_KEYCODE_G; + s_Keyboard.keycodes[XKB_KEY_h] = PAL_KEYCODE_H; + s_Keyboard.keycodes[XKB_KEY_i] = PAL_KEYCODE_I; + s_Keyboard.keycodes[XKB_KEY_j] = PAL_KEYCODE_J; + s_Keyboard.keycodes[XKB_KEY_k] = PAL_KEYCODE_K; + s_Keyboard.keycodes[XKB_KEY_l] = PAL_KEYCODE_L; + s_Keyboard.keycodes[XKB_KEY_m] = PAL_KEYCODE_M; + s_Keyboard.keycodes[XKB_KEY_n] = PAL_KEYCODE_N; + s_Keyboard.keycodes[XKB_KEY_o] = PAL_KEYCODE_O; + s_Keyboard.keycodes[XKB_KEY_p] = PAL_KEYCODE_P; + s_Keyboard.keycodes[XKB_KEY_q] = PAL_KEYCODE_Q; + s_Keyboard.keycodes[XKB_KEY_r] = PAL_KEYCODE_R; + s_Keyboard.keycodes[XKB_KEY_s] = PAL_KEYCODE_S; + s_Keyboard.keycodes[XKB_KEY_t] = PAL_KEYCODE_T; + s_Keyboard.keycodes[XKB_KEY_u] = PAL_KEYCODE_U; + s_Keyboard.keycodes[XKB_KEY_v] = PAL_KEYCODE_V; + s_Keyboard.keycodes[XKB_KEY_w] = PAL_KEYCODE_W; + s_Keyboard.keycodes[XKB_KEY_x] = PAL_KEYCODE_X; + s_Keyboard.keycodes[XKB_KEY_y] = PAL_KEYCODE_Y; + s_Keyboard.keycodes[XKB_KEY_z] = PAL_KEYCODE_Z; + + // Control + s_Keyboard.keycodes[XKB_KEY_space] = PAL_KEYCODE_SPACE; + + // Misc + s_Keyboard.keycodes[XKB_KEY_apostrophe] = PAL_KEYCODE_APOSTROPHE; + s_Keyboard.keycodes[XKB_KEY_backslash] = PAL_KEYCODE_BACKSLASH; + s_Keyboard.keycodes[XKB_KEY_comma] = PAL_KEYCODE_COMMA; + s_Keyboard.keycodes[XKB_KEY_equal] = PAL_KEYCODE_EQUAL; + s_Keyboard.keycodes[XKB_KEY_grave] = PAL_KEYCODE_GRAVEACCENT; + s_Keyboard.keycodes[XKB_KEY_minus] = PAL_KEYCODE_SUBTRACT; + s_Keyboard.keycodes[XKB_KEY_period] = PAL_KEYCODE_PERIOD; + s_Keyboard.keycodes[XKB_KEY_semicolon] = PAL_KEYCODE_SEMICOLON; + s_Keyboard.keycodes[XKB_KEY_slash] = PAL_KEYCODE_SLASH; + s_Keyboard.keycodes[XKB_KEY_bracketleft] = PAL_KEYCODE_LBRACKET; + s_Keyboard.keycodes[XKB_KEY_bracketright] = PAL_KEYCODE_RBRACKET; +} + static int createShmFile(Uint64 size) { char template[] = "/tmp/pal-shm-XXXXXX"; @@ -5976,7 +6260,8 @@ PalResult wlInitVideo() { // load wayland libray s_Wl.handle = dlopen("libwayland-client.so.0", RTLD_LAZY); - if (!s_Wl.handle) { + s_Wl.xkbCommon = dlopen("libxkbcommon.so", RTLD_LAZY); + if (!s_Wl.handle || !s_Wl.xkbCommon) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -6058,6 +6343,39 @@ PalResult wlInitVideo() s_Wl.handle, "wl_display_cancel_read"); + // load xkbcommon procs + s_Wl.xkbKeymapUnref = (xkb_keymap_unref_fn)dlsym( + s_Wl.xkbCommon, + "xkb_keymap_unref"); + + s_Wl.xkbStateKeyGetOneSym = (xkb_state_key_get_one_sym_fn)dlsym( + s_Wl.xkbCommon, + "xkb_state_key_get_one_sym"); + + s_Wl.xkbStateNew = (xkb_state_new_fn)dlsym( + s_Wl.xkbCommon, + "xkb_state_new"); + + s_Wl.xkbStateUnref = (xkb_state_unref_fn)dlsym( + s_Wl.xkbCommon, + "xkb_state_unref"); + + s_Wl.xkbContextUnref = (xkb_context_unref_fn)dlsym( + s_Wl.xkbCommon, + "xkb_context_unref"); + + s_Wl.xkbContextNew = (xkb_context_new_fn)dlsym( + s_Wl.xkbCommon, + "xkb_context_new"); + + s_Wl.xkbKeymapNewFromString = (xkb_keymap_new_from_string_fn)dlsym( + s_Wl.xkbCommon, + "xkb_keymap_new_from_string"); + + s_Wl.xkbKeysymToUtf32 = (xkb_keysym_to_utf32_fn)dlsym( + s_Wl.xkbCommon, + "xkb_keysym_to_utf32"); + // initialize wayland s_Wl.modesPhase = false; s_Wl.checkFeatures = true; @@ -6077,11 +6395,25 @@ PalResult wlInitVideo() return PAL_RESULT_PLATFORM_FAILURE; } + // create an input context + s_Wl.inputContext = s_Wl.xkbContextNew(XKB_CONTEXT_NO_FLAGS); + if (!s_Wl.inputContext) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + wlCreateKeycodeTable(); return PAL_RESULT_SUCCESS; } void wlShutdownVideo() { + if (s_Wl.state) { + s_Wl.xkbStateUnref(s_Wl.state); + s_Wl.xkbKeymapUnref(s_Wl.keymap); + } + + s_Wl.xkbContextUnref(s_Wl.inputContext); + if (s_Wl.decorationManager) { zxdgDecorationManagerV1Destroy(s_Wl.decorationManager); } @@ -6092,6 +6424,7 @@ void wlShutdownVideo() s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.registry); s_Wl.displayDisconnect(s_Wl.display); dlclose(s_Wl.handle); + dlclose(s_Wl.xkbCommon); } void wlUpdateVideo() @@ -6954,6 +7287,8 @@ PalResult PAL_CALL palInitVideo( #endif // PAL_HAS_WAYLAND } + createScancodeTable(); + // we load EGL as well s_Egl.handle = dlopen("libEGL.so", RTLD_LAZY); if (s_Egl.handle) { From 6b82ec740d3e0b838d8f7a7643d8e1bf83c2aa4a Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 11 Nov 2025 15:23:11 +0000 Subject: [PATCH 12/51] key repeats --- src/video/pal_video_linux.c | 103 +++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 7 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index e79e907..63d2504 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -175,8 +175,14 @@ typedef struct { typedef struct { bool scancodeState[PAL_SCANCODE_MAX]; bool keycodeState[PAL_KEYCODE_MAX]; + int repeatRate; + int repeatDelay; + int repeatKey; + int repeatScancode; int scancodes[512]; int keycodes[256]; + Uint64 timer; + Uint64 frequency; } Keyboard; typedef struct { @@ -878,6 +884,19 @@ typedef struct xkb_keymap* (*xkb_keymap_new_from_string_fn)( enum xkb_keymap_format, enum xkb_keymap_compile_flags); +typedef enum xkb_state_component (*xkb_state_update_mask_fn)( + struct xkb_state*, + xkb_mod_mask_t, + xkb_mod_mask_t, + xkb_mod_mask_t, + xkb_layout_index_t, + xkb_layout_index_t, + xkb_layout_index_t); + +typedef int (*xkb_keymap_key_repeats_fn)( + struct xkb_keymap*, + xkb_keycode_t); + typedef struct { bool checkFeatures; bool modesPhase; @@ -939,6 +958,8 @@ typedef struct { xkb_state_key_get_one_sym_fn xkbStateKeyGetOneSym; xkb_keymap_new_from_string_fn xkbKeymapNewFromString; xkb_keysym_to_utf32_fn xkbKeysymToUtf32; + xkb_state_update_mask_fn xkbStateUpdateMask; + xkb_keymap_key_repeats_fn xkbKeymapKeyRepeats; } Wayland; typedef struct { @@ -964,6 +985,12 @@ static Wayland s_Wl = {0}; static WindowData* findWindowData(PalWindow* window); +static inline Uint64 getTime() +{ + Uint64 now = palGetPerformanceCounter(); + return (now * 1000) / s_Keyboard.frequency; +} + static inline void* wlRegistryBind( struct wl_registry *wl_registry, uint32_t name, @@ -1578,6 +1605,7 @@ static void keyboardHandleRemap( s_Wl.state = state; s_Wl.keymap = keymap; + s_Keyboard.frequency = palGetPerformanceFrequency(); } static void keyboardHandleKey( @@ -1640,15 +1668,26 @@ static void keyboardHandleKey( keycode = (PalKeycode)(Uint32)scancode; } - bool repeat = s_Keyboard.keycodeState[keycode]; s_Keyboard.scancodeState[scancode] = pressed; s_Keyboard.keycodeState[keycode] = pressed; + // check for key repeats if (pressed) { - if (repeat) { - type = PAL_EVENT_KEYREPEAT; + if (s_Wl.xkbKeymapKeyRepeats(s_Wl.keymap, key + 8)) { + s_Keyboard.repeatKey = keycode; + s_Keyboard.repeatScancode = scancode; + s_Keyboard.timer = getTime() + s_Keyboard.repeatDelay; + } else { - type = PAL_EVENT_KEYDOWN; + s_Keyboard.repeatKey = 0; + } + + type = PAL_EVENT_KEYDOWN; + + } else { + // key release + if (s_Keyboard.repeatKey == keycode) { + s_Keyboard.repeatKey = 0; } } @@ -1685,7 +1724,16 @@ static void keyboardHandleRepeatInfo( int32_t rate, int32_t delay) { - + if (s_Wl.keyboard == keyboard) { + s_Keyboard.repeatDelay = delay; + s_Keyboard.repeatRate = rate; + + } else { + if (s_Keyboard.repeatDelay == 0) { + s_Keyboard.repeatDelay = 500; + s_Keyboard.repeatRate = 30; + } + } } static void keyboardHandleModifiers( @@ -1697,7 +1745,16 @@ static void keyboardHandleModifiers( uint32_t mods_locked, uint32_t group) { - + if (s_Wl.state) { + s_Wl.xkbStateUpdateMask( + s_Wl.state, + mods_depressed, + mods_latched, + mods_locked, + group, + 0, + 0); + } } static struct wl_pointer_listener pointerListener = { @@ -6376,6 +6433,14 @@ PalResult wlInitVideo() s_Wl.xkbCommon, "xkb_keysym_to_utf32"); + s_Wl.xkbStateUpdateMask = (xkb_state_update_mask_fn)dlsym( + s_Wl.xkbCommon, + "xkb_state_update_mask"); + + s_Wl.xkbKeymapKeyRepeats = (xkb_keymap_key_repeats_fn)dlsym( + s_Wl.xkbCommon, + "xkb_keymap_key_repeats"); + // initialize wayland s_Wl.modesPhase = false; s_Wl.checkFeatures = true; @@ -6432,8 +6497,32 @@ void wlUpdateVideo() // flush pending requests s_Mouse.tmpScrollX = 0; s_Mouse.tmpScrollY = 0; - s_Wl.displayFlush(s_Wl.display); + // push key repeats + // we only do this if the user wants key repeat events + if (s_Keyboard.repeatKey != 0 && s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(driver, PAL_EVENT_KEYREPEAT); + if (mode != PAL_DISPATCH_NONE) { + // get now time and check with the key repeat time + Uint64 now = getTime(); + if (now >= s_Keyboard.timer) { + PalWindow* window = (PalWindow*)s_Wl.keyboardSurface; + PalKeycode key = s_Keyboard.repeatKey; + PalScancode scancode = s_Keyboard.repeatScancode; + + PalEvent event = {0}; + event.type = PAL_EVENT_KEYREPEAT; + event.data = palPackUint32(key, scancode); + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); + s_Keyboard.timer += s_Keyboard.repeatRate; + } + } + } + + s_Wl.displayFlush(s_Wl.display); while (s_Wl.prepareRead(s_Wl.display) != 0) { s_Wl.dispatchPending(s_Wl.display); } From 1e70ac8f673995e63e0ed38495d89119a2467d58 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 11 Nov 2025 18:21:24 +0000 Subject: [PATCH 13/51] system cursor --- src/video/pal_video_linux.c | 102 ++++++++++++++++++++++++++++++++++-- tests/input_window_test.c | 2 +- tests/system_cursor_test.c | 16 ++++++ tests/tests_main.c | 4 +- 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 63d2504..b7850fb 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -59,6 +59,8 @@ freely, subject to the following restrictions: #include #include #include + +#include #endif // PAL_HAS_WAYLAND // ================================================== @@ -897,6 +899,19 @@ typedef int (*xkb_keymap_key_repeats_fn)( struct xkb_keymap*, xkb_keycode_t); +// wayland cursor +typedef struct wl_cursor_theme* (*wl_cursor_theme_load_fn)( + const char*, + int, + struct wl_shm*); + +typedef struct wl_cursor* (*wl_cursor_theme_get_cursor_fn)( + struct wl_cursor_theme*, + const char*); + +typedef struct wl_buffer* (*wl_cursor_image_get_buffer_fn)( + struct wl_cursor_image*); + typedef struct { bool checkFeatures; bool modesPhase; @@ -904,6 +919,7 @@ typedef struct { void* handle; void* xkbCommon; + void* libCursor; struct wl_display* display; struct wl_registry* registry; struct xdg_wm_base* xdgBase; @@ -916,6 +932,7 @@ typedef struct { struct zwp_pointer_constraints* pointerConstraints; struct wl_surface* pointerSurface; struct wl_surface* keyboardSurface; + struct wl_cursor_theme* cursorTheme; struct xkb_context* inputContext; struct xkb_keymap* keymap; @@ -960,6 +977,10 @@ typedef struct { xkb_keysym_to_utf32_fn xkbKeysymToUtf32; xkb_state_update_mask_fn xkbStateUpdateMask; xkb_keymap_key_repeats_fn xkbKeymapKeyRepeats; + + wl_cursor_theme_load_fn cursorThemeLoad; + wl_cursor_theme_get_cursor_fn cursorThemeGetCursor; + wl_cursor_image_get_buffer_fn cursorImageGetBuffer; } Wayland; typedef struct { @@ -6318,7 +6339,8 @@ PalResult wlInitVideo() // load wayland libray s_Wl.handle = dlopen("libwayland-client.so.0", RTLD_LAZY); s_Wl.xkbCommon = dlopen("libxkbcommon.so", RTLD_LAZY); - if (!s_Wl.handle || !s_Wl.xkbCommon) { + s_Wl.libCursor = dlopen("libwayland-cursor.so", RTLD_LAZY); + if (!s_Wl.handle || !s_Wl.xkbCommon || !s_Wl.libCursor) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -6441,6 +6463,19 @@ PalResult wlInitVideo() s_Wl.xkbCommon, "xkb_keymap_key_repeats"); + // load wayland cursor procs + s_Wl.cursorImageGetBuffer = (wl_cursor_image_get_buffer_fn)dlsym( + s_Wl.libCursor, + "wl_cursor_image_get_buffer"); + + s_Wl.cursorThemeLoad = (wl_cursor_theme_load_fn)dlsym( + s_Wl.libCursor, + "wl_cursor_theme_load"); + + s_Wl.cursorThemeGetCursor = (wl_cursor_theme_get_cursor_fn)dlsym( + s_Wl.libCursor, + "wl_cursor_theme_get_cursor"); + // initialize wayland s_Wl.modesPhase = false; s_Wl.checkFeatures = true; @@ -6466,6 +6501,12 @@ PalResult wlInitVideo() return PAL_RESULT_PLATFORM_FAILURE; } + // get the current theme + s_Wl.cursorTheme = s_Wl.cursorThemeLoad(nullptr, 32, s_Wl.shm); + if (!s_Wl.cursorTheme) { + return PAL_RESULT_PLATFORM_FAILURE; + } + wlCreateKeycodeTable(); return PAL_RESULT_SUCCESS; } @@ -6478,7 +6519,6 @@ void wlShutdownVideo() } s_Wl.xkbContextUnref(s_Wl.inputContext); - if (s_Wl.decorationManager) { zxdgDecorationManagerV1Destroy(s_Wl.decorationManager); } @@ -6488,8 +6528,10 @@ void wlShutdownVideo() s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.compositor); s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.registry); s_Wl.displayDisconnect(s_Wl.display); - dlclose(s_Wl.handle); + + dlclose(s_Wl.libCursor); dlclose(s_Wl.xkbCommon); + dlclose(s_Wl.handle); } void wlUpdateVideo() @@ -7169,9 +7211,7 @@ PalResult wlCreateCursor( } wlSurfaceAttach(cursor->surface, cursor->buffer, 0, 0); - wlSurfaceDamageBuffer(cursor->surface, 0, 0, info->width, info->height); wlSurfaceCommit(cursor->surface); - cursor->hotspotX = info->xHotspot; cursor->hotspotY = info->yHotspot; @@ -7183,6 +7223,58 @@ PalResult wlCreateCursorFrom( PalCursorType type, PalCursor** outCursor) { + const char* cursorType = nullptr; + switch (type) { + case PAL_CURSOR_ARROW: { + cursorType = "left_ptr"; + break; + } + + case PAL_CURSOR_HAND: { + cursorType = "hand1"; + break; + } + + case PAL_CURSOR_CROSS: { + cursorType = "crosshair"; + break; + } + + case PAL_CURSOR_IBEAM: { + cursorType = "text"; + break; + } + + case PAL_CURSOR_WAIT: { + cursorType = "wait"; + break; + } + } + + struct wl_cursor* wlCursor = nullptr; + wlCursor = s_Wl.cursorThemeGetCursor(s_Wl.cursorTheme, cursorType); + if (!wlCursor) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + WaylandCursor* cursor = nullptr; + cursor = palAllocate(s_Video.allocator, sizeof(WaylandCursor), 0); + if (!cursor) { + return PAL_RESULT_OUT_OF_MEMORY; + } + + cursor->surface = wlCompositorCreateSurface(s_Wl.compositor); + if (!cursor->surface) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + cursor->buffer = s_Wl.cursorImageGetBuffer(wlCursor->images[0]); + wlSurfaceAttach(cursor->surface, cursor->buffer, 0, 0); + wlSurfaceCommit(cursor->surface); + cursor->hotspotX = wlCursor->images[0]->hotspot_x; + cursor->hotspotY = wlCursor->images[0]->hotspot_y; + + *outCursor = (PalCursor*)cursor; return PAL_RESULT_SUCCESS; } diff --git a/tests/input_window_test.c b/tests/input_window_test.c index a7b8d00..05ab296 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -375,7 +375,7 @@ static inline void onMouseWheel(const PalEvent* event) palLog(nullptr, "%s: Mouse Wheel: (%d, %d)", dispatchString, dx, dy); palLog( nullptr, - "%s: Mouse Wheel Float: (%.2f, %.2f)", + "%s: Mouse Wheel Raw: (%.2f, %.2f)", dispatchString, fdx, fdy); diff --git a/tests/system_cursor_test.c b/tests/system_cursor_test.c index cda5991..d10e26e 100644 --- a/tests/system_cursor_test.c +++ b/tests/system_cursor_test.c @@ -45,6 +45,15 @@ bool systemCursorTest() return false; } + // check for support + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR)) { + palLog(nullptr, "Seting cursors feature not supported"); + palDestroyEventDriver(eventDriver); + palShutdownVideo(); + return false; + } + // create system cursor result = palCreateCursorFrom(PAL_CURSOR_CROSS, &cursor); if (result != PAL_RESULT_SUCCESS) { @@ -61,6 +70,13 @@ bool systemCursorTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; createInfo.title = "PAL System Cursor Window - Cross"; + // check if we support decorated windows (title bar, close etc) + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + // create the window with the create info struct result = palCreateWindow(&createInfo, &window); if (result != PAL_RESULT_SUCCESS) { diff --git a/tests/tests_main.c b/tests/tests_main.c index 99cdf0d..31dcd97 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -32,8 +32,8 @@ int main(int argc, char** argv) // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); - registerTest("Input Window Test", inputWindowTest); - // registerTest("System Cursor Test", systemCursorTest); + // registerTest("Input Window Test", inputWindowTest); + registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); From 64d4f0cdf8694952c50e242bb9b5330af477b8ea Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 11 Nov 2025 18:26:48 +0000 Subject: [PATCH 14/51] character events --- tests/char_event_test.c | 8 ++++++++ tests/tests_main.c | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/char_event_test.c b/tests/char_event_test.c index 6e8ed38..7719fea 100644 --- a/tests/char_event_test.c +++ b/tests/char_event_test.c @@ -44,6 +44,14 @@ bool charEventTest() createInfo.show = true; createInfo.title = "PAL Character Window"; + // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + // create the window with the create info struct result = palCreateWindow(&createInfo, &window); if (result != PAL_RESULT_SUCCESS) { diff --git a/tests/tests_main.c b/tests/tests_main.c index 31dcd97..f9fa8b4 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -33,10 +33,10 @@ int main(int argc, char** argv) // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); - registerTest("System Cursor Test", systemCursorTest); + // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); - // registerTest("Native Integration Test", nativeIntegrationTest); + registerTest("Native Integration Test", nativeIntegrationTest); #endif // PAL_HAS_VIDEO #if PAL_HAS_OPENGL From 821c0c27e4df5102da30bcb306f5e0817e6748a3 Mon Sep 17 00:00:00 2001 From: nichcode Date: Wed, 12 Nov 2025 14:03:27 +0000 Subject: [PATCH 15/51] start wayland gl --- src/opengl/pal_opengl_linux.c | 188 +++++++++++++++++++--------------- src/video/pal_video_linux.c | 2 + tests/tests_main.c | 2 +- 3 files changed, 108 insertions(+), 84 deletions(-) diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index 65c3b27..de5fed3 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -60,15 +60,12 @@ typedef void* EGLNativeDisplayType; #ifndef EGL_OPENGL_API // EGL header is not included -/* C++ / C typecast macros for special EGL handle values */ -#if defined(__cplusplus) -#define EGL_CAST(type, value) (static_cast(value)) -#else -#define EGL_CAST(type, value) ((type)(value)) -#endif +#define EGL_CAST(type, value) ((type)(value)) #define EGL_OPENGL_API 0x30A2 #define EGL_OPENGL_BIT 0x0008 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENGL_ES_API 0x30A0 #define EGL_NO_CONTEXT EGL_CAST(EGLContext, 0) #define EGL_NO_DISPLAY EGL_CAST(EGLDisplay, 0) #define EGL_NO_SURFACE EGL_CAST(EGLSurface, 0) @@ -218,6 +215,8 @@ typedef struct { typedef struct { bool initialized; Int32 maxContextData; + EGLenum apiType; + int apiTypeBit; const PalAllocator* allocator; eglGetProcAddressFn eglGetProcAddress; @@ -243,7 +242,7 @@ typedef struct { void* handle; ContextData* contextData; - EGLDisplay display; + EGLDisplay* display; PalGLInfo info; } GLLinux; @@ -436,8 +435,18 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) return PAL_RESULT_PLATFORM_FAILURE; } - // create a dummy context - if (!s_GL.eglBindAPI(EGL_OPENGL_API)) { + // get backend type + const char* session = getenv("XDG_SESSION_TYPE"); + if (session) { + if (strcmp(session, "wayland") == 0) { + s_GL.apiType = EGL_OPENGL_ES_API; + } else { + s_GL.apiType = EGL_OPENGL_API; + s_GL.apiTypeBit = EGL_OPENGL_BIT; + } + } + + if (!s_GL.eglBindAPI(s_GL.apiType)) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -453,25 +462,27 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) // use a simple FBConfig EGLConfig config; int numConfigs; - EGLint attribs[] = { - EGL_RENDERABLE_TYPE, - EGL_OPENGL_BIT, - EGL_SURFACE_TYPE, - EGL_PBUFFER_BIT, - EGL_NONE}; - EGLint type; - s_GL.eglChooseConfig(display, attribs, &config, 1, &numConfigs); - s_GL.eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &type); - if (!(type & EGL_OPENGL_BIT)) { - // we must support opengl API (desktop) - return PAL_RESULT_PLATFORM_FAILURE; + if (s_GL.apiType == EGL_OPENGL_API) { + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, + EGL_OPENGL_BIT, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT, + EGL_NONE}; + + s_GL.eglChooseConfig(display, attribs, &config, 1, &numConfigs); + s_GL.eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &type); + if (!(type & EGL_OPENGL_BIT)) { + // we must support opengl API (desktop) + return PAL_RESULT_PLATFORM_FAILURE; + } } // Since we don't want to create dummy window to get the driver info // we use EGL_OPENGL_ES2_BIT to create without a window if (!(type & EGL_OPENGL_ES2_BIT)) { - // FIXME: create a dummy window if EGL_OPENGL_ES2_BIT + // FIXME: create a dummy window if EGL_OPENGL_ES2_BIT if using GL API return PAL_RESULT_PLATFORM_FAILURE; } @@ -490,27 +501,33 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) return PAL_RESULT_PLATFORM_FAILURE; } - EGLint contextAttrib[] = { - EGL_CONTEXT_MAJOR_VERSION, 2, - EGL_CONTEXT_MINOR_VERSION, 1, - EGL_NONE}; - // create a dummy context EGLContext context = EGL_NO_CONTEXT; - context = s_GL.eglCreateContext( - display, - config, - EGL_NO_CONTEXT, - contextAttrib); + if (s_GL.apiType == EGL_OPENGL_API) { + EGLint contextAttrib[] = { + EGL_CONTEXT_MAJOR_VERSION, 2, + EGL_CONTEXT_MINOR_VERSION, 1, + EGL_NONE}; + + context = s_GL.eglCreateContext( + display, + config, + EGL_NO_CONTEXT, + contextAttrib); + } - if (!s_GL.eglMakeCurrent(display, surface, surface, context)) { + if (context == EGL_NO_CONTEXT) { return PAL_RESULT_PLATFORM_FAILURE; } + s_GL.eglMakeCurrent(display, surface, surface, context); s_GL.glGetString = (glGetStringFn)s_GL.eglGetProcAddress("glGetString"); + const char* version = (const char*)s_GL.glGetString(GL_VERSION); if (version) { - sscanf(version, "%d.%d", &s_GL.info.major, &s_GL.info.minor); + if (s_GL.apiType == EGL_OPENGL_API) { + sscanf(version, "%d.%d", &s_GL.info.major, &s_GL.info.minor); + } } const char* renderer = (const char*)s_GL.glGetString(GL_RENDERER); @@ -676,7 +693,7 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( continue; } - if (!(renderable & EGL_OPENGL_BIT)) { + if (!(renderable & s_GL.apiTypeBit)) { continue; } @@ -917,6 +934,8 @@ PalResult PAL_CALL palCreateGLContext( EGLContext* share = nullptr; if (info->shareContext) { share = (EGLContext)info->shareContext; + } else { + share = EGL_NO_CONTEXT; } Int32 attribs[40]; @@ -926,65 +945,68 @@ PalResult PAL_CALL palCreateGLContext( // set context attributes // the first element is the key and the second is the value - // set version - attribs[index++] = EGL_CONTEXT_MAJOR_VERSION_KHR; // key - attribs[index++] = info->major; // value - - attribs[index++] = EGL_CONTEXT_MINOR_VERSION_KHR; - attribs[index++] = info->minor; + if (s_GL.apiType == EGL_OPENGL_API) { + // set version + attribs[index++] = EGL_CONTEXT_MAJOR_VERSION_KHR; // key + attribs[index++] = info->major; // value + + attribs[index++] = EGL_CONTEXT_MINOR_VERSION_KHR; + attribs[index++] = info->minor; + + // set profile mask + if (info->profile != PAL_GL_PROFILE_NONE) { + attribs[index++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + + if (info->profile == PAL_GL_PROFILE_COMPATIBILITY) { + profile = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + } else if (info->profile == PAL_GL_PROFILE_CORE) { + profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; + } - // set profile mask - if (info->profile != PAL_GL_PROFILE_NONE) { - attribs[index++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + attribs[index++] = info->profile; + } - if (info->profile == PAL_GL_PROFILE_COMPATIBILITY) { - profile = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; - } else if (info->profile == PAL_GL_PROFILE_CORE) { - profile = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; + // set forward flag + if (info->forward) { + flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; } - attribs[index++] = info->profile; - } + // set debug flag + if (info->debug) { + flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; + } - // set forward flag - if (info->forward) { - flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; - } + // set robustness + if (info->reset != PAL_GL_CONTEXT_RESET_NONE) { + flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; + attribs[index++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR; - // set debug flag - if (info->debug) { - flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; - } + if (info->reset == PAL_GL_CONTEXT_RESET_LOSE_CONTEXT) { + attribs[index++] = EGL_LOSE_CONTEXT_ON_RESET_KHR; - // set robustness - if (info->reset != PAL_GL_CONTEXT_RESET_NONE) { - flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; - attribs[index++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR; - - if (info->reset == PAL_GL_CONTEXT_RESET_LOSE_CONTEXT) { - attribs[index++] = EGL_LOSE_CONTEXT_ON_RESET_KHR; + } else if (info->reset == PAL_GL_CONTEXT_RESET_NO_NOTIFICATION) { + attribs[index++] = EGL_NO_RESET_NOTIFICATION_KHR; + } + } - } else if (info->reset == PAL_GL_CONTEXT_RESET_NO_NOTIFICATION) { - attribs[index++] = EGL_NO_RESET_NOTIFICATION_KHR; + // set no error + if (info->noError) { + attribs[index++] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR; + attribs[index++] = true; } - } - // set no error - if (info->noError) { - attribs[index++] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR; - attribs[index++] = true; - } + // release + if (info->release != PAL_GL_RELEASE_BEHAVIOR_NONE) { + attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_KHR; + attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR; + } - // release - if (info->release != PAL_GL_RELEASE_BEHAVIOR_NONE) { - attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_KHR; - attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR; + if (flags) { + attribs[index++] = EGL_CONTEXT_FLAGS_KHR; + attribs[index++] = flags; + } } - if (flags) { - attribs[index++] = EGL_CONTEXT_FLAGS_KHR; - attribs[index++] = flags; - } attribs[index++] = EGL_NONE; // clang-format off @@ -992,7 +1014,7 @@ PalResult PAL_CALL palCreateGLContext( EGLContext context = s_GL.eglCreateContext( s_GL.display, config, - EGL_NO_CONTEXT, + share, attribs); // clang-format on @@ -1025,7 +1047,7 @@ PalResult PAL_CALL palCreateGLContext( EGLSurface surface = s_GL.eglCreateWindowSurface( s_GL.display, config, - (EGLNativeWindowType)info->window->window, + (EGLNativeWindowType)info->window->window, // wl_egl_window on wayland surfaceAttribs); if (surface == EGL_NO_SURFACE) { diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index b7850fb..cceb3d8 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -79,6 +79,8 @@ typedef void* EGLNativeDisplayType; #define EGL_CAST(type, value) ((type)(value)) #define EGL_OPENGL_API 0x30A2 #define EGL_OPENGL_BIT 0x0008 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENGL_ES_API 0x30A0 #define EGL_NO_CONTEXT EGL_CAST(EGLContext, 0) #define EGL_NO_DISPLAY EGL_CAST(EGLDisplay, 0) #define EGL_NO_SURFACE EGL_CAST(EGLSurface, 0) diff --git a/tests/tests_main.c b/tests/tests_main.c index f9fa8b4..3604e63 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -36,7 +36,7 @@ int main(int argc, char** argv) // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); - registerTest("Native Integration Test", nativeIntegrationTest); + // registerTest("Native Integration Test", nativeIntegrationTest); #endif // PAL_HAS_VIDEO #if PAL_HAS_OPENGL From 9430570e3dcb065bb3c2f1823cbab2f3f511c490 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 09:47:53 +0000 Subject: [PATCH 16/51] opengl context wayland q --- CHANGELOG.md | 1 + include/pal/pal_opengl.h | 29 ++++ include/pal/pal_video.h | 16 +- src/opengl/pal_opengl_linux.c | 195 +++++++++++++++------ src/video/pal_video_linux.c | 316 +++++++++++++++++++++------------- tests/opengl_context_test.c | 75 ++++---- tests/opengl_fbconfig_test.c | 17 +- tests/tests.lua | 4 +- tests/tests_main.c | 4 +- 9 files changed, 440 insertions(+), 217 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ae38f0..3e87b32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. - **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. +- **OpenGL:** Added **palGLSetInstance()** to set the instance or display handle. ### Tests - Added native integration example: demonstrating **Native API Integration with PAL API**. see **native_integration_test.c**. diff --git a/include/pal/pal_opengl.h b/include/pal/pal_opengl.h index 08183ea..a1e7641 100644 --- a/include/pal/pal_opengl.h +++ b/include/pal/pal_opengl.h @@ -316,6 +316,10 @@ PAL_API const PalGLFBConfig* PAL_CALL palGetClosestGLFBConfig( * The provided PalGLFBConfig must be the same as the one used to create the * window. Once set, it cannot be changed. To change it, you must destroy the * window and recreate it. + * + * `PalGLWindow::display` is only used on Wayland to point to a valid + * `wl_egl_window` created for the surface. + * It is ignored on all other platforms. * * @param[in] info Pointer to a PalGLContextCreateInfo struct that specifies * paramters. Must not be nullptr. @@ -326,6 +330,9 @@ PAL_API const PalGLFBConfig* PAL_CALL palGetClosestGLFBConfig( * failure. Call palFormatResult() for more information. * * Thread safety: This function must only be called from the main thread. + * + * @note Some field names have different meaning on each platform but + * preserved for ABI stability. * * @since 1.0 * @ingroup pal_opengl @@ -441,6 +448,28 @@ PAL_API PalResult PAL_CALL palSwapBuffers( */ PAL_API PalResult PAL_CALL palSetSwapInterval(Int32 interval); +/** + * @brief Set the native application instance or display for the opengl system + * + * This must be called before palInitGL is called. + * + * On Linux: This is the Display associated with the connection. + + * On Windows: This is the HINSTANCE of the process. + * + * On Wayland: This is the Display associated with the connection. + * + * @return The instance or display on success or nullptr on failure. + * + * Thread safety: This function is thread safe. + * + * @note The returned instance or display must not be freed. + * + * @since 1.3 + * @ingroup pal_opengl + */ +PAL_API void PAL_CALL palGLSetInstance(void* instance); + /** @} */ // end of pal_opengl group #endif // _PAL_OPENGL_H \ No newline at end of file diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 10c6b9f..ee33650 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -659,8 +659,9 @@ typedef struct { typedef struct { void* nativeDisplay; /**< The platform (OS) display.*/ void* nativeWindow; /**< The window platform (OS) handle.*/ - void* nativeHandle1; /**< Extra window handle.*/ - void* nativeHandle2; /**< Extra window handle.*/ + void* nativeHandle1; /**< Extra window handle (xdgSurface)*/ + void* nativeHandle2; /**< Extra window handle (xdgToplevel)*/ + void* nativeHandle3; /**< Extra window handle (wl_egl_window)*/ } PalWindowHandleInfoEx; /** @@ -1038,7 +1039,9 @@ PAL_API PalResult PAL_CALL palSetMonitorOrientation( * - creating non resizable windows is not supported. * PAL will always creating resizable windows. * - * * - Creating windows on a specific monitor is not supported. + * - Creating windows on a specific monitor is not supported. + * + * - Creating hidden window is not supported. It will be ignored. * * @since 1.0 * @ingroup pal_video @@ -1505,9 +1508,10 @@ PAL_API PalWindowHandleInfo PAL_CALL palGetWindowHandleInfo(PalWindow* window); * * The video system must be initialized before this call. * - * On Wayland: `PalWindowHandleInfoEx::nativeHandle1` and - * `PalWindowHandleInfoEx::nativeHandle2` are xdg_surface and - * xdg_toplevel respectively. + * On Wayland: `PalWindowHandleInfoEx::nativeHandle1`, + * `PalWindowHandleInfoEx::nativeHandle2` and + * `PalWindowHandleInfoEx::nativeHandle3` are xdg_surface, xdg_toplevel + * and wl_egl_window respectively. * * @param[in] window Pointer to the window. * diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index de5fed3..d53f3c2 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -59,8 +59,6 @@ typedef void* EGLDisplay; typedef void* EGLNativeDisplayType; #ifndef EGL_OPENGL_API -// EGL header is not included - #define EGL_CAST(type, value) ((type)(value)) #define EGL_OPENGL_API 0x30A2 #define EGL_OPENGL_BIT 0x0008 @@ -104,6 +102,7 @@ typedef void* EGLNativeDisplayType; #define EGL_WINDOW_BIT 0x0004 #define EGL_CONTEXT_MAJOR_VERSION 0x3098 #define EGL_CONTEXT_MINOR_VERSION 0x30FB +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB @@ -124,7 +123,6 @@ typedef void* EGLNativeDisplayType; #define EGL_GL_COLORSPACE_KHR 0x309D #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_GL_COLORSPACE_LINEAR_KHR 0x308A - #endif // EGL header typedef void* (*eglGetProcAddressFn)(const char*); @@ -205,6 +203,8 @@ typedef EGLSurface (*eglCreateWindowSurfaceFn)( const EGLint*); typedef const GLubyte* (*glGetStringFn)(GLenum); +typedef void(PAL_GL_APIENTRY* glClearColorFn)(float, float, float, float); +typedef void(PAL_GL_APIENTRY* glClearFn)(Uint32); typedef struct { bool used; @@ -239,8 +239,11 @@ typedef struct { eglCreateWindowSurfaceFn eglCreateWindowSurface; glGetStringFn glGetString; + glClearColorFn glClearColor; + glClearFn glClear; void* handle; + void* platformDisplay; ContextData* contextData; EGLDisplay* display; PalGLInfo info; @@ -346,6 +349,10 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) return PAL_RESULT_INVALID_ALLOCATOR; } + if (!s_GL.platformDisplay) { + return PAL_RESULT_PLATFORM_FAILURE; + } + s_GL.maxContextData = 16; // initial size s_GL.contextData = palAllocate( s_GL.allocator, @@ -414,6 +421,9 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.eglCreateWindowSurface = (eglCreateWindowSurfaceFn)s_GL.eglGetProcAddress( "eglCreateWindowSurface"); + s_GL.glClearColor = (glClearColorFn)s_GL.eglGetProcAddress("glClearColor"); + s_GL.glClear = (glClearFn)s_GL.eglGetProcAddress("glClear"); + if (!s_GL.eglBindAPI || !s_GL.eglChooseConfig || !s_GL.eglCreateContext || @@ -440,6 +450,9 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) if (session) { if (strcmp(session, "wayland") == 0) { s_GL.apiType = EGL_OPENGL_ES_API; + // TODO: check features and set to maximum supported + s_GL.apiTypeBit = EGL_OPENGL_ES2_BIT; + } else { s_GL.apiType = EGL_OPENGL_API; s_GL.apiTypeBit = EGL_OPENGL_BIT; @@ -450,7 +463,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) return PAL_RESULT_PLATFORM_FAILURE; } - EGLDisplay display = s_GL.eglGetDisplay(EGL_DEFAULT_DISPLAY); + EGLDisplay display = s_GL.eglGetDisplay(s_GL.platformDisplay); if (display == EGL_NO_DISPLAY) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -463,27 +476,41 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGLConfig config; int numConfigs; EGLint type; - if (s_GL.apiType == EGL_OPENGL_API) { - EGLint attribs[] = { - EGL_RENDERABLE_TYPE, - EGL_OPENGL_BIT, - EGL_SURFACE_TYPE, - EGL_PBUFFER_BIT, - EGL_NONE}; + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, + s_GL.apiTypeBit, + EGL_SURFACE_TYPE, + EGL_PBUFFER_BIT, + EGL_NONE}; - s_GL.eglChooseConfig(display, attribs, &config, 1, &numConfigs); - s_GL.eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &type); - if (!(type & EGL_OPENGL_BIT)) { - // we must support opengl API (desktop) - return PAL_RESULT_PLATFORM_FAILURE; - } + // get a default display and create a dummy context with it + EGLDisplay defaultDis = s_GL.eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + if (!s_GL.eglInitialize(defaultDis, nullptr, nullptr)) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + s_GL.eglChooseConfig(defaultDis, attribs, &config, 1, &numConfigs); + if (!config) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + s_GL.eglGetConfigAttrib(defaultDis, config, EGL_RENDERABLE_TYPE, &type); + if (!(type & s_GL.apiTypeBit)) { + // we must support the required API + return PAL_RESULT_PLATFORM_FAILURE; } // Since we don't want to create dummy window to get the driver info // we use EGL_OPENGL_ES2_BIT to create without a window - if (!(type & EGL_OPENGL_ES2_BIT)) { - // FIXME: create a dummy window if EGL_OPENGL_ES2_BIT if using GL API - return PAL_RESULT_PLATFORM_FAILURE; + if (s_GL.apiType == EGL_OPENGL_API) { + if (!(type & EGL_OPENGL_ES2_BIT)) { + // FIXME: create a dummy window if EGL_OPENGL_ES2_BIT + return PAL_RESULT_PLATFORM_FAILURE; + } } EGLSurface surface = EGL_NO_SURFACE; @@ -493,7 +520,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGL_NONE}; surface = s_GL.eglCreatePbufferSurface( - display, + defaultDis, config, pBufferAttribs); @@ -510,7 +537,18 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGL_NONE}; context = s_GL.eglCreateContext( - display, + defaultDis, + config, + EGL_NO_CONTEXT, + contextAttrib); + + } else { + EGLint contextAttrib[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE}; + + context = s_GL.eglCreateContext( + defaultDis, config, EGL_NO_CONTEXT, contextAttrib); @@ -520,13 +558,15 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) return PAL_RESULT_PLATFORM_FAILURE; } - s_GL.eglMakeCurrent(display, surface, surface, context); + s_GL.eglMakeCurrent(defaultDis, surface, surface, context); s_GL.glGetString = (glGetStringFn)s_GL.eglGetProcAddress("glGetString"); const char* version = (const char*)s_GL.glGetString(GL_VERSION); if (version) { if (s_GL.apiType == EGL_OPENGL_API) { sscanf(version, "%d.%d", &s_GL.info.major, &s_GL.info.minor); + } else { + sscanf(version + 10, "%d.%d", &s_GL.info.major, &s_GL.info.minor); } } @@ -538,7 +578,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) // EGL extensions can be queried without a bound context // we just do that over here after making the context current - const char* extensions = s_GL.eglQueryString(display, EGL_EXTENSIONS); + const char* extensions = s_GL.eglQueryString(defaultDis, EGL_EXTENSIONS); if (extensions) { // color space if (checkExtension("EGL_KHR_gl_colorspace", extensions)) { @@ -590,19 +630,27 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.info.extensions |= PAL_GL_EXTENSION_SWAP_CONTROL; } + if (s_GL.apiType == EGL_OPENGL_ES_API) { + if (s_GL.info.extensions & PAL_GL_EXTENSION_CONTEXT_PROFILE) { + s_GL.info.extensions &= ~PAL_GL_EXTENSION_CONTEXT_PROFILE; + } + } + s_GL.eglMakeCurrent( - display, + defaultDis, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); // clang-format on - s_GL.eglDestroyContext(display, context); - s_GL.eglDestroySurface(display, surface); + s_GL.eglDestroyContext(defaultDis, context); + s_GL.eglDestroySurface(defaultDis, surface); s_GL.allocator = allocator; s_GL.initialized = true; + s_GL.eglTerminate(defaultDis); + s_GL.display = display; return PAL_RESULT_SUCCESS; } @@ -614,7 +662,10 @@ void PAL_CALL palShutdownGL() } palFree(s_GL.allocator, s_GL.contextData); - s_GL.eglTerminate(s_GL.display); + if (s_GL.display) { + s_GL.eglTerminate(s_GL.display); + } + dlclose(s_GL.handle); s_GL.initialized = false; } @@ -971,40 +1022,44 @@ PalResult PAL_CALL palCreateGLContext( flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; } - // set debug flag - if (info->debug) { - flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; - } + } else { + attribs[index++] = EGL_CONTEXT_CLIENT_VERSION; + attribs[index++] = 2; + } - // set robustness - if (info->reset != PAL_GL_CONTEXT_RESET_NONE) { - flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; - attribs[index++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR; + // set debug flag + if (info->debug) { + flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; + } - if (info->reset == PAL_GL_CONTEXT_RESET_LOSE_CONTEXT) { - attribs[index++] = EGL_LOSE_CONTEXT_ON_RESET_KHR; + // set robustness + if (info->reset != PAL_GL_CONTEXT_RESET_NONE) { + flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; + attribs[index++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR; - } else if (info->reset == PAL_GL_CONTEXT_RESET_NO_NOTIFICATION) { - attribs[index++] = EGL_NO_RESET_NOTIFICATION_KHR; - } - } + if (info->reset == PAL_GL_CONTEXT_RESET_LOSE_CONTEXT) { + attribs[index++] = EGL_LOSE_CONTEXT_ON_RESET_KHR; - // set no error - if (info->noError) { - attribs[index++] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR; - attribs[index++] = true; + } else if (info->reset == PAL_GL_CONTEXT_RESET_NO_NOTIFICATION) { + attribs[index++] = EGL_NO_RESET_NOTIFICATION_KHR; } + } - // release - if (info->release != PAL_GL_RELEASE_BEHAVIOR_NONE) { - attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_KHR; - attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR; - } + // set no error + if (info->noError) { + attribs[index++] = EGL_CONTEXT_OPENGL_NO_ERROR_KHR; + attribs[index++] = true; + } - if (flags) { - attribs[index++] = EGL_CONTEXT_FLAGS_KHR; - attribs[index++] = flags; - } + // release + if (info->release != PAL_GL_RELEASE_BEHAVIOR_NONE) { + attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_KHR; + attribs[index++] = EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR; + } + + if (flags) { + attribs[index++] = EGL_CONTEXT_FLAGS_KHR; + attribs[index++] = flags; } attribs[index++] = EGL_NONE; @@ -1044,10 +1099,18 @@ PalResult PAL_CALL palCreateGLContext( } surfaceAttribs[2] = EGL_NONE; + // get native window + EGLNativeWindowType native = 0; + if (s_GL.apiType == EGL_OPENGL_API) { + native = (EGLNativeWindowType)info->window->window; + } else { + native = (EGLNativeWindowType)info->window->display; + } + EGLSurface surface = s_GL.eglCreateWindowSurface( s_GL.display, config, - (EGLNativeWindowType)info->window->window, // wl_egl_window on wayland + native, surfaceAttribs); if (surface == EGL_NO_SURFACE) { @@ -1060,6 +1123,21 @@ PalResult PAL_CALL palCreateGLContext( } } + // make the context current and swap for the first time + // this will make the window visible + if (s_GL.eglMakeCurrent(s_GL.display, surface, surface, context)) { + s_GL.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + s_GL.glClear(0x00004000); + s_GL.eglSwapBuffers(s_GL.display, surface); + + // revert + s_GL.eglMakeCurrent( + s_GL.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT); + } + palFree(s_GL.allocator, eglConfigs); data->context = (PalGLContext*)context; data->surface = surface; @@ -1184,4 +1262,9 @@ PalResult PAL_CALL palSetSwapInterval(Int32 interval) s_GL.eglSwapInterval(s_GL.display, interval); return PAL_RESULT_SUCCESS; +} + +void PAL_CALL palGLSetInstance(void* instance) +{ + s_GL.platformDisplay = instance; } \ No newline at end of file diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index cceb3d8..3130ed5 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -147,6 +147,7 @@ typedef struct { void* buffer; void* decoration; void* cursor; + void* eglWindow; } WindowData; typedef struct { @@ -914,6 +915,24 @@ typedef struct wl_cursor* (*wl_cursor_theme_get_cursor_fn)( typedef struct wl_buffer* (*wl_cursor_image_get_buffer_fn)( struct wl_cursor_image*); +// egl_window +struct wl_egl_window; +struct wl_surface; + +typedef struct wl_egl_window* (*wl_egl_window_create_fn)( + struct wl_surface*, + int, + int); + +typedef void (*wl_egl_window_destroy_fn)(struct wl_egl_window*); + +typedef void (*wl_egl_window_resize_fn)( + struct wl_egl_window*, + int, + int, + int, + int); + typedef struct { bool checkFeatures; bool modesPhase; @@ -922,6 +941,7 @@ typedef struct { void* handle; void* xkbCommon; void* libCursor; + void* libWaylandEgl; struct wl_display* display; struct wl_registry* registry; struct xdg_wm_base* xdgBase; @@ -983,6 +1003,11 @@ typedef struct { wl_cursor_theme_load_fn cursorThemeLoad; wl_cursor_theme_get_cursor_fn cursorThemeGetCursor; wl_cursor_image_get_buffer_fn cursorImageGetBuffer; + + EGLConfig eglFBConfig; + wl_egl_window_create_fn eglWindowCreate; + wl_egl_window_destroy_fn eglWindowDestroy; + wl_egl_window_resize_fn eglWindowResize; } Wayland; typedef struct { @@ -1305,6 +1330,67 @@ static inline int wlKeyboardAddListener( (void (**)(void)) listener, data); } +static inline struct wl_region* wlCompositorCreateRegion( + struct wl_compositor *wl_compositor) +{ + struct wl_proxy *id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_compositor, + WL_COMPOSITOR_CREATE_REGION, + s_Wl.regionInterface, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_compositor), + 0, + NULL); + + return (struct wl_region *) id; +} + +static inline void wlRegionAdd( + struct wl_region *wl_region, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_region, + WL_REGION_ADD, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_region), + 0, + x, + y, + width, + height); +} + +static inline void wlSurfaceSetOpaqueRegion( + struct wl_surface *wl_surface, + struct wl_region *region) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_surface, + WL_SURFACE_SET_OPAQUE_REGION, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_surface), + 0, + region); +} + +static inline void wlRegionDestroy(struct wl_region *wl_region) +{ + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_region, + WL_REGION_DESTROY, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_region), + WL_MARSHAL_FLAG_DESTROY); +} + static void surfaceHandleEnter( void* userData, struct wl_surface* surface, @@ -1961,23 +2047,34 @@ static void xdgSurfaceHandleConfigure( if (!winData->skipConfigure) { winData->skipConfigure = true; - // create a new buffer with the new size - struct wl_buffer* buffer = nullptr; - buffer = createShmBuffer(winData->w, winData->h, nullptr, false); - if (!buffer) { - return; - } + if (winData->eglWindow) { + s_Wl.eglWindowResize( + winData->eglWindow, + winData->w, + winData->h, + 0, + 0); + + } else { + // create a new buffer with the new size + struct wl_buffer* buffer = nullptr; + buffer = createShmBuffer(winData->w, winData->h, nullptr, false); + if (!buffer) { + return; + } - struct wl_surface* _surface = nullptr; - _surface = (struct wl_surface*)winData->window; + struct wl_surface* _surface = nullptr; + _surface = (struct wl_surface*)winData->window; - wlSurfaceAttach(_surface, buffer, 0, 0); - wlSurfaceDamageBuffer(_surface, 0, 0, winData->w, winData->h); - wlSurfaceCommit(_surface); + wlSurfaceAttach(_surface, buffer, 0, 0); + wlSurfaceDamageBuffer(_surface, 0, 0, winData->w, winData->h); + wlSurfaceCommit(_surface); - // destroy old buffer - wlBufferDestroy(winData->buffer); - winData->buffer = buffer; + + // destroy old buffer + wlBufferDestroy(winData->buffer); + winData->buffer = buffer; + } // push a window resize event if (s_Video.eventDriver) { @@ -2596,7 +2693,7 @@ static void setupZwpPointerProtocol() } #endif // PAL_HAS_WAYLAND -#pragma endregions +#pragma endregion // ================================================== // Internal API @@ -5957,6 +6054,37 @@ static Backend s_XBackend = { #pragma region Wayland API #if PAL_HAS_WAYLAND +PalResult eglWlBackend(const int index) +{ + // user choose EGL FBConfig backend + if (!s_Egl.handle) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + EGLDisplay display = EGL_NO_DISPLAY; + display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_Wl.display); + + if (display == EGL_NO_DISPLAY) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + EGLint numConfigs = 0; + if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + EGLint configSize = sizeof(EGLConfig) * numConfigs; + EGLConfig* eglConfigs = palAllocate(s_Video.allocator, configSize, 0); + if (!eglConfigs) { + return PAL_RESULT_OUT_OF_MEMORY; + } + + s_Egl.eglGetConfigs(display, eglConfigs, numConfigs, &numConfigs); + s_Wl.eglFBConfig = eglConfigs[index]; + + return PAL_RESULT_SUCCESS; +} + static void wlCreateKeycodeTable() { // Tis is for only printable and text input keys @@ -6342,7 +6470,13 @@ PalResult wlInitVideo() s_Wl.handle = dlopen("libwayland-client.so.0", RTLD_LAZY); s_Wl.xkbCommon = dlopen("libxkbcommon.so", RTLD_LAZY); s_Wl.libCursor = dlopen("libwayland-cursor.so", RTLD_LAZY); - if (!s_Wl.handle || !s_Wl.xkbCommon || !s_Wl.libCursor) { + s_Wl.libWaylandEgl = dlopen("libwayland-egl.so", RTLD_LAZY); + + // clang-format off + if (!s_Wl.handle || + !s_Wl.xkbCommon || + !s_Wl.libCursor || + !s_Wl.libWaylandEgl) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -6478,6 +6612,21 @@ PalResult wlInitVideo() s_Wl.libCursor, "wl_cursor_theme_get_cursor"); + // wl_egl procs + s_Wl.eglWindowCreate = (wl_egl_window_create_fn)dlsym( + s_Wl.libWaylandEgl, + "wl_egl_window_create"); + + s_Wl.eglWindowDestroy = (wl_egl_window_destroy_fn)dlsym( + s_Wl.libWaylandEgl, + "wl_egl_window_destroy"); + + s_Wl.eglWindowResize = (wl_egl_window_resize_fn)dlsym( + s_Wl.libWaylandEgl, + "wl_egl_window_resize"); + + // clang-format on + // initialize wayland s_Wl.modesPhase = false; s_Wl.checkFeatures = true; @@ -6533,6 +6682,7 @@ void wlShutdownVideo() dlclose(s_Wl.libCursor); dlclose(s_Wl.xkbCommon); + dlclose(s_Wl.libWaylandEgl); dlclose(s_Wl.handle); } @@ -6586,87 +6736,6 @@ void wlUpdateVideo() s_Wl.displayFlush(s_Wl.display); } -PalResult wlSetFBConfig( - const int index, - PalFBConfigBackend backend) -{ - // user choose EGL FBConfig backend - if (!s_Egl.handle) { - return PAL_RESULT_PLATFORM_FAILURE; - } - - if (!s_Egl.eglBindAPI(EGL_OPENGL_API)) { - return PAL_RESULT_PLATFORM_FAILURE; - } - - EGLDisplay display = EGL_NO_DISPLAY; - display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_Wl.display); - - if (display == EGL_NO_DISPLAY) { - return PAL_RESULT_PLATFORM_FAILURE; - } - - if (!s_Egl.eglInitialize(display, nullptr, nullptr)) { - return PAL_RESULT_PLATFORM_FAILURE; - } - - EGLint numConfigs = 0; - if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { - return PAL_RESULT_PLATFORM_FAILURE; - } - - EGLint configSize = sizeof(EGLConfig) * numConfigs; - EGLConfig* eglConfigs = palAllocate(s_Video.allocator, configSize, 0); - if (!eglConfigs) { - return PAL_RESULT_OUT_OF_MEMORY; - } - - s_Egl.eglGetConfigs(display, eglConfigs, numConfigs, &numConfigs); - EGLConfig config = eglConfigs[index]; - - // FIXME: - - // // we create a visual from the config - // EGLint visualID; - // s_Egl.eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualID); - // if (visualID == 0) { - // return PAL_RESULT_INVALID_GL_FBCONFIG; - // } - - // int numVisuals = 0; - // XVisualInfo tmp; - // tmp.visualid = visualID; - - // // clang-format off - // // get a matching visual info - // XVisualInfo* visualInfo = s_X11.getVisualInfo( - // s_X11.display, - // VisualIDMask, - // &tmp, - // &numVisuals); - // // clang-format on - - // if (!visualInfo) { - // return PAL_RESULT_INVALID_GL_FBCONFIG; - // } - - // s_X11.visual = visualInfo->visual; - // s_X11.depth = visualInfo->depth; - // s_X11.colormap = s_X11.createColormap( - // s_X11.display, - // s_X11.root, - // visualInfo->visual, - // AllocNone); - - // if (!s_X11.colormap) { - // return PAL_RESULT_INVALID_GL_FBCONFIG; - // } - - // s_Egl.eglTerminate(display); - // palFree(s_Video.allocator, eglConfigs); - return PAL_RESULT_SUCCESS; -} - PalResult wlEnumerateMonitors( Int32* count, PalMonitor** outMonitors) @@ -6916,13 +6985,6 @@ PalResult wlCreateWindow( data->isAttached = false; data->state = PAL_WINDOW_STATE_RESTORED; - // show window - if (info->show == false) { - // we cant do much anymore - *outWindow = (PalWindow*)surface; - return PAL_RESULT_SUCCESS; - } - // minimize // This is just a requeest, the compositor might ignore it if (info->minimized) { @@ -6945,19 +7007,34 @@ PalResult wlCreateWindow( data->decoration = decoration; } - // create a white buffer for the surface - struct wl_buffer* buffer = nullptr; - buffer = createShmBuffer(data->w, data->h, nullptr, false); - if (!buffer) { - return PAL_RESULT_PLATFORM_FAILURE; + if (s_Wl.eglFBConfig) { + data->eglWindow = s_Wl.eglWindowCreate(surface, data->w, data->h); + if (!data->eglWindow) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + } else { + // create a white buffer for the surface + struct wl_buffer* buffer = nullptr; + buffer = createShmBuffer(data->w, data->h, nullptr, false); + if (!buffer) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + wlSurfaceAttach(surface, buffer, 0, 0); + wlSurfaceDamageBuffer(surface, 0, 0, data->w, data->h); + wlSurfaceCommit(surface); + data->buffer = buffer; } - wlSurfaceAttach(surface, buffer, 0, 0); - wlSurfaceDamageBuffer(surface, 0, 0, data->w, data->h); - wlSurfaceCommit(surface); + struct wl_region* region = wlCompositorCreateRegion(s_Wl.compositor); + if (region) { + wlRegionAdd(region, 0, 0, data->w, data->h); + wlSurfaceSetOpaqueRegion(surface, region); + wlRegionDestroy(region); + } + s_Wl.displayRoundtrip(s_Wl.display); - - data->buffer = buffer; *outWindow = data->window; return PAL_RESULT_SUCCESS; } @@ -7100,12 +7177,12 @@ PalWindowHandleInfoEx wlGetWindowHandleInfoEx(PalWindow* window) { PalWindowHandleInfoEx info = {0}; WindowData* data = findWindowData(window); - info.nativeDisplay = (void*)s_Wl.display; - info.nativeWindow = (void*)window; - if (data) { + info.nativeDisplay = (void*)s_Wl.display; + info.nativeWindow = (void*)window; info.nativeHandle1 = data->xdgSurface; info.nativeHandle2 = data->xdgToplevel; + info.nativeHandle3 = data->eglWindow; } return info; @@ -7558,6 +7635,8 @@ PalResult PAL_CALL palSetFBConfig( backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { if (s_X11.display) { return eglXBackend(index); + } else { + return eglWlBackend(index); } } } @@ -8286,7 +8365,10 @@ void* PAL_CALL palGetInstance() if (s_X11.display) { // we are on X11 return (void*)s_X11.display; + } else { + return (void*)s_Wl.display; } + return nullptr; } diff --git a/tests/opengl_context_test.c b/tests/opengl_context_test.c index ba7d685..9cb8620 100644 --- a/tests/opengl_context_test.c +++ b/tests/opengl_context_test.c @@ -23,22 +23,7 @@ bool openglContextTest() palLog(nullptr, "==========================================="); palLog(nullptr, ""); - // initialize the opengl system - PalResult result = palInitGL(nullptr); - if (result != PAL_RESULT_SUCCESS) { - const char* error = palFormatResult(result); - palLog(nullptr, "Failed to initialize opengl: %s", error); - return false; - } - - PalWindow* window = nullptr; - PalGLContext* context = nullptr; - PalWindowCreateInfo createInfo = {0}; - PalGLContextCreateInfo contextCreateInfo = {0}; - Int32 fbCount = 0; - bool running = false; - - // event driver + PalResult result; PalEventDriver* eventDriver = nullptr; PalEventDriverCreateInfo eventDriverCreateInfo = {0}; @@ -56,6 +41,35 @@ bool openglContextTest() return false; } + // initialize the video system. We pass the event driver to recieve video + // related events the video system does not copy the event driver, it must + // be valid till the video system is shutdown + result = palInitVideo(nullptr, eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + // get the instance or display handle and pass it to the opengl system + // This must be called before the opengl system is initialized + palGLSetInstance(palGetInstance()); + + // initialize the opengl system + result = palInitGL(nullptr); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize opengl: %s", error); + return false; + } + + PalWindow* window = nullptr; + PalGLContext* context = nullptr; + PalWindowCreateInfo createInfo = {0}; + PalGLContextCreateInfo contextCreateInfo = {0}; + Int32 fbCount = 0; + bool running = false; + // enumerate supported opengl framebuffer configs // glWindow must be nullptr result = palEnumerateGLFBConfigs(nullptr, &fbCount, nullptr); @@ -123,16 +137,6 @@ bool openglContextTest() palLog(nullptr, " sRGB: %s", g_BoolsToSting[closest->sRGB]); palLog(nullptr, ""); - // initialize the video system. We pass the event driver to recieve video - // related events the video system does not copy the event driver, it must - // be valid till the video system is shutdown - result = palInitVideo(nullptr, eventDriver); - if (result != PAL_RESULT_SUCCESS) { - const char* error = palFormatResult(result); - palLog(nullptr, "Failed to initialize video: %s", error); - return false; - } - // set the FBConfig that will be used by PAL video system // to create windows. this must be set before creating a window // for this example, we set the closest we desired. @@ -155,6 +159,14 @@ bool openglContextTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; createInfo.title = "Pal Opengl Context Window"; + // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + // create the window with the create info struct result = palCreateWindow(&createInfo, &window); if (result != PAL_RESULT_SUCCESS) { @@ -172,14 +184,13 @@ bool openglContextTest() // get window handle. You can use any window from any library // so long as you can get the window handle and display (if on X11, wayland) // If pal video system will not be used, there is no need to initialize it - PalWindowHandleInfo windowHandleInfo; - windowHandleInfo = palGetWindowHandleInfo(window); + PalWindowHandleInfoEx winHandle = {0}; + winHandle = palGetWindowHandleInfoEx(window); // PalGLWindow is just a struct to hold native handles PalGLWindow glWindow = {0}; - // needed when using X11 or wayland - glWindow.display = windowHandleInfo.nativeDisplay; - glWindow.window = windowHandleInfo.nativeWindow; + glWindow.display = winHandle.nativeHandle3; + glWindow.window = winHandle.nativeWindow; // get opengl info const PalGLInfo* info = palGetGLInfo(); @@ -238,7 +249,7 @@ bool openglContextTest() glClear = (PFNGLCLEARPROC)palGLGetProcAddress("glClear"); // set clear color - glClearColor(.2f, .2f, .2f, .2f); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); running = true; while (running) { diff --git a/tests/opengl_fbconfig_test.c b/tests/opengl_fbconfig_test.c index 407daee..8ca0673 100644 --- a/tests/opengl_fbconfig_test.c +++ b/tests/opengl_fbconfig_test.c @@ -13,8 +13,20 @@ bool openglFBConfigTest() palLog(nullptr, "==========================================="); palLog(nullptr, ""); + // initialize the video system and create a window + PalResult result = palInitVideo(nullptr, nullptr); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + // get the instance or display handle and pass it to the opengl system + // This must be called before the opengl system is initialized + palGLSetInstance(palGetInstance()); + // initialize the opengl system - PalResult result = palInitGL(nullptr); + result = palInitGL(nullptr); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); palLog(nullptr, "Failed to initialize opengl: %s", error); @@ -22,7 +34,6 @@ bool openglFBConfigTest() } // enumerate supported opengl framebuffer configs - // glWindow must be nullptr for default Int32 fbCount = 0; result = palEnumerateGLFBConfigs(nullptr, &fbCount, nullptr); if (result != PAL_RESULT_SUCCESS) { @@ -127,6 +138,8 @@ bool openglFBConfigTest() // shutdown the opengl system palShutdownGL(); + palShutdownVideo(); + // free the framebuffer configs palFree(nullptr, fbConfigs); diff --git a/tests/tests.lua b/tests/tests.lua index e3fe863..547fc40 100644 --- a/tests/tests.lua +++ b/tests/tests.lua @@ -48,13 +48,13 @@ project "tests" if (PAL_BUILD_OPENGL) then files { - "opengl_test.c", - "opengl_fbconfig_test.c" + "opengl_test.c" } end if (PAL_BUILD_OPENGL and PAL_BUILD_VIDEO) then files { + "opengl_fbconfig_test.c", "opengl_context_test.c", "opengl_multi_context_test.c" } diff --git a/tests/tests_main.c b/tests/tests_main.c index 3604e63..4216df9 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -41,13 +41,13 @@ int main(int argc, char** argv) #if PAL_HAS_OPENGL // registerTest("Opengl Test", openglTest); - // registerTest("Opengl FBConfig Test", openglFBConfigTest); #endif // PAL_HAS_OPENGL // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - // registerTest("Opengl Context Test", openglContextTest); + // registerTest("Opengl FBConfig Test", openglFBConfigTest); + registerTest("Opengl Context Test", openglContextTest); // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL From 996b26454cdf48d84f2a96140f70f38c0e52157d Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 12:59:41 +0000 Subject: [PATCH 17/51] wayland opengl --- CHANGELOG.md | 2 + include/pal/pal_opengl.h | 39 ++++++--- include/pal/pal_video.h | 3 +- src/opengl/pal_opengl_linux.c | 71 +++++++++++++---- src/opengl/pal_opengl_win32.c | 20 +++++ src/video/pal_video_linux.c | 8 +- tests/multi_thread_opengl_test.c | 114 ++++++++++++++++++++------- tests/opengl_context_test.c | 43 ++++++++-- tests/opengl_fbconfig_test.c | 1 + tests/opengl_multi_context_test.c | 126 +++++++++++++++++++----------- tests/tests_main.c | 4 +- 11 files changed, 314 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e87b32..2556ef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,9 +77,11 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Video:** Added **palGetVideoFeaturesEx()** to check old and extended supported features. - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. - **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. +- **Video:** Added **PAL_CONFIG_BACKEND_GLES** to `PalFBConfigBackend` enum. - **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. - **OpenGL:** Added **palGLSetInstance()** to set the instance or display handle. +- **OpenGL:** Added **palGLGetBackend()** to get the opengl backend. ### Tests - Added native integration example: demonstrating **Native API Integration with PAL API**. see **native_integration_test.c**. diff --git a/include/pal/pal_opengl.h b/include/pal/pal_opengl.h index a1e7641..d43011d 100644 --- a/include/pal/pal_opengl.h +++ b/include/pal/pal_opengl.h @@ -169,7 +169,7 @@ typedef struct { */ typedef struct { void* display; /**< Can be nullptr depending on platform (eg. Windows).*/ - void* window; /**< Must not be nullptr.*/ + void* window; /**< Must not be nullptr. (egl_wl_window on Wayland)*/ } PalGLWindow; /** @@ -215,6 +215,7 @@ typedef struct { * @since 1.0 * @ingroup pal_opengl * @sa palShutdownGL + * @sa palGLSetInstance */ PAL_API PalResult PAL_CALL palInitGL(const PalAllocator* allocator); @@ -317,9 +318,8 @@ PAL_API const PalGLFBConfig* PAL_CALL palGetClosestGLFBConfig( * window. Once set, it cannot be changed. To change it, you must destroy the * window and recreate it. * - * `PalGLWindow::display` is only used on Wayland to point to a valid - * `wl_egl_window` created for the surface. - * It is ignored on all other platforms. + * On Wayland: PalGLContextCreateInfo::PalGLWindow::window is the wl_egl_window + * not the wl_surface. * * @param[in] info Pointer to a PalGLContextCreateInfo struct that specifies * paramters. Must not be nullptr. @@ -330,9 +330,6 @@ PAL_API const PalGLFBConfig* PAL_CALL palGetClosestGLFBConfig( * failure. Call palFormatResult() for more information. * * Thread safety: This function must only be called from the main thread. - * - * @note Some field names have different meaning on each platform but - * preserved for ABI stability. * * @since 1.0 * @ingroup pal_opengl @@ -369,6 +366,10 @@ PAL_API void PAL_CALL palDestroyGLContext(PalGLContext* context); * context. If the PalGLFBConfig of the opengl window is not the same as the one * used to create the context, this function fails and returns * `PAL_RESULT_INVALID_GL_WINDOW`. + * + * If the window was created with a different display other than the one + * passed to the opengl system, this function fails and returns + * `PAL_RESULT_INVALID_GL_WINDOW`. see palGLSetInstance() * * @param[in] glWindow Pointer to the opengl window. * @param[in] context Pointer to the context to make current. @@ -449,27 +450,39 @@ PAL_API PalResult PAL_CALL palSwapBuffers( PAL_API PalResult PAL_CALL palSetSwapInterval(Int32 interval); /** - * @brief Set the native application instance or display for the opengl system + * @brief Set the native application instance or display for the opengl system. * - * This must be called before palInitGL is called. + * This must be called before palInitGL() is called. if palInitGL() is called + * before this function, + * it fails and returns `PAL_RESULT_PLATFORM_FAILURE` will be returned. * * On Linux: This is the Display associated with the connection. * On Windows: This is the HINSTANCE of the process. * * On Wayland: This is the Display associated with the connection. - * - * @return The instance or display on success or nullptr on failure. * * Thread safety: This function is thread safe. * - * @note The returned instance or display must not be freed. - * * @since 1.3 * @ingroup pal_opengl + * @sa palInitGL */ PAL_API void PAL_CALL palGLSetInstance(void* instance); +/** + * @brief Get the backend of the opengl system. + * + * The opengl system must be initialized before this call. + * Possible values are `wgl`, `glx`, `gles`, `egl`. + * + * Thread safety: This function is thread safe. + * + * @since 1.3 + * @ingroup pal_opengl + */ +PAL_API const char* PAL_CALL palGLGetBackend(); + /** @} */ // end of pal_opengl group #endif // _PAL_OPENGL_H \ No newline at end of file diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index ee33650..439a193 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -254,7 +254,8 @@ typedef enum { PAL_CONFIG_BACKEND_EGL, PAL_CONFIG_BACKEND_GLX, PAL_CONFIG_BACKEND_WGL, - PAL_CONFIG_BACKEND_PAL_OPENGL /**< Use PAL opengl module backend.*/ + PAL_CONFIG_BACKEND_PAL_OPENGL, /**< Use PAL opengl module backend.*/ + PAL_CONFIG_BACKEND_GLES } PalFBConfigBackend; /** diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index d53f3c2..f46673c 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -450,8 +450,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) if (session) { if (strcmp(session, "wayland") == 0) { s_GL.apiType = EGL_OPENGL_ES_API; - // TODO: check features and set to maximum supported - s_GL.apiTypeBit = EGL_OPENGL_ES2_BIT; + s_GL.apiTypeBit = EGL_OPENGL_ES2_BIT; // default } else { s_GL.apiType = EGL_OPENGL_API; @@ -620,8 +619,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.info.extensions |= PAL_GL_EXTENSION_MULTISAMPLE; if (type & EGL_OPENGL_ES_BIT || - type & EGL_OPENGL_ES2_BIT || - type & EGL_OPENGL_ES3_BIT) { + type & EGL_OPENGL_ES2_BIT) { s_GL.info.extensions |= PAL_GL_EXTENSION_CONTEXT_PROFILE_ES2; } @@ -630,9 +628,15 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.info.extensions |= PAL_GL_EXTENSION_SWAP_CONTROL; } - if (s_GL.apiType == EGL_OPENGL_ES_API) { + if (s_GL.apiType != EGL_OPENGL_API) { if (s_GL.info.extensions & PAL_GL_EXTENSION_CONTEXT_PROFILE) { s_GL.info.extensions &= ~PAL_GL_EXTENSION_CONTEXT_PROFILE; + s_GL.info.extensions &= ~PAL_GL_EXTENSION_CONTEXT_PROFILE_ES2; + } + } else { + // check to see if we support EGL_OPENGL_ES3_BIT + if (type & EGL_OPENGL_ES3_BIT) { + s_GL.apiTypeBit = EGL_OPENGL_ES3_BIT; } } @@ -744,8 +748,22 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( continue; } - if (!(renderable & s_GL.apiTypeBit)) { - continue; + if (s_GL.apiType == EGL_OPENGL_ES3_BIT) { + // EGL_OPENGL_ES2_BIT + if (!(renderable & EGL_OPENGL_ES2_BIT) && + !(renderable & EGL_OPENGL_ES3_BIT)) { + continue; + } + + } else if (s_GL.apiType == EGL_OPENGL_ES2_BIT) { + if (!(renderable & EGL_OPENGL_ES2_BIT)) { + continue; + } + + } else { + if (!(renderable & EGL_OPENGL_BIT)) { + continue; + } } if (!(surfaceType & EGL_WINDOW_BIT)) { @@ -920,6 +938,12 @@ PalResult PAL_CALL palCreateGLContext( return PAL_RESULT_NULL_POINTER; } + // check if the window was created with the same display + // the opengl system is using + if (info->window->display != s_GL.platformDisplay) { + return PAL_RESULT_INVALID_GL_WINDOW; + } + // check support for requested features if (info->profile != PAL_GL_PROFILE_NONE) { if (!(s_GL.info.extensions & PAL_GL_EXTENSION_CONTEXT_PROFILE)) { @@ -1024,7 +1048,8 @@ PalResult PAL_CALL palCreateGLContext( } else { attribs[index++] = EGL_CONTEXT_CLIENT_VERSION; - attribs[index++] = 2; + // our configs support EGL_OPENGL_ES2_BIT and EGL_OPENGL_ES3_BIT + attribs[index++] = info->major; } // set debug flag @@ -1099,18 +1124,10 @@ PalResult PAL_CALL palCreateGLContext( } surfaceAttribs[2] = EGL_NONE; - // get native window - EGLNativeWindowType native = 0; - if (s_GL.apiType == EGL_OPENGL_API) { - native = (EGLNativeWindowType)info->window->window; - } else { - native = (EGLNativeWindowType)info->window->display; - } - EGLSurface surface = s_GL.eglCreateWindowSurface( s_GL.display, config, - native, + (EGLNativeWindowType)info->window->window, surfaceAttribs); if (surface == EGL_NO_SURFACE) { @@ -1151,6 +1168,13 @@ void PAL_CALL palDestroyGLContext(PalGLContext* context) if (s_GL.initialized && context) { ContextData* data = findContextData(context); if (data) { + // make it not current if it was current + s_GL.eglMakeCurrent( + s_GL.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT); + s_GL.eglDestroyContext(s_GL.display, (EGLContext)context); s_GL.eglDestroySurface(s_GL.display, data->surface); data->used = false; @@ -1267,4 +1291,17 @@ PalResult PAL_CALL palSetSwapInterval(Int32 interval) void PAL_CALL palGLSetInstance(void* instance) { s_GL.platformDisplay = instance; +} + +const char* PAL_CALL palGLGetBackend() +{ + if (!s_GL.initialized) { + return nullptr; + } + + if (s_GL.apiType == EGL_OPENGL_API) { + return "egl"; + } else { + return "gles"; + } } \ No newline at end of file diff --git a/src/opengl/pal_opengl_win32.c b/src/opengl/pal_opengl_win32.c index 94f43e6..d815fed 100644 --- a/src/opengl/pal_opengl_win32.c +++ b/src/opengl/pal_opengl_win32.c @@ -1036,4 +1036,24 @@ PalResult PAL_CALL palSetSwapInterval(Int32 interval) s_Wgl.wglSwapIntervalEXT(interval); return PAL_RESULT_SUCCESS; +} + +void PAL_CALL palGLSetInstance(void* instance) +{ + // TODO + s_GL.platformDisplay = instance; +} + +const char* PAL_CALL palGLGetBackend() +{ + // TODO: + if (!s_GL.initialized) { + return nullptr; + } + + if (s_GL.apiType == EGL_OPENGL_API) { + return "egl"; + } else { + return "gles"; + } } \ No newline at end of file diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 3130ed5..8da0479 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -7032,6 +7032,7 @@ PalResult wlCreateWindow( wlRegionAdd(region, 0, 0, data->w, data->h); wlSurfaceSetOpaqueRegion(surface, region); wlRegionDestroy(region); + wlSurfaceCommit(surface); } s_Wl.displayRoundtrip(s_Wl.display); @@ -7050,7 +7051,12 @@ void wlDestroyWindow(PalWindow* window) zxdgToplevelDecorationV1Destroy(data->decoration); } - wlBufferDestroy(data->buffer); + if (data->eglWindow) { + s_Wl.eglWindowDestroy(data->eglWindow); + } else { + wlBufferDestroy(data->buffer); + } + xdgToplevelDestroy(data->xdgToplevel); xdgSurfaceDestroy(data->xdgSurface); wlSurfaceDestroy((struct wl_surface*)window); diff --git a/tests/multi_thread_opengl_test.c b/tests/multi_thread_opengl_test.c index b375b53..16e4fe1 100644 --- a/tests/multi_thread_opengl_test.c +++ b/tests/multi_thread_opengl_test.c @@ -37,6 +37,7 @@ typedef void (*glViewportFn)( typedef struct { bool driverCreated; bool running; + bool fixedPipeline; PalEventDriver* videoEventDriver; PalEventDriver* openglEventDriver; PalGLContext* context; @@ -131,7 +132,7 @@ static void* PAL_CALL rendererWorkder(void* arg) // set clear color glViewport(0, 0, 640, 480); - glClearColor(.2f, .2f, .2f, .2f); + glClearColor(.2f, .2f, .2f, 1.0f); // run our while loop over there while (shared->running) { @@ -159,18 +160,20 @@ static void* PAL_CALL rendererWorkder(void* arg) glClear(0x00004000); // GL_COLOR_BUFFER_BIT // draw a triangle using the fixed pipeline - glBegin(0x0004); // GL_TRIANGLES - glColor3f(1.0, 0.0, 0.0); - glVertex2f(-0.5f, -0.5f); + if (shared->fixedPipeline) { + glBegin(0x0004); // GL_TRIANGLES + glColor3f(1.0, 0.0, 0.0); + glVertex2f(-0.5f, -0.5f); - glColor3f(0.0, 1.0, 0.0); - glVertex2f(0.5f, -0.5f); + glColor3f(0.0, 1.0, 0.0); + glVertex2f(0.5f, -0.5f); - glColor3f(0.0, 0.0, 1.0); - glVertex2f(0.0f, 0.5f); + glColor3f(0.0, 0.0, 1.0); + glVertex2f(0.0f, 0.5f); - glEnd(); - glFlush(); + glEnd(); + glFlush(); + } // swap buffers result = palSwapBuffers(&shared->window, shared->context); @@ -213,6 +216,27 @@ bool multiThreadOpenGlTest() return false; } + // check to see if the event driver thread is done creating the drivers + // if not we wait for it + if (!shared->driverCreated) { + palJoinThread(eventDriverThread, nullptr); + } + palDetachThread(eventDriverThread); // we dont need it anymore + + // initialize the video system. We pass the event driver to recieve video + // related events the video system does not copy the event driver, it must + // be valid till the video system is shutdown + result = palInitVideo(nullptr, shared->videoEventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + // get the instance or display handle and pass it to the opengl system + // This must be called before the opengl system is initialized + palGLSetInstance(palGetInstance()); + // initialize video and opengl systems // we need to initialize these on the main thread result = palInitGL(nullptr); @@ -268,20 +292,6 @@ bool multiThreadOpenGlTest() const PalGLFBConfig* closest = nullptr; closest = palGetClosestGLFBConfig(fbConfigs, fbCount, &desired); - // check to see if the event driver thread is done creating the drivers - // if not we wait for it - if (!shared->driverCreated) { - palJoinThread(eventDriverThread, nullptr); - } - palDetachThread(eventDriverThread); // we dont need it anymore - - result = palInitVideo(nullptr, shared->videoEventDriver); - if (result != PAL_RESULT_SUCCESS) { - const char* error = palFormatResult(result); - palLog(nullptr, "Failed to initialize video: %s", error); - return false; - } - // tell the video system to use our closest FBConfig // to create the windows result = palSetFBConfig(closest->index, PAL_CONFIG_BACKEND_PAL_OPENGL); @@ -291,6 +301,25 @@ bool multiThreadOpenGlTest() return false; } + // if not using pal_opengl with pal_video + // we get the backend string from the opengl system and + // get the backend from it + // Possible values are `wgl`, `glx`, `gles`, `egl`. + // PalFBConfigBackend backend; + // const char* glBackendString = palGLGetBackend(); + // if (strcmp(glBackendString, "wgl") == 0) { + // backend = PAL_CONFIG_BACKEND_WGL; + + // } else if (strcmp(glBackendString, "glx") == 0) { + // backend = PAL_CONFIG_BACKEND_GLX; + + // } else if (strcmp(glBackendString, "gles") == 0) { + // backend = PAL_CONFIG_BACKEND_GLES; + + // } else if (strcmp(glBackendString, "egl") == 0) { + // backend = PAL_CONFIG_BACKEND_EGL; + // } + // all windows also needs to be created on the main thread PalWindow* window = nullptr; PalWindowCreateInfo windowCreateInfo = {0}; @@ -299,6 +328,15 @@ bool multiThreadOpenGlTest() windowCreateInfo.show = true; windowCreateInfo.style = PAL_WINDOW_STYLE_RESIZABLE; windowCreateInfo.title = "Multi Thread OpenGL Window"; + + // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + windowCreateInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + result = palCreateWindow(&windowCreateInfo, &window); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); @@ -310,10 +348,19 @@ bool multiThreadOpenGlTest() const PalGLInfo* glInfo = palGetGLInfo(); // get the native handles of our created window - PalWindowHandleInfo windowHandleInfo; - windowHandleInfo = palGetWindowHandleInfo(window); - shared->window.display = windowHandleInfo.nativeDisplay; - shared->window.window = windowHandleInfo.nativeWindow; + PalWindowHandleInfoEx winHandle = {0}; + winHandle = palGetWindowHandleInfoEx(window); + + shared->window.display = winHandle.nativeDisplay; + + // On Wayland the window is the wl_egl_window + if (winHandle.nativeHandle3) { + // the window has a valid wl_egl_window + shared->window.window = winHandle.nativeHandle3; + + } else { + shared->window.window = winHandle.nativeWindow; + } PalGLContextCreateInfo contextCreateInfo = {0}; contextCreateInfo.debug = true; @@ -325,8 +372,14 @@ bool multiThreadOpenGlTest() // we dont want to get into GL pipeline for this example // so we request a Compatibility profile if supported // NOTE: is its not supported, no triangle would be displayed + shared->fixedPipeline = false; if (glInfo->extensions & PAL_GL_EXTENSION_CONTEXT_PROFILE) { contextCreateInfo.profile = PAL_GL_PROFILE_COMPATIBILITY; + shared->fixedPipeline = true; + } + + if (!shared->fixedPipeline) { + palLog(nullptr, "Fixed pipeline not supported"); } result = palCreateGLContext(&contextCreateInfo, &shared->context); @@ -375,11 +428,12 @@ bool multiThreadOpenGlTest() } } + palDestroyGLContext(shared->context); + palShutdownGL(); + // shutdown video and opengl systems // we need to shutdown these on the main thread palDestroyWindow(window); - palDestroyGLContext(shared->context); - palShutdownGL(); palShutdownVideo(); // The event drivers cn be destroyed on a seperate thread diff --git a/tests/opengl_context_test.c b/tests/opengl_context_test.c index 9cb8620..dccf63b 100644 --- a/tests/opengl_context_test.c +++ b/tests/opengl_context_test.c @@ -152,6 +152,25 @@ bool openglContextTest() return false; } + // if not using pal_opengl with pal_video + // we get the backend string from the opengl system and + // get the backend from it + // Possible values are `wgl`, `glx`, `gles`, `egl`. + // PalFBConfigBackend backend; + // const char* glBackendString = palGLGetBackend(); + // if (strcmp(glBackendString, "wgl") == 0) { + // backend = PAL_CONFIG_BACKEND_WGL; + + // } else if (strcmp(glBackendString, "glx") == 0) { + // backend = PAL_CONFIG_BACKEND_GLX; + + // } else if (strcmp(glBackendString, "gles") == 0) { + // backend = PAL_CONFIG_BACKEND_GLES; + + // } else if (strcmp(glBackendString, "egl") == 0) { + // backend = PAL_CONFIG_BACKEND_EGL; + // } + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; @@ -189,8 +208,16 @@ bool openglContextTest() // PalGLWindow is just a struct to hold native handles PalGLWindow glWindow = {0}; - glWindow.display = winHandle.nativeHandle3; - glWindow.window = winHandle.nativeWindow; + glWindow.display = winHandle.nativeDisplay; + + // On Wayland the window is the wl_egl_window + if (winHandle.nativeHandle3) { + // the window has a valid wl_egl_window + glWindow.window = winHandle.nativeHandle3; + + } else { + glWindow.window = winHandle.nativeWindow; + } // get opengl info const PalGLInfo* info = palGetGLInfo(); @@ -279,6 +306,12 @@ bool openglContextTest() } } + // destroy the opengl context + palDestroyGLContext(context); + + // shutdown the opengl system + palShutdownGL(); + // destroy the window palDestroyWindow(window); @@ -288,12 +321,6 @@ bool openglContextTest() // destroy the event driver palDestroyEventDriver(eventDriver); - // destroy the opengl context - palDestroyGLContext(context); - - // shutdown the opengl system - palShutdownGL(); - // free the framebuffer configs palFree(nullptr, fbConfigs); diff --git a/tests/opengl_fbconfig_test.c b/tests/opengl_fbconfig_test.c index 8ca0673..ebd14ff 100644 --- a/tests/opengl_fbconfig_test.c +++ b/tests/opengl_fbconfig_test.c @@ -138,6 +138,7 @@ bool openglFBConfigTest() // shutdown the opengl system palShutdownGL(); + // shutdown the video system palShutdownVideo(); // free the framebuffer configs diff --git a/tests/opengl_multi_context_test.c b/tests/opengl_multi_context_test.c index 21a319b..93415ca 100644 --- a/tests/opengl_multi_context_test.c +++ b/tests/opengl_multi_context_test.c @@ -23,22 +23,7 @@ bool openglMultiContextTest() palLog(nullptr, "==========================================="); palLog(nullptr, ""); - // initialize the opengl system - PalResult result = palInitGL(nullptr); - if (result != PAL_RESULT_SUCCESS) { - const char* error = palFormatResult(result); - palLog(nullptr, "Failed to initialize opengl: %s", error); - return false; - } - - PalWindow* window = nullptr; - PalGLContext* context = nullptr; - PalWindowCreateInfo createInfo = {0}; - PalGLContextCreateInfo contextCreateInfo = {0}; - Int32 fbCount = 0; - bool running = false; - - // event driver + PalResult result; PalEventDriver* eventDriver = nullptr; PalEventDriverCreateInfo eventDriverCreateInfo = {0}; @@ -56,6 +41,35 @@ bool openglMultiContextTest() return false; } + // initialize the video system. We pass the event driver to recieve video + // related events the video system does not copy the event driver, it must + // be valid till the video system is shutdown + result = palInitVideo(nullptr, eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + // get the instance or display handle and pass it to the opengl system + // This must be called before the opengl system is initialized + palGLSetInstance(palGetInstance()); + + // initialize the opengl system + result = palInitGL(nullptr); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize opengl: %s", error); + return false; + } + + PalWindow* window = nullptr; + PalGLContext* context = nullptr; + PalWindowCreateInfo createInfo = {0}; + PalGLContextCreateInfo contextCreateInfo = {0}; + Int32 fbCount = 0; + bool running = false; + // enumerate supported opengl framebuffer configs // glWindow must be nullptr result = palEnumerateGLFBConfigs(nullptr, &fbCount, nullptr); @@ -123,16 +137,6 @@ bool openglMultiContextTest() palLog(nullptr, " sRGB: %s", g_BoolsToSting[closest->sRGB]); palLog(nullptr, ""); - // initialize the video system. We pass the event driver to recieve video - // related events the video system does not copy the event driver, it must - // be valid till the video system is shutdown - result = palInitVideo(nullptr, eventDriver); - if (result != PAL_RESULT_SUCCESS) { - const char* error = palFormatResult(result); - palLog(nullptr, "Failed to initialize video: %s", error); - return false; - } - // set the FBConfig that will be used by PAL video system // to create windows. this must be set before creating a window // for this example, we set the closest we desired. @@ -144,16 +148,43 @@ bool openglMultiContextTest() result = palSetFBConfig(closest->index, PAL_CONFIG_BACKEND_PAL_OPENGL); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); - palLog(nullptr, "Failed to set GL pixel format: %s", error); + palLog(nullptr, "Failed to set GL FBConfig: %s", error); return false; } + // if not using pal_opengl with pal_video + // we get the backend string from the opengl system and + // get the backend from it + // Possible values are `wgl`, `glx`, `gles`, `egl`. + // PalFBConfigBackend backend; + // const char* glBackendString = palGLGetBackend(); + // if (strcmp(glBackendString, "wgl") == 0) { + // backend = PAL_CONFIG_BACKEND_WGL; + + // } else if (strcmp(glBackendString, "glx") == 0) { + // backend = PAL_CONFIG_BACKEND_GLX; + + // } else if (strcmp(glBackendString, "gles") == 0) { + // backend = PAL_CONFIG_BACKEND_GLES; + + // } else if (strcmp(glBackendString, "egl") == 0) { + // backend = PAL_CONFIG_BACKEND_EGL; + // } + createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "Pal Opengl Multi Context Window"; + createInfo.title = "Pal Opengl Context Window"; + + // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } // create the window with the create info struct result = palCreateWindow(&createInfo, &window); @@ -172,14 +203,21 @@ bool openglMultiContextTest() // get window handle. You can use any window from any library // so long as you can get the window handle and display (if on X11, wayland) // If pal video system will not be used, there is no need to initialize it - PalWindowHandleInfo windowHandleInfo; - windowHandleInfo = palGetWindowHandleInfo(window); + PalWindowHandleInfoEx winHandle = {0}; + winHandle = palGetWindowHandleInfoEx(window); // PalGLWindow is just a struct to hold native handles PalGLWindow glWindow = {0}; - // needed when using X11 or wayland - glWindow.display = windowHandleInfo.nativeDisplay; - glWindow.window = windowHandleInfo.nativeWindow; + glWindow.display = winHandle.nativeDisplay; + + // On Wayland the window is the wl_egl_window + if (winHandle.nativeHandle3) { + // the window has a valid wl_egl_window + glWindow.window = winHandle.nativeHandle3; + + } else { + glWindow.window = winHandle.nativeWindow; + } // get opengl info const PalGLInfo* info = palGetGLInfo(); @@ -217,7 +255,7 @@ bool openglMultiContextTest() return false; } - // make the context current on this thread + // make the context current and optionally set vsync if supported result = palMakeContextCurrent(&glWindow, context); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); @@ -226,7 +264,6 @@ bool openglMultiContextTest() return false; } - // check if vsync is supported. if (info->extensions & PAL_GL_EXTENSION_SWAP_CONTROL) { // vsync is supported. This is set for the current context palSetSwapInterval(1); @@ -239,7 +276,7 @@ bool openglMultiContextTest() glClear = (PFNGLCLEARPROC)palGLGetProcAddress("glClear"); // set clear color - glClearColor(.2f, .2f, .2f, .2f); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); running = true; while (running) { @@ -273,8 +310,7 @@ bool openglMultiContextTest() palDestroyGLContext(context); // create a new opengl context with the same window - // the window's FBConfig has already be set, so we can skip it or set it. - // Opengl system will ignore it + // the FBConfig of the new context must match the windows context = nullptr; result = palCreateGLContext(&contextCreateInfo, &context); if (result != PAL_RESULT_SUCCESS) { @@ -293,7 +329,7 @@ bool openglMultiContextTest() return false; } - glClearColor(.2f, .6f, .6f, .2f); + glClearColor(.2f, .6f, .6f, 1.0f); running = true; while (running) { palUpdateVideo(); @@ -318,6 +354,12 @@ bool openglMultiContextTest() } } + // destroy the opengl context + palDestroyGLContext(context); + + // shutdown the opengl system + palShutdownGL(); + // destroy the window palDestroyWindow(window); @@ -327,12 +369,6 @@ bool openglMultiContextTest() // destroy the event driver palDestroyEventDriver(eventDriver); - // destroy the opengl context. - palDestroyGLContext(context); - - // shutdown the opengl system - palShutdownGL(); - // free the framebuffer configs palFree(nullptr, fbConfigs); diff --git a/tests/tests_main.c b/tests/tests_main.c index 4216df9..593f3d9 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -46,8 +46,8 @@ int main(int argc, char** argv) // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - // registerTest("Opengl FBConfig Test", openglFBConfigTest); - registerTest("Opengl Context Test", openglContextTest); + registerTest("Opengl FBConfig Test", openglFBConfigTest); + // registerTest("Opengl Context Test", openglContextTest); // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL From 52991f9709e20fb86d127d41800129874194a7b0 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 14:45:53 +0000 Subject: [PATCH 18/51] x11 opengl --- include/pal/pal_thread.h | 7 +++ src/opengl/pal_opengl_linux.c | 54 +++++++++++------- src/thread/pal_thread_linux.c | 6 +- src/thread/pal_thread_win32.c | 5 ++ src/video/pal_video_linux.c | 96 ++++++++++++-------------------- src/video/pal_video_win32.c | 1 + tests/condvar_test.c | 1 + tests/multi_thread_opengl_test.c | 14 +++-- tests/mutex_test.c | 5 +- tests/opengl_test.c | 18 +++++- tests/tests.lua | 7 +-- tests/tests_main.c | 9 +-- tests/thread_test.c | 8 +-- tests/tls_test.c | 4 +- tests/video_test.c | 10 +++- 15 files changed, 130 insertions(+), 115 deletions(-) diff --git a/include/pal/pal_thread.h b/include/pal/pal_thread.h index 40000a7..ed4d5c8 100644 --- a/include/pal/pal_thread.h +++ b/include/pal/pal_thread.h @@ -178,9 +178,14 @@ PAL_API PalResult PAL_CALL palCreateThread( /** * @brief Wait for the provided thread to finish executing. + * + * After the thread is done executing, it is freed automatically and + * must not be used anymore nor detached. * * @param[in] thread Pointer to the thread. * @param[out] retval Optionally pointer to get the threads exit value. + * Pass the address of the pointer. Internally it will be reinterpreted into a + * pointer-to-pointer. This is for ABI stability. * * @return `PAL_RESULT_SUCCESS` on success or a result code on * failure. Call palFormatResult() for more information. @@ -200,6 +205,8 @@ PAL_API PalResult PAL_CALL palJoinThread( * This function must be called when the thread is done executing. * After this call, the thread cannot be attached or used anymore. * If the thread is invalid or nullptr, this function returns silently. + * + * This must not be called on a thread that has been attached. * * @param[in] thread Pointer to the thread to detach. * diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index f46673c..efaf545 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -245,7 +245,7 @@ typedef struct { void* handle; void* platformDisplay; ContextData* contextData; - EGLDisplay* display; + EGLDisplay display; PalGLInfo info; } GLLinux; @@ -463,6 +463,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } EGLDisplay display = s_GL.eglGetDisplay(s_GL.platformDisplay); + EGLDisplay * tmpDisplay = EGL_NO_DISPLAY; if (display == EGL_NO_DISPLAY) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -482,22 +483,29 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGL_PBUFFER_BIT, EGL_NONE}; - // get a default display and create a dummy context with it - EGLDisplay defaultDis = s_GL.eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (display == EGL_NO_DISPLAY) { - return PAL_RESULT_PLATFORM_FAILURE; - } + s_GL.eglChooseConfig(display, attribs, &config, 1, &numConfigs); + if (!config || numConfigs == 0) { + // API bind type might not support puffer + // create a default display and use that + tmpDisplay = s_GL.eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + return PAL_RESULT_PLATFORM_FAILURE; + } - if (!s_GL.eglInitialize(defaultDis, nullptr, nullptr)) { - return PAL_RESULT_PLATFORM_FAILURE; + if (!s_GL.eglInitialize(tmpDisplay, nullptr, nullptr)) { + return PAL_RESULT_PLATFORM_FAILURE; + } + } else { + // set the tmp display to our display + tmpDisplay = display; } - s_GL.eglChooseConfig(defaultDis, attribs, &config, 1, &numConfigs); + s_GL.eglChooseConfig(tmpDisplay, attribs, &config, 1, &numConfigs); if (!config) { return PAL_RESULT_PLATFORM_FAILURE; } - s_GL.eglGetConfigAttrib(defaultDis, config, EGL_RENDERABLE_TYPE, &type); + s_GL.eglGetConfigAttrib(tmpDisplay, config, EGL_RENDERABLE_TYPE, &type); if (!(type & s_GL.apiTypeBit)) { // we must support the required API return PAL_RESULT_PLATFORM_FAILURE; @@ -519,7 +527,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGL_NONE}; surface = s_GL.eglCreatePbufferSurface( - defaultDis, + tmpDisplay, config, pBufferAttribs); @@ -536,7 +544,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGL_NONE}; context = s_GL.eglCreateContext( - defaultDis, + tmpDisplay, config, EGL_NO_CONTEXT, contextAttrib); @@ -547,7 +555,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) EGL_NONE}; context = s_GL.eglCreateContext( - defaultDis, + tmpDisplay, config, EGL_NO_CONTEXT, contextAttrib); @@ -557,7 +565,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) return PAL_RESULT_PLATFORM_FAILURE; } - s_GL.eglMakeCurrent(defaultDis, surface, surface, context); + s_GL.eglMakeCurrent(tmpDisplay, surface, surface, context); s_GL.glGetString = (glGetStringFn)s_GL.eglGetProcAddress("glGetString"); const char* version = (const char*)s_GL.glGetString(GL_VERSION); @@ -577,7 +585,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) // EGL extensions can be queried without a bound context // we just do that over here after making the context current - const char* extensions = s_GL.eglQueryString(defaultDis, EGL_EXTENSIONS); + const char* extensions = s_GL.eglQueryString(tmpDisplay, EGL_EXTENSIONS); if (extensions) { // color space if (checkExtension("EGL_KHR_gl_colorspace", extensions)) { @@ -633,27 +641,31 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.info.extensions &= ~PAL_GL_EXTENSION_CONTEXT_PROFILE; s_GL.info.extensions &= ~PAL_GL_EXTENSION_CONTEXT_PROFILE_ES2; } - } else { - // check to see if we support EGL_OPENGL_ES3_BIT + if (type & EGL_OPENGL_ES3_BIT) { s_GL.apiTypeBit = EGL_OPENGL_ES3_BIT; } } s_GL.eglMakeCurrent( - defaultDis, + tmpDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); // clang-format on - s_GL.eglDestroyContext(defaultDis, context); - s_GL.eglDestroySurface(defaultDis, surface); + s_GL.eglDestroyContext(tmpDisplay, context); + s_GL.eglDestroySurface(tmpDisplay, surface); s_GL.allocator = allocator; s_GL.initialized = true; - s_GL.eglTerminate(defaultDis); + + if (tmpDisplay != display) { + // tmpDisplay is a seperate display + // terminate it + s_GL.eglTerminate(tmpDisplay); + } s_GL.display = display; return PAL_RESULT_SUCCESS; diff --git a/src/thread/pal_thread_linux.c b/src/thread/pal_thread_linux.c index c3e5fbe..5909a07 100644 --- a/src/thread/pal_thread_linux.c +++ b/src/thread/pal_thread_linux.c @@ -109,9 +109,13 @@ PalResult PAL_CALL palJoinThread( } int ret = 0; + void* value = nullptr; pthread_t _thread = FROM_PAL_HANDLE(pthread_t, thread); if (retval) { - ret = pthread_join(_thread, &retval); + ret = pthread_join(_thread, &value); + void** out = (void**)retval; + *out = value; + } else { ret = pthread_join(_thread, nullptr); } diff --git a/src/thread/pal_thread_win32.c b/src/thread/pal_thread_win32.c index 53de896..ca3fcbc 100644 --- a/src/thread/pal_thread_win32.c +++ b/src/thread/pal_thread_win32.c @@ -157,6 +157,11 @@ PalResult PAL_CALL palJoinThread( GetExitCodeThread((HANDLE)thread, (LPDWORD)&ret); retval = (void*)ret; + // thread is done + // destroy the HANDLE + // TODO: + CloseHandle((HANDLE)thread); + } else if (wait == WAIT_FAILED) { return PAL_RESULT_INVALID_THREAD; } diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 8da0479..bb8dbd9 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -140,6 +140,7 @@ typedef struct { // X11 only XIC ic; + Colormap colormap; // Wayland only void* xdgSurface; @@ -731,9 +732,7 @@ typedef struct { Display* display; Window root; XContext dataID; - - Visual* visual; - Colormap colormap; + XVisualInfo* visualInfo; XOpenDisplayFn openDisplay; XCloseDisplayFn closeDisplay; @@ -2963,13 +2962,6 @@ static PalResult glxBackend() } // clang-format off - // check if we already have a colormap - // if so we delete it and create a new one - if (s_X11.colormap) { - s_X11.freeColormap(s_X11.display, s_X11.colormap); - s_X11.colormap = None; - } - int count = 0; GLXFBConfig* configs = s_X11.glxGetFBConfigs( s_X11.display, @@ -2990,20 +2982,7 @@ static PalResult glxBackend() return PAL_RESULT_INVALID_GL_FBCONFIG; } - // clang-format on - - s_X11.visual = visualInfo->visual; - s_X11.depth = visualInfo->depth; - s_X11.colormap = s_X11.createColormap( - s_X11.display, - s_X11.root, - visualInfo->visual, - AllocNone); - - if (!s_X11.colormap) { - return PAL_RESULT_INVALID_GL_FBCONFIG; - } - + s_X11.visualInfo = visualInfo; return PAL_RESULT_SUCCESS; } @@ -3014,17 +2993,6 @@ static PalResult eglXBackend(int index) return PAL_RESULT_PLATFORM_FAILURE; } - if (!s_Egl.eglBindAPI(EGL_OPENGL_API)) { - return PAL_RESULT_PLATFORM_FAILURE; - } - - // check if we already have a colormap - // if so we delete it and create a new one - if (s_X11.colormap) { - s_X11.freeColormap(s_X11.display, s_X11.colormap); - s_X11.colormap = None; - } - EGLDisplay display = EGL_NO_DISPLAY; display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_X11.display); @@ -3050,7 +3018,7 @@ static PalResult eglXBackend(int index) s_Egl.eglGetConfigs(display, eglConfigs, numConfigs, &numConfigs); EGLConfig config = eglConfigs[index]; - // we create a visual from the config + // we get a visual info from the config EGLint visualID; s_Egl.eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &visualID); if (visualID == 0) { @@ -3074,19 +3042,9 @@ static PalResult eglXBackend(int index) return PAL_RESULT_INVALID_GL_FBCONFIG; } - s_X11.visual = visualInfo->visual; - s_X11.depth = visualInfo->depth; - s_X11.colormap = s_X11.createColormap( - s_X11.display, - s_X11.root, - visualInfo->visual, - AllocNone); - - if (!s_X11.colormap) { - return PAL_RESULT_INVALID_GL_FBCONFIG; - } + s_X11.visualInfo = visualInfo; + return PAL_RESULT_SUCCESS; - s_Egl.eglTerminate(display); palFree(s_Video.allocator, eglConfigs); return PAL_RESULT_SUCCESS; } @@ -3231,6 +3189,7 @@ static void xCheckFeatures() features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_TITLE; features64 |= PAL_VIDEO_FEATURE64_WINDOW_FLASH_TRAY; + features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_ICON; features64 |= PAL_VIDEO_FEATURE64_TOPMOST_WINDOW; features64 |= PAL_VIDEO_FEATURE64_DECORATED_WINDOW; features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_PRIMARY; @@ -3835,10 +3794,6 @@ static PalResult xInitVideo() static void xShutdownVideo() { - if (s_X11.colormap) { - s_X11.freeColormap(s_X11.display, s_X11.colormap); - } - s_X11.closeIM(s_X11.im); s_X11.closeDisplay(s_X11.display); dlclose(s_X11.handle); @@ -4784,13 +4739,24 @@ static PalResult xCreateWindow( unsigned long bgPixel = 0; unsigned long borderPixel = 0; - if (s_X11.colormap) { - visual = s_X11.visual; - depth = s_X11.depth; - colormap = s_X11.colormap; + if (s_X11.visualInfo) { + visual = s_X11.visualInfo->visual; + depth = s_X11.visualInfo->depth; bgPixel = 0; borderPixel = 0; + colormap = s_X11.createColormap( + s_X11.display, + s_X11.root, + visual, + AllocNone); + + if (!colormap) { + return PAL_RESULT_PLATFORM_FAILURE; + } + + data->colormap = colormap; + } else { // use a default visual visual = DefaultVisual(s_X11.display, s_X11.screen); @@ -4798,6 +4764,7 @@ static PalResult xCreateWindow( bgPixel = WhitePixel(s_X11.display, s_X11.screen); borderPixel = BlackPixel(s_X11.display, s_X11.screen); colormap = DefaultColormap(s_X11.display, s_X11.screen); + data->colormap = None; } // get monitor @@ -5100,8 +5067,11 @@ static PalResult xCreateWindow( s_X11.iconifyWindow(s_X11.display, window, s_X11.screen); } - s_X11 - .setWMProtocols(s_X11.display, window, &s_X11Atoms.WM_DELETE_WINDOW, 1); + s_X11.setWMProtocols( + s_X11.display, + window, + &s_X11Atoms.WM_DELETE_WINDOW, + 1); s_X11.flush(s_X11.display); @@ -5145,6 +5115,11 @@ static void xDestroyWindow(PalWindow* window) s_X11.destroyIC(data->ic); s_X11.destroyWindow(s_X11.display, xWin); + + if (data->colormap != None) { + s_X11.freeColormap(s_X11.display, data->colormap); + } + data->used = false; } @@ -7627,7 +7602,7 @@ PalResult PAL_CALL palSetFBConfig( return PAL_RESULT_VIDEO_NOT_INITIALIZED; } - // X11 can used GLX and EGL + // X11 and wayland can used GLX and EGL if (backend == PAL_CONFIG_BACKEND_WGL) { return PAL_RESULT_INVALID_FBCONFIG_BACKEND; } @@ -7637,7 +7612,8 @@ PalResult PAL_CALL palSetFBConfig( return glxBackend(); } else if ( - backend == PAL_CONFIG_BACKEND_EGL || + backend == PAL_CONFIG_BACKEND_EGL || + backend == PAL_CONFIG_BACKEND_GLES || backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { if (s_X11.display) { return eglXBackend(index); diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index b6d9e9d..ab0d572 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -1312,6 +1312,7 @@ PalResult PAL_CALL palSetFBConfig( } if (backend == PAL_CONFIG_BACKEND_EGL || + backend == PAL_CONFIG_BACKEND_GLES || backend == PAL_CONFIG_BACKEND_GLX) { return PAL_RESULT_INVALID_FBCONFIG_BACKEND; } diff --git a/tests/condvar_test.c b/tests/condvar_test.c index c22e356..266b707 100644 --- a/tests/condvar_test.c +++ b/tests/condvar_test.c @@ -111,6 +111,7 @@ bool condvarTest() palUnlockMutex(g_Mutex); // wait for the remaining threads + // joint threads does not need to be detached for (Int32 i = 0; i < THREAD_COUNT; i++) { palJoinThread(threads[i], nullptr); palLog(nullptr, "Thread %d finished successfully", data[i].id); diff --git a/tests/multi_thread_opengl_test.c b/tests/multi_thread_opengl_test.c index 16e4fe1..35b29b7 100644 --- a/tests/multi_thread_opengl_test.c +++ b/tests/multi_thread_opengl_test.c @@ -179,7 +179,7 @@ static void* PAL_CALL rendererWorkder(void* arg) result = palSwapBuffers(&shared->window, shared->context); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); - palLog(nullptr, "Failed to swap buffers: %s", error); + palLog(nullptr, "Failed to swap buffers from: %s", error); return nullptr; } } @@ -219,9 +219,12 @@ bool multiThreadOpenGlTest() // check to see if the event driver thread is done creating the drivers // if not we wait for it if (!shared->driverCreated) { + // this will be detached automatically when done palJoinThread(eventDriverThread, nullptr); + + } else { + palDetachThread(eventDriverThread); // we dont need it anymore } - palDetachThread(eventDriverThread); // we dont need it anymore // initialize the video system. We pass the event driver to recieve video // related events the video system does not copy the event driver, it must @@ -428,6 +431,10 @@ bool multiThreadOpenGlTest() } } + // we wait for the render thread to finish with + // the current frame and destroy the context + palJoinThread(rendererThread, nullptr); + palDestroyGLContext(shared->context); palShutdownGL(); @@ -442,8 +449,5 @@ bool multiThreadOpenGlTest() palDestroyEventDriver(shared->openglEventDriver); palFree(nullptr, fbConfigs); - // destroy the renderer thread - palDetachThread(rendererThread); - return true; } \ No newline at end of file diff --git a/tests/mutex_test.c b/tests/mutex_test.c index 1b7b955..048d8aa 100644 --- a/tests/mutex_test.c +++ b/tests/mutex_test.c @@ -72,14 +72,11 @@ bool mutexTest() } // join the threads to main thread + // joint threads does not need to be detached for (Int32 i = 0; i < THREAD_COUNT; i++) { palJoinThread(threads[i], nullptr); } - for (Int32 i = 0; i < THREAD_COUNT; i++) { - palDetachThread(threads[i]); - } - palDestroyMutex(data->mutex); palLog(nullptr, "Expected Counter: %d", MAX_COUNTER * THREAD_COUNT); diff --git a/tests/opengl_test.c b/tests/opengl_test.c index 4f4f5b6..a61d7f0 100644 --- a/tests/opengl_test.c +++ b/tests/opengl_test.c @@ -1,5 +1,6 @@ #include "pal/pal_opengl.h" +#include "pal/pal_video.h" #include "tests.h" bool openglTest() @@ -10,8 +11,20 @@ bool openglTest() palLog(nullptr, "==========================================="); palLog(nullptr, ""); + // initialize the video system and create a window + PalResult result = palInitVideo(nullptr, nullptr); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + // get the instance or display handle and pass it to the opengl system + // This must be called before the opengl system is initialized + palGLSetInstance(palGetInstance()); + // initialize the opengl system. This loads the icd. - PalResult result = palInitGL(nullptr); + result = palInitGL(nullptr); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); palLog(nullptr, "Failed to initialize opengl: %s", error); @@ -73,5 +86,8 @@ bool openglTest() // shutdown the opengl system palShutdownGL(); + // shutdown the video system + palShutdownVideo(); + return true; } \ No newline at end of file diff --git a/tests/tests.lua b/tests/tests.lua index 547fc40..f216334 100644 --- a/tests/tests.lua +++ b/tests/tests.lua @@ -46,14 +46,9 @@ project "tests" } end - if (PAL_BUILD_OPENGL) then - files { - "opengl_test.c" - } - end - if (PAL_BUILD_OPENGL and PAL_BUILD_VIDEO) then files { + "opengl_test.c", "opengl_fbconfig_test.c", "opengl_context_test.c", "opengl_multi_context_test.c" diff --git a/tests/tests_main.c b/tests/tests_main.c index 593f3d9..be6a7db 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -39,20 +39,17 @@ int main(int argc, char** argv) // registerTest("Native Integration Test", nativeIntegrationTest); #endif // PAL_HAS_VIDEO -#if PAL_HAS_OPENGL - // registerTest("Opengl Test", openglTest); -#endif // PAL_HAS_OPENGL - // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - registerTest("Opengl FBConfig Test", openglFBConfigTest); + // registerTest("Opengl Test", openglTest); + // registerTest("Opengl FBConfig Test", openglFBConfigTest); // registerTest("Opengl Context Test", openglContextTest); // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); diff --git a/tests/thread_test.c b/tests/thread_test.c index f016c8f..16f0bd9 100644 --- a/tests/thread_test.c +++ b/tests/thread_test.c @@ -47,6 +47,7 @@ bool threadTest() // join threads for (Int32 i = 0; i < THREAD_COUNT; i++) { // we dont need the return value + // joint threads does not need to be detached result = palJoinThread(threads[i], nullptr); if (result != PAL_RESULT_SUCCESS) { const char* error = palFormatResult(result); @@ -56,12 +57,5 @@ bool threadTest() } palLog(nullptr, "All threads finished successfully"); - - // detach threads - for (Int32 i = 0; i < THREAD_COUNT; i++) { - palDetachThread(threads[i]); - palLog(nullptr, "Thread %d: detached", i + 1); - } - return true; } \ No newline at end of file diff --git a/tests/tls_test.c b/tests/tls_test.c index fcc0e52..fcbd3e5 100644 --- a/tests/tls_test.c +++ b/tests/tls_test.c @@ -89,11 +89,9 @@ bool tlsTest() } // join thread + // joint threads does not need to be detached palJoinThread(thread, nullptr); // wait for thread to finish - // detach thread - palDetachThread(thread); - // destroy the tls palDestroyTLS(tlsID); diff --git a/tests/video_test.c b/tests/video_test.c index 8535e13..c018c74 100644 --- a/tests/video_test.c +++ b/tests/video_test.c @@ -147,6 +147,10 @@ bool videoTest() palLog(nullptr, " Getting cursor position"); } + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_ICON) { + palLog(nullptr, " Setting window icon"); + } + if (features & PAL_VIDEO_FEATURE64_TOPMOST_WINDOW) { palLog(nullptr, " Topmost windows"); } @@ -171,10 +175,14 @@ bool videoTest() palLog(nullptr, " Attaching and detaching foreign windows"); } - if (features & PAL_VIDEO_FEATURE64_MONITOR_VALIDATE_MODE ) { + if (features & PAL_VIDEO_FEATURE64_MONITOR_VALIDATE_MODE) { palLog(nullptr, " Validate monitor display mode"); } + if (features & PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR) { + palLog(nullptr, " Setting window cursor"); + } + // shutdown the video system palShutdownVideo(); From 05329b0962e30ad41a8d67b7de36fdb32a0add34 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 15:42:57 +0000 Subject: [PATCH 19/51] native instance test x11 --- include/pal/pal_opengl.h | 4 +- include/pal/pal_video.h | 22 +++ src/video/pal_video_linux.c | 38 ++++- tests/native_instance_test.c | 255 ++++++++++++++++++++++++++++++++ tests/native_integration_test.c | 4 +- tests/tests.h | 1 + tests/tests.lua | 3 +- tests/tests_main.c | 3 +- 8 files changed, 320 insertions(+), 10 deletions(-) create mode 100644 tests/native_instance_test.c diff --git a/include/pal/pal_opengl.h b/include/pal/pal_opengl.h index d43011d..d1bb961 100644 --- a/include/pal/pal_opengl.h +++ b/include/pal/pal_opengl.h @@ -460,9 +460,9 @@ PAL_API PalResult PAL_CALL palSetSwapInterval(Int32 interval); * On Windows: This is the HINSTANCE of the process. * - * On Wayland: This is the Display associated with the connection. - * * Thread safety: This function is thread safe. + * + * @note The provided instance will not be freed by the opengl system. * * @since 1.3 * @ingroup pal_opengl diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index 439a193..b0c8f89 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -709,6 +709,7 @@ typedef struct { * @since 1.0 * @ingroup pal_video * @sa palShutdownVideo + * @sa palSetPreferredInstance */ PAL_API PalResult PAL_CALL palInitVideo( const PalAllocator* allocator, @@ -1999,6 +2000,27 @@ PAL_API PalResult PAL_CALL palDetachWindow( PalWindow* window, void** outWindowHandle); +/** + * @brief Set the preferred instance the video system should use. + * + * This function must be called before palInitVideo(). This will be ignored + * if the video system is already initialized. + * If there is no preferred instance set, the video system creates one. + * + * On Linux: This is the Display associated with the connection. + + * On Windows: This is the HINSTANCE of the process. + * + * Thread safety: This function must be called from the main thread. + * + * @note The provided instance will not be freed by the video system. + * + * @since 1.3 + * @ingroup pal_video + * @sa palInitVideo + */ +PAL_API void PAL_CALL palSetPreferredInstance(void* instance); + /** @} */ // end of pal_video group #endif // _PAL_VIDEO_H \ No newline at end of file diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index bb8dbd9..56ddcd6 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -272,6 +272,7 @@ typedef struct { WindowData* windowData; MonitorData* monitorData; const char* className; + void* platformInstance; } VideoLinux; static VideoLinux s_Video = {0}; @@ -3725,6 +3726,14 @@ static PalResult xInitVideo() "Xutf8LookupString"); // X11 server + if (s_Video.platformInstance) { + s_X11.display = (Display*)s_Video.platformInstance; + + } else { + s_X11.display = s_X11.openDisplay(nullptr); + s_Video.platformInstance = nullptr; + } + s_X11.display = s_X11.openDisplay(nullptr); if (!s_X11.display) { return PAL_RESULT_PLATFORM_FAILURE; @@ -3795,7 +3804,11 @@ static PalResult xInitVideo() static void xShutdownVideo() { s_X11.closeIM(s_X11.im); - s_X11.closeDisplay(s_X11.display); + if (!s_Video.platformInstance) { + // opened by PAL + s_X11.closeDisplay(s_X11.display); + } + dlclose(s_X11.handle); dlclose(s_X11.xrandr); dlclose(s_X11.libCursor); @@ -6609,7 +6622,15 @@ PalResult wlInitVideo() setupXdgShellProtocol(); setupZwpPointerProtocol(); - s_Wl.display = s_Wl.displayConnect(nullptr); + // check if user supplied their own display + if (s_Video.platformInstance) { + s_Wl.display = (struct wl_display*)s_Video.platformInstance; + + } else { + s_Wl.display = s_Wl.displayConnect(nullptr); + s_Video.platformInstance = nullptr; + } + s_Wl.registry = wlDisplayGetRegistry(s_Wl.display); wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); s_Wl.displayRoundtrip(s_Wl.display); @@ -6653,7 +6674,11 @@ void wlShutdownVideo() xdgWmBaseDestroy(s_Wl.xdgBase); s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.compositor); s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.registry); - s_Wl.displayDisconnect(s_Wl.display); + + if (!s_Video.platformInstance) { + // opened by PAL + s_Wl.displayDisconnect(s_Wl.display); + } dlclose(s_Wl.libCursor); dlclose(s_Wl.xkbCommon); @@ -8382,4 +8407,11 @@ PalResult PAL_CALL palDetachWindow( } return s_Video.backend->detachWindow(window, outWindowHandle); +} + +void PAL_CALL palSetPreferredInstance(void* instance) +{ + if (!s_Video.initialized && instance) { + s_Video.platformInstance = instance; + } } \ No newline at end of file diff --git a/tests/native_instance_test.c b/tests/native_instance_test.c new file mode 100644 index 0000000..953aae6 --- /dev/null +++ b/tests/native_instance_test.c @@ -0,0 +1,255 @@ + +#include "pal/pal_video.h" +#include "tests.h" + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN + +#ifndef NOMINMAX +#define NOMINMAX +#endif // NOMINMAX + +// set unicode +#ifndef UNICODE +#define UNICODE +#endif // UNICODE + +#elif defined(__linux__) +#include +#include +#include + +typedef Display* (*XOpenDisplayFn)(const char*); +typedef int (*XCloseDisplayFn)(Display*); + +static void* s_LibX; +static XOpenDisplayFn s_XOpenDisplay; +static XCloseDisplayFn s_XCloseDisplay; + +struct wl_display; +typedef struct wl_display* (*wl_display_connect_fn)(const char*); +typedef void (*wl_display_disconnect_fn)(struct wl_display*); + +static void* s_LibWayland; +static wl_display_connect_fn s_wl_display_connect; +static wl_display_disconnect_fn s_wl_display_disconnect; + +static bool s_OnWayland = false; + +#endif // _WIN32 + +void* openDisplayX11() +{ +#ifdef __linux__ + // load the procs + s_LibX = dlopen("libX11.so", RTLD_LAZY); + if (!s_LibX) { + return nullptr; + } + + // clang-format off + s_XOpenDisplay = (XOpenDisplayFn)dlsym( + s_LibX, + "XOpenDisplay"); + + s_XCloseDisplay = (XCloseDisplayFn)dlsym( + s_LibX, + "XCloseDisplay"); + + return s_XOpenDisplay(nullptr); +#endif // __linux__ +} + +void closeDisplayX11(void* instance) +{ +#ifdef __linux__ + s_XCloseDisplay((Display*)instance); + dlclose(s_LibX); +#endif // __linux__ +} + +void* openDisplayWayland() +{ +#ifdef __linux__ + // load the procs + s_LibWayland = dlopen("libwayland-client.so.0", RTLD_LAZY); + if (!s_LibWayland) { + return nullptr; + } + + // clang-format off + s_wl_display_connect = (wl_display_connect_fn)dlsym( + s_LibWayland, + "wl_display_connect"); + + s_wl_display_disconnect = (wl_display_disconnect_fn)dlsym( + s_LibWayland, + "wl_display_disconnect"); + + return s_wl_display_connect(nullptr); +#endif // __linux__ +} + +void closeDisplayWayland(void* instance) +{ +#ifdef __linux__ + s_wl_display_disconnect((struct wl_display*)instance); + dlclose(s_LibWayland); +#endif // __linux__ +} + +void* openInstance() +{ +#ifdef _WIN32 +#elif defined(__linux__) + // get the active session + const char* session = getenv("XDG_SESSION_TYPE"); + if (session) { + if (strcmp(session, "wayland") == 0) { + s_OnWayland = true; + } else { + s_OnWayland = false; + } + } + + if (s_OnWayland) { + return openDisplayWayland(); + } else { + return openDisplayX11(); + } +#endif // _WIN32 +} + +void closeInstance(void* instance) +{ +#ifdef _WIN32 +#elif defined(__linux__) + if (s_OnWayland) { + closeDisplayWayland(instance); + } else { + closeDisplayX11(instance); + } +#endif // _WIN32 +} + +bool nativeInstanceTest() +{ + palLog(nullptr, ""); + palLog(nullptr, "==========================================="); + palLog(nullptr, "Native Instance Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); + palLog(nullptr, "==========================================="); + palLog(nullptr, ""); + + PalResult result; + + // event driver + PalEventDriver* eventDriver = nullptr; + PalEventDriverCreateInfo eventDriverCreateInfo = {0}; + + // create the event driver + result = palCreateEventDriver(&eventDriverCreateInfo, &eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to create event driver: %s", error); + return false; + } + + // open our own display or instance + void* instance = openInstance(); + if (!instance) { + palLog(nullptr, "Failed to open instance"); + return false; + } + + // tell the video system to use out instance rather + // than creating a new one + // this can be set to the opengl system as well + palSetPreferredInstance(instance); + + // initialize the video system. We pass the event driver to recieve video + // related events the video system does not copy the event driver, it must + // be valid till the video system is shutdown + result = palInitVideo(nullptr, eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + PalWindow* window = nullptr; + PalWindowCreateInfo createInfo = {0}; + createInfo.monitor = nullptr; // use default monitor + createInfo.height = 480; + createInfo.width = 640; + createInfo.show = true; + createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; + createInfo.title = "Native Integration Test"; + + // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); + if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { + // if we dont support, we need to create a borderless window + // and create the decorations ourselves + createInfo.style |= PAL_WINDOW_STYLE_BORDERLESS; + } + + result = palCreateWindow(&createInfo, &window); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to create window: %s", error); + return false; + } + + // we set window close to poll + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_CLOSE, + PAL_DISPATCH_POLL); + + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + + bool running = true; + while (running) { + // update the video system to push video events + palUpdateVideo(); + + PalEvent event; + while (palPollEvent(eventDriver, &event)) { + switch (event.type) { + case PAL_EVENT_WINDOW_CLOSE: { + running = false; + break; + } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } + } + } + } + + // destroy the window + palDestroyWindow(window); + + // shutdown the video system + palShutdownVideo(); + + // destroy the event driver + palDestroyEventDriver(eventDriver); + + // the video system does not destroy or close the handle provided + closeInstance(instance); + + return true; +} \ No newline at end of file diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 688bd9d..7fe0903 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -341,9 +341,6 @@ bool nativeIntegrationTest() return false; } - // check for support - PalVideoFeatures64 features = palGetVideoFeaturesEx(); - PalWindow* window = nullptr; PalWindowCreateInfo createInfo = {0}; createInfo.monitor = nullptr; // use default monitor @@ -354,6 +351,7 @@ bool nativeIntegrationTest() createInfo.title = "Native Integration Test"; // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { // if we dont support, we need to create a borderless window // and create the decorations ourselves diff --git a/tests/tests.h b/tests/tests.h index 3711336..1b7e226 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -39,6 +39,7 @@ bool systemCursorTest(); bool attachWindowTest(); bool charEventTest(); bool nativeIntegrationTest(); +bool nativeInstanceTest(); // opengl test bool openglTest(); diff --git a/tests/tests.lua b/tests/tests.lua index f216334..2cd994a 100644 --- a/tests/tests.lua +++ b/tests/tests.lua @@ -42,7 +42,8 @@ project "tests" "system_cursor_test.c", "attach_window_test.c", "char_event_test.c", - "native_integration_test.c" + "native_integration_test.c", + "native_instance_test.c" } end diff --git a/tests/tests_main.c b/tests/tests_main.c index be6a7db..e0deb04 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -37,6 +37,7 @@ int main(int argc, char** argv) // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); + registerTest("Native Instance Test", nativeInstanceTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid @@ -49,7 +50,7 @@ int main(int argc, char** argv) #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); From e9cd4964ecdd8fe9b422aacb79996c1cf1991f6c Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 16:37:59 +0000 Subject: [PATCH 20/51] start monitor hotpluggin --- CHANGELOG.md | 19 ++++++++++++++++--- src/video/pal_video_linux.c | 13 ++++++++++++- tests/char_event_test.c | 2 +- tests/cursor_test.c | 2 +- tests/icon_test.c | 2 +- tests/input_window_test.c | 2 +- tests/multi_thread_opengl_test.c | 15 +++++++++++++++ tests/native_instance_test.c | 2 +- tests/opengl_context_test.c | 17 ++++++++++++++++- tests/opengl_multi_context_test.c | 26 +++++++++++++++++++++++++- tests/system_cursor_test.c | 2 +- tests/tests_main.c | 2 +- tests/window_test.c | 4 ++-- 13 files changed, 93 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2556ef3..dea7552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,14 +78,27 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. - **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. - **Video:** Added **PAL_CONFIG_BACKEND_GLES** to `PalFBConfigBackend` enum. +- **Video:** Added **palSetPreferredInstance()** to use native instance or display wth PAL video. - **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. - **OpenGL:** Added **palGLSetInstance()** to set the instance or display handle. - **OpenGL:** Added **palGLGetBackend()** to get the opengl backend. ### Tests -- Added native integration example: demonstrating **Native API Integration with PAL API**. see **native_integration_test.c**. +- Added native integration example: demonstrating **Native API Integration with PAL API**. see +**native_integration_test.c**. + +- Added native instance example: demonstrating +**Native Instance or Display Integration with PAL API**. +see **native_instance_test.c**. ### Notes -- No API or ABI changes - existing code remains compatible. -- Safe upgrade from **v1.2.0** - just rebuild your project after updating. \ No newline at end of file +- **No ABI changes** - existing code remains compatible. + +- **Old tests may fail** - some tests written against old version need to +be updated to follow the new initialization order. +This is a runtime behavior change. The tests are updated in the repo. + +- **palJoinThread()** - ABI remains unchanged but now takes the address +of a pointer variable for the return value of the thread. +PAL internally reinterpreted into a pointer-to-pointer. This is for ABI stability. \ No newline at end of file diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 56ddcd6..2b94303 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1736,6 +1736,7 @@ static void keyboardHandleKey( PalScancode scancode = 0; PalKeycode keycode = 0; + bool validCodepoint = true; bool pressed = (state == WL_KEYBOARD_KEY_STATE_PRESSED); PalEventType type = PAL_EVENT_KEYUP; PalDispatchMode mode = PAL_DISPATCH_NONE; @@ -1769,12 +1770,14 @@ static void keyboardHandleKey( // we can make a direct cast without a table // Examle: PAL_KEYCODE_A(int 0) == PAL_SCANCODE_A(int 0) keycode = (PalKeycode)(Uint32)scancode; + validCodepoint = false; } // If we got a keySym but its not mapped into our keycode array // we do a direct cast as well if (keycode == PAL_KEYCODE_UNKNOWN) { keycode = (PalKeycode)(Uint32)scancode; + validCodepoint = false; } s_Keyboard.scancodeState[scancode] = pressed; @@ -1814,7 +1817,7 @@ static void keyboardHandleKey( // check for char event if enabled type = PAL_EVENT_KEYCHAR; mode = palGetEventDispatchMode(driver, type); - if (mode == PAL_DISPATCH_NONE) { + if (mode == PAL_DISPATCH_NONE || validCodepoint == false) { return; } @@ -3816,6 +3819,8 @@ static void xShutdownVideo() if (s_X11.glxHandle) { dlclose(s_X11.glxHandle); } + memset(&s_X11, 0, sizeof(X11)); + memset(&s_X11Atoms, 0, sizeof(X11Atoms)); } static void xUpdateVideo() @@ -6684,6 +6689,7 @@ void wlShutdownVideo() dlclose(s_Wl.xkbCommon); dlclose(s_Wl.libWaylandEgl); dlclose(s_Wl.handle); + memset(&s_Wl, 0, sizeof(Wayland)); } void wlUpdateVideo() @@ -6928,6 +6934,9 @@ PalResult wlCreateWindow( return PAL_RESULT_OUT_OF_MEMORY; } + memset(data, 0, sizeof(WindowData)); + data->used = true; + // create surface surface = wlCompositorCreateSurface(s_Wl.compositor); if (!surface) { @@ -7590,6 +7599,8 @@ void PAL_CALL palShutdownVideo() if (s_Egl.handle) { dlclose(s_Egl.handle); } + + s_Video.platformInstance = nullptr; s_Video.initialized = false; } } diff --git a/tests/char_event_test.c b/tests/char_event_test.c index 7719fea..c405987 100644 --- a/tests/char_event_test.c +++ b/tests/char_event_test.c @@ -42,7 +42,7 @@ bool charEventTest() createInfo.height = 480; createInfo.width = 640; createInfo.show = true; - createInfo.title = "PAL Character Window"; + createInfo.title = "Character Window"; // check if we support decorated windows (title bar, close etc) PalVideoFeatures64 features = palGetVideoFeaturesEx(); diff --git a/tests/cursor_test.c b/tests/cursor_test.c index 6781cfe..1560334 100644 --- a/tests/cursor_test.c +++ b/tests/cursor_test.c @@ -97,7 +97,7 @@ bool cursorTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "PAL cursor Window"; + createInfo.title = "Cursor Window"; // check if we support decorated windows (title bar, close etc) if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { diff --git a/tests/icon_test.c b/tests/icon_test.c index ba32679..390c20a 100644 --- a/tests/icon_test.c +++ b/tests/icon_test.c @@ -95,7 +95,7 @@ bool iconTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "PAL Icon Window"; + createInfo.title = "Icon Window"; // create the window with the create info struct result = palCreateWindow(&createInfo, &window); diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 05ab296..532b553 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -459,7 +459,7 @@ bool inputWindowTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "PAL Input Window"; + createInfo.title = "Input Window"; PalVideoFeatures64 features = palGetVideoFeaturesEx(); diff --git a/tests/multi_thread_opengl_test.c b/tests/multi_thread_opengl_test.c index 35b29b7..0950156 100644 --- a/tests/multi_thread_opengl_test.c +++ b/tests/multi_thread_opengl_test.c @@ -86,6 +86,11 @@ static void* PAL_CALL eventDriverWorker(void* arg) PAL_EVENT_WINDOW_SIZE, PAL_DISPATCH_POLL); + palSetEventDispatchMode( + shared->videoEventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // we are done shared->driverCreated = true; return nullptr; @@ -192,6 +197,7 @@ bool multiThreadOpenGlTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Multi Thread OpenGL Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -422,6 +428,15 @@ bool multiThreadOpenGlTest() break; } + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + shared->running = false; + } + break; + } + case PAL_EVENT_WINDOW_SIZE: { // tell the opengl driver about our size change palPushEvent(shared->openglEventDriver, &event); diff --git a/tests/native_instance_test.c b/tests/native_instance_test.c index 953aae6..097ac03 100644 --- a/tests/native_instance_test.c +++ b/tests/native_instance_test.c @@ -186,7 +186,7 @@ bool nativeInstanceTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "Native Integration Test"; + createInfo.title = "Native Instance Test"; // check if we support decorated windows (title bar, close etc) PalVideoFeatures64 features = palGetVideoFeaturesEx(); diff --git a/tests/opengl_context_test.c b/tests/opengl_context_test.c index dccf63b..62ceb0e 100644 --- a/tests/opengl_context_test.c +++ b/tests/opengl_context_test.c @@ -20,6 +20,7 @@ bool openglContextTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Opengl Context Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -176,7 +177,7 @@ bool openglContextTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "Pal Opengl Context Window"; + createInfo.title = "Opengl Context Window"; // check if we support decorated windows (title bar, close etc) PalVideoFeatures64 features = palGetVideoFeaturesEx(); @@ -200,6 +201,11 @@ bool openglContextTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // get window handle. You can use any window from any library // so long as you can get the window handle and display (if on X11, wayland) // If pal video system will not be used, there is no need to initialize it @@ -290,6 +296,15 @@ bool openglContextTest() running = false; break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } diff --git a/tests/opengl_multi_context_test.c b/tests/opengl_multi_context_test.c index 93415ca..03faebd 100644 --- a/tests/opengl_multi_context_test.c +++ b/tests/opengl_multi_context_test.c @@ -20,6 +20,7 @@ bool openglMultiContextTest() palLog(nullptr, ""); palLog(nullptr, "==========================================="); palLog(nullptr, "Opengl Multi Context Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -176,7 +177,7 @@ bool openglMultiContextTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "Pal Opengl Context Window"; + createInfo.title = "Opengl Multi Context Window"; // check if we support decorated windows (title bar, close etc) PalVideoFeatures64 features = palGetVideoFeaturesEx(); @@ -200,6 +201,11 @@ bool openglMultiContextTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_KEYDOWN, + PAL_DISPATCH_POLL); + // get window handle. You can use any window from any library // so long as you can get the window handle and display (if on X11, wayland) // If pal video system will not be used, there is no need to initialize it @@ -290,6 +296,15 @@ bool openglMultiContextTest() running = false; break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } @@ -341,6 +356,15 @@ bool openglMultiContextTest() running = false; break; } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } } } diff --git a/tests/system_cursor_test.c b/tests/system_cursor_test.c index d10e26e..09a52e3 100644 --- a/tests/system_cursor_test.c +++ b/tests/system_cursor_test.c @@ -68,7 +68,7 @@ bool systemCursorTest() createInfo.width = 640; createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; - createInfo.title = "PAL System Cursor Window - Cross"; + createInfo.title = "System Cursor Window - Cross"; // check if we support decorated windows (title bar, close etc) if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { diff --git a/tests/tests_main.c b/tests/tests_main.c index e0deb04..cc1df55 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -37,7 +37,7 @@ int main(int argc, char** argv) // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); - registerTest("Native Instance Test", nativeInstanceTest); + // registerTest("Native Instance Test", nativeInstanceTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid diff --git a/tests/window_test.c b/tests/window_test.c index 82488f4..f3c75d1 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -210,9 +210,9 @@ bool windowTest() } #if UNICODE_NAME - createInfo.title = "PAL Test Window Unicode - àà"; + createInfo.title = "Test Window Unicode - àà"; #else - createInfo.title = "PAL Test Window"; + createInfo.title = "Test Window"; #endif // UNICODE_NAME #if MAKE_BORDERLESS From d5a7260d41ff8763e62a8275c044dfd6c249c2f6 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 17:17:46 +0000 Subject: [PATCH 21/51] optimize monitor query --- src/video/pal_video_linux.c | 27 ++++++++------------------- tests/tests_main.c | 2 +- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 2b94303..870bb73 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -6300,7 +6300,6 @@ static void wlGlobalHandle( // wayland does not let use query monitors directly // so we enumerate and store at init and update the // cache when a monitor is added or removed - MonitorData* monitorData = getFreeMonitorData(); if (!monitorData) { return; @@ -6321,6 +6320,7 @@ static void wlGlobalRemove( MonitorData* monitorData = findMonitorData(m); if (monitorData) { monitorData->used = false; + s_Wl.monitorCount--; } } @@ -6746,25 +6746,14 @@ PalResult wlEnumerateMonitors( Int32* count, PalMonitor** outMonitors) { - int _count = 0; - int index = 0; - int maxCount = outMonitors ? *count : 0; if (outMonitors) { - if (_count < maxCount) { - for (int i = 0; i < maxCount; i++) { - // we get the monitor from our cache array - PalMonitor* monitor = nullptr; - for (;index < s_Video.maxMonitorData;) { - if (s_Video.monitorData[index].used) { - monitor = s_Video.monitorData[index].monitor; - break; - } - } - - // write to user provided array - outMonitors[_count] = monitor; - _count++; // index into user array - index++; + int index = 0; + int maxCount = s_Video.maxMonitorData; + for (int i = 0; i < maxCount && index < *count; i++) { + if (s_Video.monitorData[i].used) { + // found a monitor + PalMonitor* monitor = s_Video.monitorData[index].monitor; + outMonitors[index++] = monitor; } } } diff --git a/tests/tests_main.c b/tests/tests_main.c index cc1df55..8e1ae77 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -27,7 +27,7 @@ int main(int argc, char** argv) #if PAL_HAS_VIDEO // registerTest("Video Test", videoTest); - // registerTest("Monitor Test", monitorTest); + registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); From 82b393226f04093258d03af009ded24608efb491 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 21:40:35 +0000 Subject: [PATCH 22/51] x11 hotpluggin --- src/video/pal_video_linux.c | 173 ++++++++++++++++++++---------------- tests/tests_main.c | 4 +- 2 files changed, 98 insertions(+), 79 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 870bb73..52baa18 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -288,7 +288,6 @@ static EGL s_Egl; #define X_INTERN(x) s_X11Atoms.x = s_X11.internAtom(s_X11.display, #x, False) #define RANDR_SCREEN_CHANGE_EVENT 1040 -#define RANDR_NOTIFY_EVENT 1041 // optionally, needed to create visual from FBConfig #define GLX_FBCONFIG_ID 0x8012 @@ -720,10 +719,10 @@ typedef struct { typedef struct { bool error; bool skipScreenEvent; - bool skipNotifyEvent; int bpp; int screen; int depth; + int monitorCount; int rrEventBase; void* handle; void* xrandr; @@ -1396,6 +1395,7 @@ static void surfaceHandleEnter( struct wl_surface* surface, struct wl_output* output) { + int a = 10; // TODO: } @@ -1405,6 +1405,7 @@ static void surfaceHandleLeave( struct wl_surface* surface, struct wl_output* output) { + int b = 10; // TODO: } @@ -3254,8 +3255,10 @@ static PalWindowState xQueryWindowState(Window xWin) return state; } -static void xCacheMonitors(bool enumerate) +static void xCacheMonitors() { + resetMonitorData(); + XRRScreenResources* resources = nullptr; resources = s_X11.getScreenResources(s_X11.display, s_X11.root); @@ -3269,31 +3272,39 @@ static void xCacheMonitors(bool enumerate) // get monitor data and update info PalMonitor* monitor = TO_PAL_HANDLE(PalMonitor, output); MonitorData* data = nullptr; - - if (enumerate) { - data = getFreeMonitorData(); - if (!data) { - return; - } - - data->monitor = monitor; - - } else { - data = findMonitorData(monitor); + data = getFreeMonitorData(); + if (!data) { + return; } + data->monitor = monitor; + // clang-format off XRRCrtcInfo* crtc = s_X11.getCrtcInfo(s_X11.display, resources, info->crtc); // clang-format on - double dpi = (double)(crtc->width * 25.4) / (double)info->mm_width; - data->dpi = (int)dpi; + // get DPI + float raw = crtc->width / 1920.0f; + float steps[] = { 1.0f, 1.2f, 1.5f, 1.75, 2.0f }; + float closest = steps[0]; + float minDiff = fabsf(raw - steps[0]); + + for (int i = 1; i < sizeof(steps) / sizeof(steps[0]); i++) { + float diff = fabsf(raw - steps[i]); + if (diff < minDiff) { + minDiff = diff; + closest = steps[i]; + } + } + + data->dpi = (Uint32)(closest * 96.0f); data->w = crtc->width; data->h = crtc->height; data->x = crtc->x; data->y = crtc->y; s_X11.freeCrtcInfo(crtc); + s_X11.monitorCount++; } s_X11.freeOutputInfo(info); @@ -3302,9 +3313,7 @@ static void xCacheMonitors(bool enumerate) s_X11.freeScreenResources(resources); } -static int xGetWindowMonitorDPI( - WindowData* data, - bool enumerate) +static int xGetWindowMonitorDPI(WindowData* data) { int winX = data->x + data->w / 2; int winY = data->y + data->w / 2; @@ -3757,11 +3766,12 @@ static PalResult xInitVideo() s_X11.queryRRExtension(s_X11.display, &eventBase, &errorBase); s_X11.rrEventBase = eventBase; s_X11.skipScreenEvent = true; - s_X11.skipNotifyEvent = true; s_X11.dataID = (XContext)s_X11.uniqueContext(); resetMonitorData(); - xCacheMonitors(true); + + s_X11.monitorCount = 0; + xCacheMonitors(); // since X11 supports both EGL and GLX // we try to load them and resolve the needed functions @@ -3837,8 +3847,6 @@ static void xUpdateVideo() if (event.type == s_X11.rrEventBase + RRScreenChangeNotify) { event.type = RANDR_SCREEN_CHANGE_EVENT; // for switch flow - } else if (event.type == s_X11.rrEventBase + RRNotify) { - event.type = RANDR_NOTIFY_EVENT; // for switch flow } switch (event.type) { @@ -3858,7 +3866,7 @@ static void xUpdateVideo() } } } - return; + break; } case ConfigureNotify: { @@ -3871,7 +3879,7 @@ static void xUpdateVideo() data->h = event.xconfigure.height; data->x = event.xconfigure.x; data->y = event.xconfigure.y; - return; + break; } // real configure event @@ -3901,7 +3909,7 @@ static void xUpdateVideo() if (data->isAttached) { if (data->skipIfAttached) { data->skipIfAttached = false; - return; + break; } } @@ -3929,7 +3937,7 @@ static void xUpdateVideo() we get the monitor the moved window is on and check if the dpi is different from the one it was created on */ - int monitorDPI = xGetWindowMonitorDPI(data, false); + int monitorDPI = xGetWindowMonitorDPI(data); if (monitorDPI != data->dpi) { // window is on a different monitor data->dpi = monitorDPI; @@ -3948,7 +3956,7 @@ static void xUpdateVideo() } } } - return; + break; } case FocusIn: { @@ -3957,7 +3965,7 @@ static void xUpdateVideo() int mode = event.xfocus.mode; if (mode == NotifyGrab || mode == NotifyUngrab) { // ignore dragging and popup focus events - return; + break; } PalEventDriver* driver = s_Video.eventDriver; @@ -3971,7 +3979,7 @@ static void xUpdateVideo() palPushEvent(driver, &event); } } - return; + break; } case FocusOut: { @@ -3994,7 +4002,7 @@ static void xUpdateVideo() palPushEvent(driver, &event); } } - return; + break; } case PropertyNotify: { @@ -4008,7 +4016,7 @@ static void xUpdateVideo() // skip the first state event if (data->skipState) { data->skipState = false; - return; + break; } // push event @@ -4025,55 +4033,38 @@ static void xUpdateVideo() } } } - return; + break; } case RANDR_SCREEN_CHANGE_EVENT: { // skip the first event if (s_X11.skipScreenEvent) { s_X11.skipScreenEvent = false; - return; + break; } - // something change on pre existing monitor - // cache the information - xCacheMonitors(false); - return; - } + // store old monitor count + int oldCount = s_X11.monitorCount; + s_X11.monitorCount = 0; + xCacheMonitors(); - case RANDR_NOTIFY_EVENT: { - // skip the first event - if (s_X11.skipNotifyEvent) { - s_X11.skipNotifyEvent = false; - return; - } - - XRRNotifyEvent* e = (XRRNotifyEvent*)&event; - switch (e->subtype) { - case RRNotify_OutputChange: { - // push a event monitor list changed event - if (s_Video.eventDriver) { - PalEventDriver* driver = s_Video.eventDriver; - PalEventType type = PAL_EVENT_MONITOR_LIST_CHANGED; - mode = palGetEventDispatchMode(driver, type); - if (mode != PAL_DISPATCH_NONE) { - PalEvent event = {0}; - event.type = type; - event.data2 = palPackPointer(window); - palPushEvent(driver, &event); - } + if (oldCount != s_X11.monitorCount) { + // a monitor has been added or removed + if (s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + PalEventType type = PAL_EVENT_MONITOR_LIST_CHANGED; + mode = palGetEventDispatchMode(driver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data2 = palPackPointer(window); + palPushEvent(driver, &event); } - - /** enumerate monitors and cache them - these will be used to detect DPI changed - since X11 does not have a DPI changed function */ - resetMonitorData(); - xCacheMonitors(true); - return; } } + break; } - + case MotionNotify: { // mouse moved const int x = event.xmotion.x; @@ -4109,7 +4100,7 @@ static void xUpdateVideo() s_Mouse.lastY = y; s_Mouse.dx = dx; s_Mouse.dy = dy; - return; + break; } case ButtonPress: @@ -4178,7 +4169,7 @@ static void xUpdateVideo() palPushEvent(driver, &event); } } - return; + break; } case KeyPress: @@ -4255,7 +4246,7 @@ static void xUpdateVideo() type = PAL_EVENT_KEYCHAR; mode = palGetEventDispatchMode(driver, type); if (mode == PAL_DISPATCH_NONE) { - return; + break; } int status; @@ -4306,7 +4297,7 @@ static void xUpdateVideo() palPushEvent(driver, &event); } } - return; + break; } } } @@ -4446,8 +4437,20 @@ static PalResult xGetMonitorInfo( } // get dpi - double tmp = (double)(crtc->width * 25.4) / (double)outputInfo->mm_width; - info->dpi = (Uint32)tmp; + float raw = crtc->width / 1920.0f; + float steps[] = { 1.0f, 1.2f, 1.5f, 1.75, 2.0f }; + float closest = steps[0]; + float minDiff = fabsf(raw - steps[0]); + + for (int i = 1; i < sizeof(steps) / sizeof(steps[0]); i++) { + float diff = fabsf(raw - steps[i]); + if (diff < minDiff) { + minDiff = diff; + closest = steps[i]; + } + } + + info->dpi = (Uint32)(closest * 96.0f); s_X11.freeCrtcInfo(crtc); s_X11.freeOutputInfo(outputInfo); @@ -4790,6 +4793,7 @@ static PalResult xCreateWindow( int monitorY = 0; Uint32 monitorW = 0; Uint32 monitorH = 0; + int dpi = 0; if (info->monitor) { monitor = info->monitor; @@ -4809,6 +4813,7 @@ static PalResult xCreateWindow( monitorY = monitorInfo.y; monitorW = monitorInfo.width; monitorH = monitorInfo.height; + dpi = monitorInfo.dpi; } else { // primary monitor is not set @@ -4835,13 +4840,27 @@ static PalResult xCreateWindow( outputInfo->crtc); // clang-format on - monitorX = crtc->x; - monitorX = crtc->x; monitorY = crtc->y; monitorW = crtc->width; monitorH = crtc->height; + // get DPI + float raw = crtc->width / 1920.0f; + float steps[] = { 1.0f, 1.2f, 1.5f, 1.75, 2.0f }; + float closest = steps[0]; + float minDiff = fabsf(raw - steps[0]); + + for (int i = 1; i < sizeof(steps) / sizeof(steps[0]); i++) { + float diff = fabsf(raw - steps[i]); + if (diff < minDiff) { + minDiff = diff; + closest = steps[i]; + } + } + + dpi = (Uint32)(closest * 96.0f); + s_X11.freeCrtcInfo(crtc); s_X11.freeOutputInfo(outputInfo); break; @@ -5097,7 +5116,7 @@ static PalResult xCreateWindow( data->skipConfigure = true; data->skipState = true; data->isAttached = false; // true for attached windows - data->dpi = monitorInfo.dpi; // the current window monitor + data->dpi = dpi; // the current window monitor data->window = TO_PAL_HANDLE(PalWindow, window); s_X11.saveContext(s_X11.display, window, s_X11.dataID, (XPointer)data); diff --git a/tests/tests_main.c b/tests/tests_main.c index 8e1ae77..8213dd4 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -27,9 +27,9 @@ int main(int argc, char** argv) #if PAL_HAS_VIDEO // registerTest("Video Test", videoTest); - registerTest("Monitor Test", monitorTest); + // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); + registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); From 484e4c08a8272cd62b876806bd5ee6ce880ebbf1 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 13 Nov 2025 21:49:14 +0000 Subject: [PATCH 23/51] hotpluggin wayland --- src/video/pal_video_linux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 52baa18..da1b463 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -6231,7 +6231,7 @@ static struct wl_buffer* createShmBuffer( return buffer; } -static void wlGlobalHandle( +static void globalHandle( void* data, struct wl_registry* registry, uint32_t name, @@ -6330,7 +6330,7 @@ static void wlGlobalHandle( } } -static void wlGlobalRemove( +static void globalRemove( void* data, struct wl_registry* registry, uint32_t name) @@ -6451,8 +6451,8 @@ static void wlOutputDone( } static const struct wl_registry_listener s_RegistryListener = { - .global = wlGlobalHandle, - .global_remove = nullptr + .global = globalHandle, + .global_remove = globalRemove }; static const struct wl_output_listener s_OutputListener = { From 5dbea2dbb7d6dc8b7d03141a37f4ba15d21fab34 Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 13:34:50 +0000 Subject: [PATCH 24/51] update changelog --- CHANGELOG.md | 7 +- src/video/pal_video_linux.c | 42 +++------- tests/attach_window_test.c | 20 ++--- tests/native_instance_test.c | 146 ++++++++++++++++++++++++++++++++++- tests/tests_main.c | 4 +- 5 files changed, 172 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dea7552..860f8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,15 +88,14 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - Added native integration example: demonstrating **Native API Integration with PAL API**. see **native_integration_test.c**. -- Added native instance example: demonstrating -**Native Instance or Display Integration with PAL API**. +- Added native instance example: demonstrating **Native Instance or Display Integration with PAL API**. see **native_instance_test.c**. ### Notes - **No ABI changes** - existing code remains compatible. -- **Old tests may fail** - some tests written against old version need to -be updated to follow the new initialization order. +- **OpenGL tests may fail** - The opengl system now needs to call **palGLSetInstance()** +to set the instance or display before initializing. Failure to do this fails. This is a runtime behavior change. The tests are updated in the repo. - **palJoinThread()** - ABI remains unchanged but now takes the address diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index da1b463..5d0f812 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1207,16 +1207,16 @@ static inline void wlSurfaceAttach( int32_t x, int32_t y) { - s_Wl.proxyMarshalFlags - ((struct wl_proxy *) wl_surface, - WL_SURFACE_ATTACH, - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) wl_surface), - 0, - buffer, - x, - y); + s_Wl.proxyMarshalFlags( + (struct wl_proxy *) wl_surface, + WL_SURFACE_ATTACH, + NULL, + s_Wl.proxyGetVersion( + (struct wl_proxy *) wl_surface), + 0, + buffer, + x, + y); } static inline void wlSurfaceDamageBuffer( @@ -2178,17 +2178,6 @@ static inline int xdgSurfaceAddListener( (void (**)(void)) listener, data); } -static inline void xdgWmBaseDestroy(struct xdg_wm_base *xdg_wm_base) -{ - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_wm_base, - 0, // XDG_WM_BASE_DESTROY - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_wm_base), - WL_MARSHAL_FLAG_DESTROY); -} - static inline void xdgSurfaceDestroy(struct xdg_surface *xdg_surface) { s_Wl.proxyMarshalFlags( @@ -5937,7 +5926,7 @@ PalResult xAttachWindow( // we assume the window was just created, since there is // no official way to get the DPI data->isAttached = true; - data->dpi = 100; // if this is not the DPI, a dpi event will be triggered + data->dpi = 96; // if this is not the DPI, a dpi event will be triggered // If the window manager has not mapped the window yet, // we dont need the initial Size / Move events @@ -6690,15 +6679,6 @@ void wlShutdownVideo() } s_Wl.xkbContextUnref(s_Wl.inputContext); - if (s_Wl.decorationManager) { - zxdgDecorationManagerV1Destroy(s_Wl.decorationManager); - } - - s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.shm); - xdgWmBaseDestroy(s_Wl.xdgBase); - s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.compositor); - s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.registry); - if (!s_Video.platformInstance) { // opened by PAL s_Wl.displayDisconnect(s_Wl.display); diff --git a/tests/attach_window_test.c b/tests/attach_window_test.c index 396da8f..bbe8a16 100644 --- a/tests/attach_window_test.c +++ b/tests/attach_window_test.c @@ -48,7 +48,7 @@ typedef int (*XDestroyWindowFn)( Display*, Window); -static void* s_X11Lib; +static void* s_LibX; static XCreateSimpleWindowFn s_XCreateWindow; static XSyncFn s_XSync; static XMapRaisedFn s_XMapRaised; @@ -60,32 +60,32 @@ static XDestroyWindowFn s_XDestroyWindow; #define WINDOW_POSY 100 #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 -#define WINDOW_TITLE "PAL Attach Window Test" +#define WINDOW_TITLE "Attach Window Test" static void* createX11Window() { #ifdef __linux__ // load the procs - s_X11Lib = dlopen("libX11.so", RTLD_LAZY); - if (!s_X11Lib) { + s_LibX = dlopen("libX11.so", RTLD_LAZY); + if (!s_LibX) { return nullptr; } // clang-format off s_XCreateWindow = (XCreateSimpleWindowFn)dlsym( - s_X11Lib, + s_LibX, "XCreateSimpleWindow"); s_XSync = (XSyncFn)dlsym( - s_X11Lib, + s_LibX, "XSync"); s_XMapRaised = (XMapRaisedFn)dlsym( - s_X11Lib, + s_LibX, "XMapRaised"); s_XDestroyWindow = (XDestroyWindowFn)dlsym( - s_X11Lib, + s_LibX, "XDestroyWindow"); if (!s_XCreateWindow || !s_XSync || !s_XMapRaised || !s_XDestroyWindow) { @@ -126,6 +126,8 @@ static void* createX11Window() return nullptr; } + + static void* createWin32Window() { #ifdef _WIN32 @@ -183,7 +185,7 @@ static void destroyX11Window(void* windowHandle) #ifdef __linux__ Display* display = palGetInstance(); s_XDestroyWindow(display, (Window)(UintPtr)windowHandle); - dlclose(s_X11Lib); // we loaded dynamically + dlclose(s_LibX); // we loaded dynamically #endif } diff --git a/tests/native_instance_test.c b/tests/native_instance_test.c index 097ac03..0c6ffcc 100644 --- a/tests/native_instance_test.c +++ b/tests/native_instance_test.c @@ -29,12 +29,131 @@ static XOpenDisplayFn s_XOpenDisplay; static XCloseDisplayFn s_XCloseDisplay; struct wl_display; +struct wl_registry; +struct wl_proxy; +struct wl_interface; + typedef struct wl_display* (*wl_display_connect_fn)(const char*); typedef void (*wl_display_disconnect_fn)(struct wl_display*); +typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); +typedef int (*wl_display_roundtrip_fn)(struct wl_display*); + +typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, + uint32_t, ...); + +typedef int (*wl_proxy_add_listener_fn)( + struct wl_proxy*, + void (**)(void), void*); + +struct wl_interface { + const char *name; + int version; + int method_count; + const struct wl_message *methods; + int event_count; + const struct wl_message *events; +}; + +struct wl_registry_listener { + void (*global)(void*, + struct wl_registry*, + uint32_t, + const char*, + uint32_t); + + void (*global_remove)(void*, struct wl_registry*, uint32_t); +}; static void* s_LibWayland; static wl_display_connect_fn s_wl_display_connect; static wl_display_disconnect_fn s_wl_display_disconnect; +static wl_display_roundtrip_fn s_wl_display_roundtrip; +static wl_proxy_get_version_fn s_wl_proxy_get_version; +static wl_proxy_marshal_flags_fn s_wl_proxy_marshal_flags; +static wl_proxy_add_listener_fn s_wl_proxy_add_listener; + +static const struct wl_interface* registryInterface; + +static inline void* wlRegistryBind( + struct wl_registry *wl_registry, + uint32_t name, + const struct wl_interface* interface, + uint32_t version) +{ + struct wl_proxy *id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy *)wl_registry, + 0, // WL_REGISTRY_BIND + interface, + version, + 0, + name, + interface->name, + version, + NULL); + + return (void *)id; +} + +static inline int wlRegistryAddListener( + struct wl_registry *wl_registry, + const struct wl_registry_listener *listener, + void *data) +{ + return s_wl_proxy_add_listener( + (struct wl_proxy *) wl_registry, + (void (**)(void)) listener, data); +} + +static inline struct wl_registry* wlDisplayGetRegistry( + struct wl_display *wl_display) +{ + struct wl_proxy *registry; + registry = s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_display, + 1, // WL_DISPLAY_GET_REGISTRY + registryInterface, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_display), + 0, + NULL); + + return (struct wl_registry *)registry; +} + +static bool s_Logged = false; +static void globalHandle( + void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) +{ + if (!s_Logged) { + palLog(nullptr, "Registry global handle working"); + s_Logged = true; + } +} + +static void globalRemove( + void* data, + struct wl_registry* registry, + uint32_t name) +{ + if (s_Logged) { + palLog(nullptr, "Registry global remove working"); + s_Logged = false; + } +} + +static const struct wl_registry_listener s_RegistryListener = { + .global = globalHandle, + .global_remove = globalRemove +}; static bool s_OnWayland = false; @@ -88,7 +207,32 @@ void* openDisplayWayland() s_LibWayland, "wl_display_disconnect"); - return s_wl_display_connect(nullptr); + s_wl_display_roundtrip = (wl_display_roundtrip_fn)dlsym( + s_LibWayland, + "wl_display_roundtrip"); + + s_wl_proxy_marshal_flags = (wl_proxy_marshal_flags_fn)dlsym( + s_LibWayland, + "wl_proxy_marshal_flags"); + + s_wl_proxy_get_version = (wl_proxy_get_version_fn)dlsym( + s_LibWayland, + "wl_proxy_get_version"); + + s_wl_proxy_add_listener = (wl_proxy_add_listener_fn)dlsym( + s_LibWayland, + "wl_proxy_add_listener"); + + registryInterface = dlsym(s_LibWayland, "wl_registry_interface"); + + struct wl_display* display = s_wl_display_connect(nullptr); + if (display) { + struct wl_registry* registry = wlDisplayGetRegistry(display); + wlRegistryAddListener(registry, &s_RegistryListener, nullptr); + s_wl_display_roundtrip(display); + } + + return display; #endif // __linux__ } diff --git a/tests/tests_main.c b/tests/tests_main.c index 8213dd4..1e1aae9 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -29,12 +29,12 @@ int main(int argc, char** argv) // registerTest("Video Test", videoTest); // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); + // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); - // registerTest("Attach Window Test", attachWindowTest); + registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); // registerTest("Native Instance Test", nativeInstanceTest); From 2a3eabb0d4e32e345d67511d2585994dddbb6baf Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 14:20:07 +0000 Subject: [PATCH 25/51] fix wayland keychar --- src/video/pal_video_linux.c | 20 ++++++++++++-------- tests/tests_main.c | 4 ++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 5d0f812..4e7aa09 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1737,7 +1737,6 @@ static void keyboardHandleKey( PalScancode scancode = 0; PalKeycode keycode = 0; - bool validCodepoint = true; bool pressed = (state == WL_KEYBOARD_KEY_STATE_PRESSED); PalEventType type = PAL_EVENT_KEYUP; PalDispatchMode mode = PAL_DISPATCH_NONE; @@ -1758,11 +1757,11 @@ static void keyboardHandleKey( } // printable and text input keys are from the range - // 39 (PAL_KEYCODE_APOSTROPHE) and 122 (PAL_KEYCODE_Z) + // 32 (PAL_KEYCODE_SPACE) and 122 (PAL_KEYCODE_Z) // The rest are almost the same as their scancode // Maybe there will be a layout that makes this wrong // but for now this works - if (keySym >= XKB_KEY_apostrophe && keySym <= XKB_KEY_z) { + if (keySym >= XKB_KEY_space && keySym <= XKB_KEY_z) { // a printable or input key keycode = s_Keyboard.keycodes[keySym]; @@ -1771,14 +1770,12 @@ static void keyboardHandleKey( // we can make a direct cast without a table // Examle: PAL_KEYCODE_A(int 0) == PAL_SCANCODE_A(int 0) keycode = (PalKeycode)(Uint32)scancode; - validCodepoint = false; } // If we got a keySym but its not mapped into our keycode array // we do a direct cast as well if (keycode == PAL_KEYCODE_UNKNOWN) { keycode = (PalKeycode)(Uint32)scancode; - validCodepoint = false; } s_Keyboard.scancodeState[scancode] = pressed; @@ -1818,11 +1815,15 @@ static void keyboardHandleKey( // check for char event if enabled type = PAL_EVENT_KEYCHAR; mode = palGetEventDispatchMode(driver, type); - if (mode == PAL_DISPATCH_NONE || validCodepoint == false) { + if (mode == PAL_DISPATCH_NONE) { return; } Uint32 codepoint = s_Wl.xkbKeysymToUtf32(keySym); + if (codepoint <= 0) { + return; + } + PalEvent event = {0}; event.type = type; event.data = codepoint; @@ -4184,11 +4185,11 @@ static void xUpdateVideo() } // printable and text input keys are from the range - // 39 (PAL_KEYCODE_APOSTROPHE) and 122 (PAL_KEYCODE_Z) + // 32 (PAL_KEYCODE_SPACE) and 122 (PAL_KEYCODE_Z) // The rest are almost the same as their scancode // Maybe there will be a layout that makes this wrong // but for now this works - if (keySym >= XK_apostrophe && keySym <= XK_z) { + if (keySym >= XK_space && keySym <= XK_z) { // a printable or input key keycode = s_Keyboard.keycodes[keySym]; @@ -6688,6 +6689,7 @@ void wlShutdownVideo() dlclose(s_Wl.xkbCommon); dlclose(s_Wl.libWaylandEgl); dlclose(s_Wl.handle); + memset(&s_Wl, 0, sizeof(Wayland)); } @@ -7589,6 +7591,8 @@ void PAL_CALL palShutdownVideo() } s_Video.platformInstance = nullptr; + memset(&s_Keyboard, 0, sizeof(Keyboard)); + memset(&s_Mouse, 0, sizeof(Mouse)); s_Video.initialized = false; } } diff --git a/tests/tests_main.c b/tests/tests_main.c index 1e1aae9..2dce6ae 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -34,8 +34,8 @@ int main(int argc, char** argv) // registerTest("Cursor Test", cursorTest); // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); - registerTest("Attach Window Test", attachWindowTest); - // registerTest("Character Event Test", charEventTest); + // registerTest("Attach Window Test", attachWindowTest); + registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); // registerTest("Native Instance Test", nativeInstanceTest); #endif // PAL_HAS_VIDEO From b0f9db6d96d3092b9502d9088d4c2f04923e05ea Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 15:39:09 +0000 Subject: [PATCH 26/51] update win32 --- src/opengl/pal_opengl_win32.c | 26 ++++---- src/thread/pal_thread_win32.c | 8 +-- src/video/pal_video_win32.c | 109 ++++++++++++++++++++++++++------ tests/input_window_test.c | 3 +- tests/native_instance_test.c | 16 +++++ tests/native_integration_test.c | 17 ++++- tests/tests_main.c | 2 +- 7 files changed, 139 insertions(+), 42 deletions(-) diff --git a/src/opengl/pal_opengl_win32.c b/src/opengl/pal_opengl_win32.c index d815fed..b34f688 100644 --- a/src/opengl/pal_opengl_win32.c +++ b/src/opengl/pal_opengl_win32.c @@ -203,8 +203,8 @@ typedef struct { PalGLInfo info; } Wgl; -static Gdi s_Gdi; -static Wgl s_Wgl; +static Gdi s_Gdi = {0}; +static Wgl s_Wgl = {0}; // ================================================== // Internal API @@ -254,8 +254,10 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } // get the instance - s_Wgl.instance = GetModuleHandleW(nullptr); - + if (!s_Wgl.instance) { + s_Wgl.instance = GetModuleHandleW(nullptr); + } + // register class WNDCLASSEXW wc = {0}; wc.style = CS_OWNDC; @@ -514,12 +516,13 @@ void PAL_CALL palShutdownGL() FreeLibrary(s_Wgl.opengl); FreeLibrary(s_Gdi.handle); + + memset(&s_Wgl, 0, sizeof(Wgl)); s_Wgl.initialized = false; } const PalGLInfo* PAL_CALL palGetGLInfo() { - if (!s_Wgl.initialized) { return nullptr; } @@ -1040,20 +1043,13 @@ PalResult PAL_CALL palSetSwapInterval(Int32 interval) void PAL_CALL palGLSetInstance(void* instance) { - // TODO - s_GL.platformDisplay = instance; + s_Wgl.instance = instance; } const char* PAL_CALL palGLGetBackend() { - // TODO: - if (!s_GL.initialized) { + if (!s_Wgl.initialized) { return nullptr; } - - if (s_GL.apiType == EGL_OPENGL_API) { - return "egl"; - } else { - return "gles"; - } + return "wgl"; } \ No newline at end of file diff --git a/src/thread/pal_thread_win32.c b/src/thread/pal_thread_win32.c index ca3fcbc..20dc15b 100644 --- a/src/thread/pal_thread_win32.c +++ b/src/thread/pal_thread_win32.c @@ -151,15 +151,15 @@ PalResult PAL_CALL palJoinThread( return PAL_RESULT_NULL_POINTER; } + void* value = nullptr; DWORD wait = WaitForSingleObject((HANDLE)thread, INFINITE); if (wait == WAIT_OBJECT_0 && retval) { uintptr_t ret; GetExitCodeThread((HANDLE)thread, (LPDWORD)&ret); - retval = (void*)ret; + void** out = (void**)retval; + *out = value; - // thread is done - // destroy the HANDLE - // TODO: + // thread is done destroy the HANDLE CloseHandle((HANDLE)thread); } else if (wait == WAIT_FAILED) { diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index ab0d572..54ebf4a 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -120,6 +120,7 @@ typedef struct { HINSTANCE instance; HWND hiddenWindow; + HCURSOR defaultCursor; WindowData* windowData; } VideoWin32; @@ -653,11 +654,12 @@ LRESULT CALLBACK videoProc( if (LOWORD(lParam) == HTCLIENT) { if (data && data->cursor) { SetCursor(data->cursor); - return TRUE; + } else { + // no cursor, use default + SetCursor(s_Video.defaultCursor); } - return FALSE; + return TRUE; } - break; } @@ -1071,12 +1073,17 @@ PalResult PAL_CALL palInitVideo( 0); // get the instance - s_Video.instance = GetModuleHandleW(nullptr); + if (!s_Video.instance) { + s_Video.instance = GetModuleHandleW(nullptr); + } + + // load default cursor + s_Video.defaultCursor = LoadCursorW(NULL, IDC_ARROW); // register class WNDCLASSEXW wc = {0}; wc.cbSize = sizeof(WNDCLASSEXW); - wc.hCursor = LoadCursorW(NULL, IDC_ARROW); + wc.hCursor = s_Video.defaultCursor; wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); wc.hIconSm = LoadIconW(NULL, IDI_APPLICATION); wc.hInstance = s_Video.instance; @@ -1167,7 +1174,6 @@ PalResult PAL_CALL palInitVideo( // clang-format on - // TODO:: // set features s_Video.features |= PAL_VIDEO_FEATURE_MONITOR_SET_ORIENTATION; s_Video.features |= PAL_VIDEO_FEATURE_MONITOR_GET_ORIENTATION; @@ -1203,18 +1209,51 @@ PalResult PAL_CALL palInitVideo( if (s_Video.getDpiForMonitor && s_Video.setProcessAwareness) { s_Video.features |= PAL_VIDEO_FEATURE_HIGH_DPI; + s_Video.features64 |= PAL_VIDEO_FEATURE64_HIGH_DPI; s_Video.setProcessAwareness(WIN32_DPI_AWARE); } // extended features - s_Video.features64 |= PAL_VIDEO_FEATURE_TOPMOST_WINDOW; - s_Video.features64 |= PAL_VIDEO_FEATURE_DECORATED_WINDOW; - s_Video.features64 |= PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY; - s_Video.features64 |= PAL_VIDEO_FEATURE_WINDOW_GET_MONITOR; - s_Video.features64 |= PAL_VIDEO_FEATURE_MONITOR_GET_PRIMARY; - s_Video.features64 |= PAL_VIDEO_FEATURE_FOREIGN_WINDOWS; - s_Video.features64 |= PAL_VIDEO_FEATURE_MONITOR_VALIDATE_MODE; - s_Video.features64 |= PAL_VIDEO_FEATURE_WINDOW_SET_CURSOR; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MONITOR_SET_ORIENTATION; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_ORIENTATION; + s_Video.features64 |= PAL_VIDEO_FEATURE64_BORDERLESS_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE64_TRANSPARENT_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE64_TOOL_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MONITOR_SET_MODE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_MODE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MULTI_MONITORS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_SIZE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_SIZE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_POS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_POS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_STATE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_STATE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_VISIBILITY; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_VISIBILITY; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_TITLE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_TITLE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_NO_MAXIMIZEBOX; + s_Video.features64 |= PAL_VIDEO_FEATURE64_NO_MINIMIZEBOX; + s_Video.features64 |= PAL_VIDEO_FEATURE64_CLIP_CURSOR; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_FLASH_CAPTION; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_FLASH_TRAY; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_FLASH_INTERVAL; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_INPUT_FOCUS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_INPUT_FOCUS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_STYLE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_STYLE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_CURSOR_SET_POS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_CURSOR_GET_POS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_ICON; + + s_Video.features64 |= PAL_VIDEO_FEATURE64_TOPMOST_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE64_DECORATED_WINDOW; + s_Video.features64 |= PAL_VIDEO_FEATURE64_CURSOR_SET_VISIBILITY; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_GET_MONITOR; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MONITOR_GET_PRIMARY; + s_Video.features64 |= PAL_VIDEO_FEATURE64_FOREIGN_WINDOWS; + s_Video.features64 |= PAL_VIDEO_FEATURE64_MONITOR_VALIDATE_MODE; + s_Video.features64 |= PAL_VIDEO_FEATURE64_WINDOW_SET_CURSOR; s_Video.initialized = true; s_Video.allocator = allocator; @@ -1237,6 +1276,12 @@ void PAL_CALL palShutdownVideo() DestroyWindow(s_Video.hiddenWindow); UnregisterClassW(PAL_VIDEO_CLASS, s_Video.instance); palFree(s_Video.allocator, s_Video.windowData); + + memset(&s_Video, 0, sizeof(VideoWin32)); + memset(&s_Keyboard, 0, sizeof(Keyboard)); + memset(&s_Mouse, 0, sizeof(Mouse)); + + s_Video.windowData = nullptr; s_Video.initialized = false; } @@ -1293,13 +1338,13 @@ PalVideoFeatures PAL_CALL palGetVideoFeatures() return s_Video.features; } -palGetVideoFeaturesEx PAL_CALL palGetVideoFeaturesEx() +PalVideoFeatures64 PAL_CALL palGetVideoFeaturesEx() { if (!s_Video.initialized) { return 0; } - - return ((Uint64)s_Video.features64) | (Uint64)s_Video.features; + + return s_Video.features64; } PalResult PAL_CALL palSetFBConfig( @@ -1851,7 +1896,9 @@ void PAL_CALL palDestroyWindow(PalWindow* window) if (data->isAttached) { return; } + DestroyWindow((HWND)window); + data->used = false; } } @@ -2262,8 +2309,17 @@ void PAL_CALL palGetRawMouseWheelDelta( float* dx, float* dy) { - // TODO: + if (!s_Video.initialized) { + return; + } + + if (dx) { + *dx = (float)s_Mouse.WheelX; + } + if (dy) { + *dy = (float)s_Mouse.WheelY; + } } bool PAL_CALL palIsWindowVisible(PalWindow* window) @@ -2297,6 +2353,16 @@ PalWindowHandleInfo PAL_CALL palGetWindowHandleInfo(PalWindow* window) return handle; } +PalWindowHandleInfoEx PAL_CALL palGetWindowHandleInfoEx(PalWindow* window) +{ + PalWindowHandleInfoEx handle = {0}; + if (s_Video.initialized && window) { + handle.nativeDisplay = nullptr; + handle.nativeWindow = (void*)window; + } + return handle; +} + PalResult PAL_CALL palSetWindowOpacity( PalWindow* window, float opacity) @@ -3005,4 +3071,11 @@ PalResult PAL_CALL palDetachWindow( } return PAL_RESULT_SUCCESS; +} + +void PAL_CALL palSetPreferredInstance(void* instance) +{ + if (!s_Video.initialized && instance) { + s_Video.instance = instance; + } } \ No newline at end of file diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 532b553..05536e0 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -461,9 +461,8 @@ bool inputWindowTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; createInfo.title = "Input Window"; - PalVideoFeatures64 features = palGetVideoFeaturesEx(); - // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { // if we dont support, we need to create a borderless window // and create the decorations ourselves diff --git a/tests/native_instance_test.c b/tests/native_instance_test.c index 0c6ffcc..9411f0f 100644 --- a/tests/native_instance_test.c +++ b/tests/native_instance_test.c @@ -16,6 +16,8 @@ #define UNICODE #endif // UNICODE +#include + #elif defined(__linux__) #include #include @@ -244,9 +246,22 @@ void closeDisplayWayland(void* instance) #endif // __linux__ } +void* openDisplayWin32() +{ +#ifdef __WIN32 + return GetModuleHandleW(nullptr); +#endif // __WIN32 +} + +void closeDisplayWin32(void* instance) +{ + // this does nothing +} + void* openInstance() { #ifdef _WIN32 + return openDisplayWin32(); #elif defined(__linux__) // get the active session const char* session = getenv("XDG_SESSION_TYPE"); @@ -269,6 +284,7 @@ void* openInstance() void closeInstance(void* instance) { #ifdef _WIN32 + closeDisplayWin32(instance); #elif defined(__linux__) if (s_OnWayland) { closeDisplayWayland(instance); diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 7fe0903..5793cd0 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -271,12 +271,24 @@ void getWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) #endif // __linux__ } -void setWindowTitleWin32(PalWindowHandleInfoEx* windowInfo); -void getWindowTitleWin32(PalWindowHandleInfoEx* windowInfo); +void setWindowTitleWin32(PalWindowHandleInfoEx* windowInfo) +{ + const char* title = "Hello from native Win32 API"; + SetWindowTextA((HWND)windowInfo->nativeWindow, title); +} + +void getWindowTitleWin32(PalWindowHandleInfoEx* windowInfo) +{ + GetWindowTextA( + (HWND)windowInfo->nativeWindow, + s_TitleBuffer, + sizeof(s_TitleBuffer)); +} void setWindowTitle(PalWindowHandleInfoEx* windowInfo) { #ifdef _WIN32 + setWindowTitleWin32(windowInfo); #elif defined(__linux__) // get the active session const char* session = getenv("XDG_SESSION_TYPE"); @@ -299,6 +311,7 @@ void setWindowTitle(PalWindowHandleInfoEx* windowInfo) void getWindowTitle(PalWindowHandleInfoEx* windowInfo) { #ifdef _WIN32 + getWindowTitleWin32(windowInfo); #elif defined(__linux__) if (s_OnWayland) { getWindowTitleWayland(windowInfo); diff --git a/tests/tests_main.c b/tests/tests_main.c index 2dce6ae..cc1df55 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -35,7 +35,7 @@ int main(int argc, char** argv) // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); - registerTest("Character Event Test", charEventTest); + // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); // registerTest("Native Instance Test", nativeInstanceTest); #endif // PAL_HAS_VIDEO From 5d3f0d0cafacf87d204a73686bde6f2d3dbcc927 Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 15:43:50 +0000 Subject: [PATCH 27/51] format includes --- include/pal/pal_core.h | 6 ++--- include/pal/pal_opengl.h | 10 ++++---- include/pal/pal_thread.h | 4 ++-- include/pal/pal_video.h | 32 +++++++++++++------------ tests/tests_main.c | 52 ++++++++++++++++++++-------------------- 5 files changed, 53 insertions(+), 51 deletions(-) diff --git a/include/pal/pal_core.h b/include/pal/pal_core.h index 309d12f..a21c117 100644 --- a/include/pal/pal_core.h +++ b/include/pal/pal_core.h @@ -78,7 +78,7 @@ typedef _Bool bool; #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define PAL_BIG_ENDIAN 1 -#else +#else #define PAL_BIG_ENDIAN 0 #endif // __ORDER_BIG_ENDIAN__ @@ -489,7 +489,7 @@ static inline Int64 PAL_CALL palPackFloat( #if PAL_BIG_ENDIAN memcpy(&((Uint32*)&combined)[0], &high, sizeof(float)); memcpy(&((Uint32*)&combined)[1], &low, sizeof(float)); -#else +#else memcpy(&((Uint32*)&combined)[0], &low, sizeof(float)); memcpy(&((Uint32*)&combined)[1], &high, sizeof(float)); #endif // PAL_BIG_ENDIAN @@ -592,7 +592,7 @@ static inline void PAL_CALL palUnpackFloat( if (high) { memcpy(high, &((Uint32*)&data)[0], sizeof(float)); } -#else +#else if (low) { memcpy(low, &((Uint32*)&data)[0], sizeof(float)); } diff --git a/include/pal/pal_opengl.h b/include/pal/pal_opengl.h index d1bb961..a54a5e7 100644 --- a/include/pal/pal_opengl.h +++ b/include/pal/pal_opengl.h @@ -317,7 +317,7 @@ PAL_API const PalGLFBConfig* PAL_CALL palGetClosestGLFBConfig( * The provided PalGLFBConfig must be the same as the one used to create the * window. Once set, it cannot be changed. To change it, you must destroy the * window and recreate it. - * + * * On Wayland: PalGLContextCreateInfo::PalGLWindow::window is the wl_egl_window * not the wl_surface. * @@ -366,7 +366,7 @@ PAL_API void PAL_CALL palDestroyGLContext(PalGLContext* context); * context. If the PalGLFBConfig of the opengl window is not the same as the one * used to create the context, this function fails and returns * `PAL_RESULT_INVALID_GL_WINDOW`. - * + * * If the window was created with a different display other than the one * passed to the opengl system, this function fails and returns * `PAL_RESULT_INVALID_GL_WINDOW`. see palGLSetInstance() @@ -453,7 +453,7 @@ PAL_API PalResult PAL_CALL palSetSwapInterval(Int32 interval); * @brief Set the native application instance or display for the opengl system. * * This must be called before palInitGL() is called. if palInitGL() is called - * before this function, + * before this function, * it fails and returns `PAL_RESULT_PLATFORM_FAILURE` will be returned. * * On Linux: This is the Display associated with the connection. @@ -461,7 +461,7 @@ PAL_API PalResult PAL_CALL palSetSwapInterval(Int32 interval); * On Windows: This is the HINSTANCE of the process. * * Thread safety: This function is thread safe. - * + * * @note The provided instance will not be freed by the opengl system. * * @since 1.3 @@ -472,7 +472,7 @@ PAL_API void PAL_CALL palGLSetInstance(void* instance); /** * @brief Get the backend of the opengl system. - * + * * The opengl system must be initialized before this call. * Possible values are `wgl`, `glx`, `gles`, `egl`. * diff --git a/include/pal/pal_thread.h b/include/pal/pal_thread.h index ed4d5c8..666f27e 100644 --- a/include/pal/pal_thread.h +++ b/include/pal/pal_thread.h @@ -178,7 +178,7 @@ PAL_API PalResult PAL_CALL palCreateThread( /** * @brief Wait for the provided thread to finish executing. - * + * * After the thread is done executing, it is freed automatically and * must not be used anymore nor detached. * @@ -205,7 +205,7 @@ PAL_API PalResult PAL_CALL palJoinThread( * This function must be called when the thread is done executing. * After this call, the thread cannot be attached or used anymore. * If the thread is invalid or nullptr, this function returns silently. - * + * * This must not be called on a thread that has been attached. * * @param[in] thread Pointer to the thread to detach. diff --git a/include/pal/pal_video.h b/include/pal/pal_video.h index b0c8f89..d06c968 100644 --- a/include/pal/pal_video.h +++ b/include/pal/pal_video.h @@ -660,9 +660,9 @@ typedef struct { typedef struct { void* nativeDisplay; /**< The platform (OS) display.*/ void* nativeWindow; /**< The window platform (OS) handle.*/ - void* nativeHandle1; /**< Extra window handle (xdgSurface)*/ - void* nativeHandle2; /**< Extra window handle (xdgToplevel)*/ - void* nativeHandle3; /**< Extra window handle (wl_egl_window)*/ + void* nativeHandle1; /**< Extra window handle (xdgSurface)*/ + void* nativeHandle2; /**< Extra window handle (xdgToplevel)*/ + void* nativeHandle3; /**< Extra window handle (wl_egl_window)*/ } PalWindowHandleInfoEx; /** @@ -955,7 +955,7 @@ PAL_API PalResult PAL_CALL palGetCurrentMonitorMode( * * PAL only validates the monitor display mode pointer not the values. To be * safe, users must get the monitor mode from palEnumerateMonitorModes() or call - * palValidateMonitorMode() to validate before switching. + * palValidateMonitorMode() to validate before switching. * palValidateMonitorMode() is not supported on all platforms. * * If the monitor display mode submitted is invalid, this function might fail @@ -1035,14 +1035,14 @@ PAL_API PalResult PAL_CALL palSetMonitorOrientation( * failure. Call palFormatResult() for more information. * * Thread safety: This function must only be called from the main thread. - * + * * @note On Wayland - * - * - creating non resizable windows is not supported. + * + * - creating non resizable windows is not supported. * PAL will always creating resizable windows. - * + * * - Creating windows on a specific monitor is not supported. - * + * * - Creating hidden window is not supported. It will be ignored. * * @since 1.0 @@ -1124,7 +1124,7 @@ PAL_API PalResult PAL_CALL palMaximizeWindow(PalWindow* window); * failure. Call palFormatResult() for more information. * * Thread safety: This function must only be called from the main thread. - * + * * @note Wayland does not support restoring a minimized windows. * * @since 1.0 @@ -1443,8 +1443,10 @@ PAL_API void PAL_CALL palGetMouseWheelDelta( * The video system must be initialized before this call. * The wheel delta will be updated when palUpdateVideo() is called. * - * @param[in] dx Pointer to recieve the mouse wheel delta x in floats. Can be nullptr. - * @param[in] dy Pointer to recieve the mouse wheel delta y in floats. Can be nullptr. + * @param[in] dx Pointer to recieve the mouse wheel delta x in floats. Can be + * nullptr. + * @param[in] dy Pointer to recieve the mouse wheel delta y in floats. Can be + * nullptr. * * Thread safety: This function is thread-safe if `dx` and `dy` are thread * local. @@ -1509,9 +1511,9 @@ PAL_API PalWindowHandleInfo PAL_CALL palGetWindowHandleInfo(PalWindow* window); * @brief Get the native handles of the provided window. * * The video system must be initialized before this call. - * + * * On Wayland: `PalWindowHandleInfoEx::nativeHandle1`, - * `PalWindowHandleInfoEx::nativeHandle2` and + * `PalWindowHandleInfoEx::nativeHandle2` and * `PalWindowHandleInfoEx::nativeHandle3` are xdg_surface, xdg_toplevel * and wl_egl_window respectively. * @@ -1793,7 +1795,7 @@ PAL_API void PAL_CALL palDestroyCursor(PalCursor* cursor); * * The video system must be initialized before this call. * `PAL_VIDEO_FEATURE_CURSOR_SET_VISIBILITY` must be supported. - * + * * This affects all created cursors since the platform (OS) merges all cursors * into a single one on the screen. * diff --git a/tests/tests_main.c b/tests/tests_main.c index cc1df55..141878c 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,48 +9,48 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - // registerTest("Logger Test", loggerTest); - // registerTest("Time Test", timeTest); - // registerTest("User Event Test", userEventTest); - // registerTest("Event Test", eventTest); + registerTest("Logger Test", loggerTest); + registerTest("Time Test", timeTest); + registerTest("User Event Test", userEventTest); + registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - // registerTest("System Test", systemTest); + registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - // registerTest("Thread Test", threadTest); - // registerTest("TLS Test", tlsTest); - // registerTest("Mutex Test", mutexTest); - // registerTest("Condvar Test", condvarTest); + registerTest("Thread Test", threadTest); + registerTest("TLS Test", tlsTest); + registerTest("Mutex Test", mutexTest); + registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO - // registerTest("Video Test", videoTest); - // registerTest("Monitor Test", monitorTest); - // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); - // registerTest("Icon Test", iconTest); - // registerTest("Cursor Test", cursorTest); - // registerTest("Input Window Test", inputWindowTest); - // registerTest("System Cursor Test", systemCursorTest); - // registerTest("Attach Window Test", attachWindowTest); - // registerTest("Character Event Test", charEventTest); - // registerTest("Native Integration Test", nativeIntegrationTest); - // registerTest("Native Instance Test", nativeInstanceTest); + registerTest("Video Test", videoTest); + registerTest("Monitor Test", monitorTest); + registerTest("Monitor Mode Test", monitorModeTest); + registerTest("Window Test", windowTest); + registerTest("Icon Test", iconTest); + registerTest("Cursor Test", cursorTest); + registerTest("Input Window Test", inputWindowTest); + registerTest("System Cursor Test", systemCursorTest); + registerTest("Attach Window Test", attachWindowTest); + registerTest("Character Event Test", charEventTest); + registerTest("Native Integration Test", nativeIntegrationTest); + registerTest("Native Instance Test", nativeInstanceTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - // registerTest("Opengl Test", openglTest); - // registerTest("Opengl FBConfig Test", openglFBConfigTest); - // registerTest("Opengl Context Test", openglContextTest); - // registerTest("Opengl Multi Context Test", openglMultiContextTest); + registerTest("Opengl Test", openglTest); + registerTest("Opengl FBConfig Test", openglFBConfigTest); + registerTest("Opengl Context Test", openglContextTest); + registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); From 22c28924e837e3a5d8bf927f97c31a9e0263149e Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 15:51:06 +0000 Subject: [PATCH 28/51] format source --- src/opengl/pal_opengl_linux.c | 23 +- src/opengl/pal_opengl_win32.c | 4 +- src/video/pal_video_linux.c | 902 ++++++++++++++++------------------ 3 files changed, 453 insertions(+), 476 deletions(-) diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index efaf545..7c5dc92 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -203,8 +203,13 @@ typedef EGLSurface (*eglCreateWindowSurfaceFn)( const EGLint*); typedef const GLubyte* (*glGetStringFn)(GLenum); -typedef void(PAL_GL_APIENTRY* glClearColorFn)(float, float, float, float); -typedef void(PAL_GL_APIENTRY* glClearFn)(Uint32); +typedef void(PAL_GL_APIENTRY* glClearFn)(Uint32); + +typedef void(PAL_GL_APIENTRY* glClearColorFn)( + float, + float, + float, + float); typedef struct { bool used; @@ -681,7 +686,7 @@ void PAL_CALL palShutdownGL() if (s_GL.display) { s_GL.eglTerminate(s_GL.display); } - + dlclose(s_GL.handle); s_GL.initialized = false; } @@ -762,7 +767,7 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( if (s_GL.apiType == EGL_OPENGL_ES3_BIT) { // EGL_OPENGL_ES2_BIT - if (!(renderable & EGL_OPENGL_ES2_BIT) && + if (!(renderable & EGL_OPENGL_ES2_BIT) && !(renderable & EGL_OPENGL_ES3_BIT)) { continue; } @@ -1039,7 +1044,7 @@ PalResult PAL_CALL palCreateGLContext( attribs[index++] = EGL_CONTEXT_MINOR_VERSION_KHR; attribs[index++] = info->minor; - + // set profile mask if (info->profile != PAL_GL_PROFILE_NONE) { attribs[index++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; @@ -1182,9 +1187,9 @@ void PAL_CALL palDestroyGLContext(PalGLContext* context) if (data) { // make it not current if it was current s_GL.eglMakeCurrent( - s_GL.display, - EGL_NO_SURFACE, - EGL_NO_SURFACE, + s_GL.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); s_GL.eglDestroyContext(s_GL.display, (EGLContext)context); @@ -1305,7 +1310,7 @@ void PAL_CALL palGLSetInstance(void* instance) s_GL.platformDisplay = instance; } -const char* PAL_CALL palGLGetBackend() +const char* PAL_CALL palGLGetBackend() { if (!s_GL.initialized) { return nullptr; diff --git a/src/opengl/pal_opengl_win32.c b/src/opengl/pal_opengl_win32.c index b34f688..f4a23f7 100644 --- a/src/opengl/pal_opengl_win32.c +++ b/src/opengl/pal_opengl_win32.c @@ -257,7 +257,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) if (!s_Wgl.instance) { s_Wgl.instance = GetModuleHandleW(nullptr); } - + // register class WNDCLASSEXW wc = {0}; wc.style = CS_OWNDC; @@ -1046,7 +1046,7 @@ void PAL_CALL palGLSetInstance(void* instance) s_Wgl.instance = instance; } -const char* PAL_CALL palGLGetBackend() +const char* PAL_CALL palGLGetBackend() { if (!s_Wgl.initialized) { return nullptr; diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 4e7aa09..3866e8b 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -50,15 +50,15 @@ freely, subject to the following restrictions: #include #include -#include -#include #include -#include #include +#include +#include +#include #include -#include #include +#include #include #endif // PAL_HAS_WAYLAND @@ -139,7 +139,7 @@ typedef struct { PalWindow* window; // X11 only - XIC ic; + XIC ic; Colormap colormap; // Wayland only @@ -1961,80 +1961,77 @@ struct xdg_toplevel_listener { }; static inline void xdgWmBasePong( - struct xdg_wm_base *xdg_wm_base, + struct xdg_wm_base* xdg_wm_base, uint32_t serial) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_wm_base, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_wm_base, 3, // XDG_WM_BASE_PONG - NULL, - s_Wl.proxyGetVersion(( - struct wl_proxy *) xdg_wm_base), - 0, - serial); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_wm_base), + 0, + serial); } static inline int xdgWmBaseAddListener( - struct xdg_wm_base *xdg_wm_base, - const struct xdg_wm_base_listener *listener, - void *data) + struct xdg_wm_base* xdg_wm_base, + const struct xdg_wm_base_listener* listener, + void* data) { - return s_Wl.proxyAddListener( - (struct wl_proxy *) xdg_wm_base, - (void (**)(void)) listener, data); + return s_Wl.proxyAddListener( + (struct wl_proxy*)xdg_wm_base, + (void (**)(void))listener, + data); } static inline struct xdg_surface* xdgWmBaseGetXdgSurface( - struct xdg_wm_base *xdg_wm_base, - struct wl_surface *surface) + struct xdg_wm_base* xdg_wm_base, + struct wl_surface* surface) { - struct wl_proxy *id; - id = s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_wm_base, - 2, // XDG_WM_BASE_GET_XDG_SURFACE, - &xdg_surface_interface, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_wm_base), - 0, - NULL, - surface); + struct wl_proxy* id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_wm_base, + 2, // XDG_WM_BASE_GET_XDG_SURFACE, + &xdg_surface_interface, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_wm_base), + 0, + NULL, + surface); - return (struct xdg_surface *) id; + return (struct xdg_surface*)id; } -static inline struct xdg_toplevel* xdgSurfaceGetToplevel( - struct xdg_surface *xdg_surface) +static inline struct xdg_toplevel* +xdgSurfaceGetToplevel(struct xdg_surface* xdg_surface) { - struct wl_proxy *id; - id = s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_surface, - 1, // XDG_SURFACE_GET_TOPLEVEL, - &xdg_toplevel_interface, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_surface), - 0, - NULL); + struct wl_proxy* id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_surface, + 1, // XDG_SURFACE_GET_TOPLEVEL, + &xdg_toplevel_interface, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_surface), + 0, + NULL); - return (struct xdg_toplevel *) id; + return (struct xdg_toplevel*)id; } static inline void xdgSurfaceAckConfigure( - struct xdg_surface *xdg_surface, + struct xdg_surface* xdg_surface, uint32_t serial) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_surface, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_surface, 4, // XDG_SURFACE_ACK_CONFIGURE - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_surface), - 0, - serial); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_surface), + 0, + serial); } static void wmBaseHandlePing( - void* data, - struct xdg_wm_base* base, + void* data, + struct xdg_wm_base* base, uint32_t serial) { xdgWmBasePong(base, serial); @@ -2054,12 +2051,12 @@ static void xdgSurfaceHandleConfigure( if (winData->eglWindow) { s_Wl.eglWindowResize( - winData->eglWindow, - winData->w, - winData->h, - 0, + winData->eglWindow, + winData->w, + winData->h, + 0, 0); - + } else { // create a new buffer with the new size struct wl_buffer* buffer = nullptr; @@ -2075,7 +2072,6 @@ static void xdgSurfaceHandleConfigure( wlSurfaceDamageBuffer(_surface, 0, 0, winData->w, winData->h); wlSurfaceCommit(_surface); - // destroy old buffer wlBufferDestroy(winData->buffer); winData->buffer = buffer; @@ -2129,7 +2125,8 @@ static void xdgToplevelHandleConfigure( if (!winData->skipState) { uint32_t* state; - wl_array_for_each(state, states) { + wl_array_for_each(state, states) + { // we need only maximized if (*state == 1) { // XDG_TOPLEVEL_STATE_MAXIMIZED if (winData->state != PAL_WINDOW_STATE_MAXIMIZED) { @@ -2147,7 +2144,6 @@ static void xdgToplevelHandleConfigure( winData->skipConfigure = false; } } - } static void xdgToplevelHandleClose( @@ -2170,288 +2166,293 @@ static void xdgToplevelHandleClose( } static inline int xdgSurfaceAddListener( - struct xdg_surface *xdg_surface, - const struct xdg_surface_listener *listener, - void *data) + struct xdg_surface* xdg_surface, + const struct xdg_surface_listener* listener, + void* data) { - return s_Wl.proxyAddListener( - (struct wl_proxy *) xdg_surface, - (void (**)(void)) listener, data); + return s_Wl.proxyAddListener( + (struct wl_proxy*)xdg_surface, + (void (**)(void))listener, + data); } -static inline void xdgSurfaceDestroy(struct xdg_surface *xdg_surface) +static inline void xdgSurfaceDestroy(struct xdg_surface* xdg_surface) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_surface, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_surface, 0, // XDG_SURFACE_DESTROY - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_surface), - WL_MARSHAL_FLAG_DESTROY); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_surface), + WL_MARSHAL_FLAG_DESTROY); } -static inline void xdgToplevelDestroy(struct xdg_toplevel *xdg_toplevel) +static inline void xdgToplevelDestroy(struct xdg_toplevel* xdg_toplevel) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, 0, // XDG_TOPLEVEL_DESTROY - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - WL_MARSHAL_FLAG_DESTROY); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + WL_MARSHAL_FLAG_DESTROY); } static inline void xdgToplevelSetTitle( - struct xdg_toplevel *xdg_toplevel, - const char *title) + struct xdg_toplevel* xdg_toplevel, + const char* title) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, 2, // XDG_TOPLEVEL_SET_TITLE - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0, - title); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0, + title); } -static inline void xdgToplevelSetMaximized(struct xdg_toplevel *xdg_toplevel) +static inline void xdgToplevelSetMaximized(struct xdg_toplevel* xdg_toplevel) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, - 9, //XDG_TOPLEVEL_SET_MAXIMIZED - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0); + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, + 9, // XDG_TOPLEVEL_SET_MAXIMIZED + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0); } -static inline void xdgToplevelSetMinimized(struct xdg_toplevel *xdg_toplevel) +static inline void xdgToplevelSetMinimized(struct xdg_toplevel* xdg_toplevel) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, 13, // XDG_TOPLEVEL_SET_MINIMIZED - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0); } static inline int xdgToplevelAddListener( - struct xdg_toplevel *xdg_toplevel, - const struct xdg_toplevel_listener *listener, - void *data) + struct xdg_toplevel* xdg_toplevel, + const struct xdg_toplevel_listener* listener, + void* data) { - return s_Wl.proxyAddListener( - (struct wl_proxy *) xdg_toplevel, - (void (**)(void)) listener, data); + return s_Wl.proxyAddListener( + (struct wl_proxy*)xdg_toplevel, + (void (**)(void))listener, + data); } static inline void xdgToplevelSetMinSize( - struct xdg_toplevel *xdg_toplevel, - int32_t width, + struct xdg_toplevel* xdg_toplevel, + int32_t width, int32_t height) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, - 8, //XDG_TOPLEVEL_SET_MIN_SIZE - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0, - width, - height); + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, + 8, // XDG_TOPLEVEL_SET_MIN_SIZE + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0, + width, + height); } static inline void xdgToplevelSetMaxSize( - struct xdg_toplevel *xdg_toplevel, - int32_t width, + struct xdg_toplevel* xdg_toplevel, + int32_t width, int32_t height) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, - 7, //XDG_TOPLEVEL_SET_MAX_SIZE - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0, - width, - height); + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, + 7, // XDG_TOPLEVEL_SET_MAX_SIZE + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0, + width, + height); } static inline void xdgToplevelSetAppId( - struct xdg_toplevel *xdg_toplevel, - const char *app_id) + struct xdg_toplevel* xdg_toplevel, + const char* app_id) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, 3, // XDG_TOPLEVEL_SET_APP_ID - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0, - app_id); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0, + app_id); } -static inline void xdgToplevelUnsetMaximized(struct xdg_toplevel *xdg_toplevel) +static inline void xdgToplevelUnsetMaximized(struct xdg_toplevel* xdg_toplevel) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) xdg_toplevel, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)xdg_toplevel, 10, // XDG_TOPLEVEL_UNSET_MAXIMIZED - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) xdg_toplevel), - 0); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_toplevel), + 0); } -static const struct wl_interface *xdg_shell_types[26]; +static const struct wl_interface* xdg_shell_types[26]; static const struct wl_message xdg_wm_base_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "create_positioner", "n", xdg_shell_types + 4 }, - { "get_xdg_surface", "no", xdg_shell_types + 5 }, - { "pong", "u", xdg_shell_types + 0 }, + {"destroy", "", xdg_shell_types + 0}, + {"create_positioner", "n", xdg_shell_types + 4}, + {"get_xdg_surface", "no", xdg_shell_types + 5}, + {"pong", "u", xdg_shell_types + 0}, }; static const struct wl_message xdg_wm_base_events[] = { - { "ping", "u", xdg_shell_types + 0 }, + {"ping", "u", xdg_shell_types + 0}, }; const struct wl_interface xdg_wm_base_interface = { - "xdg_wm_base", 6, - 4, xdg_wm_base_requests, - 1, xdg_wm_base_events, + "xdg_wm_base", + 6, + 4, + xdg_wm_base_requests, + 1, + xdg_wm_base_events, }; static const struct wl_message xdg_positioner_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "set_size", "ii", xdg_shell_types + 0 }, - { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, - { "set_anchor", "u", xdg_shell_types + 0 }, - { "set_gravity", "u", xdg_shell_types + 0 }, - { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, - { "set_offset", "ii", xdg_shell_types + 0 }, - { "set_reactive", "3", xdg_shell_types + 0 }, - { "set_parent_size", "3ii", xdg_shell_types + 0 }, - { "set_parent_configure", "3u", xdg_shell_types + 0 }, + {"destroy", "", xdg_shell_types + 0}, + {"set_size", "ii", xdg_shell_types + 0}, + {"set_anchor_rect", "iiii", xdg_shell_types + 0}, + {"set_anchor", "u", xdg_shell_types + 0}, + {"set_gravity", "u", xdg_shell_types + 0}, + {"set_constraint_adjustment", "u", xdg_shell_types + 0}, + {"set_offset", "ii", xdg_shell_types + 0}, + {"set_reactive", "3", xdg_shell_types + 0}, + {"set_parent_size", "3ii", xdg_shell_types + 0}, + {"set_parent_configure", "3u", xdg_shell_types + 0}, }; const struct wl_interface xdg_positioner_interface = { - "xdg_positioner", 6, - 10, xdg_positioner_requests, - 0, NULL, + "xdg_positioner", + 6, + 10, + xdg_positioner_requests, + 0, + NULL, }; static const struct wl_message xdg_surface_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "get_toplevel", "n", xdg_shell_types + 7 }, - { "get_popup", "n?oo", xdg_shell_types + 8 }, - { "set_window_geometry", "iiii", xdg_shell_types + 0 }, - { "ack_configure", "u", xdg_shell_types + 0 }, + {"destroy", "", xdg_shell_types + 0}, + {"get_toplevel", "n", xdg_shell_types + 7}, + {"get_popup", "n?oo", xdg_shell_types + 8}, + {"set_window_geometry", "iiii", xdg_shell_types + 0}, + {"ack_configure", "u", xdg_shell_types + 0}, }; static const struct wl_message xdg_surface_events[] = { - { "configure", "u", xdg_shell_types + 0 }, + {"configure", "u", xdg_shell_types + 0}, }; const struct wl_interface xdg_surface_interface = { - "xdg_surface", 6, - 5, xdg_surface_requests, - 1, xdg_surface_events, + "xdg_surface", + 6, + 5, + xdg_surface_requests, + 1, + xdg_surface_events, }; static const struct wl_message xdg_toplevel_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "set_parent", "?o", xdg_shell_types + 11 }, - { "set_title", "s", xdg_shell_types + 0 }, - { "set_app_id", "s", xdg_shell_types + 0 }, - { "show_window_menu", "ouii", xdg_shell_types + 12 }, - { "move", "ou", xdg_shell_types + 16 }, - { "resize", "ouu", xdg_shell_types + 18 }, - { "set_max_size", "ii", xdg_shell_types + 0 }, - { "set_min_size", "ii", xdg_shell_types + 0 }, - { "set_maximized", "", xdg_shell_types + 0 }, - { "unset_maximized", "", xdg_shell_types + 0 }, - { "set_fullscreen", "?o", xdg_shell_types + 21 }, - { "unset_fullscreen", "", xdg_shell_types + 0 }, - { "set_minimized", "", xdg_shell_types + 0 }, + {"destroy", "", xdg_shell_types + 0}, + {"set_parent", "?o", xdg_shell_types + 11}, + {"set_title", "s", xdg_shell_types + 0}, + {"set_app_id", "s", xdg_shell_types + 0}, + {"show_window_menu", "ouii", xdg_shell_types + 12}, + {"move", "ou", xdg_shell_types + 16}, + {"resize", "ouu", xdg_shell_types + 18}, + {"set_max_size", "ii", xdg_shell_types + 0}, + {"set_min_size", "ii", xdg_shell_types + 0}, + {"set_maximized", "", xdg_shell_types + 0}, + {"unset_maximized", "", xdg_shell_types + 0}, + {"set_fullscreen", "?o", xdg_shell_types + 21}, + {"unset_fullscreen", "", xdg_shell_types + 0}, + {"set_minimized", "", xdg_shell_types + 0}, }; static const struct wl_message xdg_toplevel_events[] = { - { "configure", "iia", xdg_shell_types + 0 }, - { "close", "", xdg_shell_types + 0 }, - { "configure_bounds", "4ii", xdg_shell_types + 0 }, - { "wm_capabilities", "5a", xdg_shell_types + 0 }, + {"configure", "iia", xdg_shell_types + 0}, + {"close", "", xdg_shell_types + 0}, + {"configure_bounds", "4ii", xdg_shell_types + 0}, + {"wm_capabilities", "5a", xdg_shell_types + 0}, }; const struct wl_interface xdg_toplevel_interface = { - "xdg_toplevel", 6, - 14, xdg_toplevel_requests, - 4, xdg_toplevel_events, + "xdg_toplevel", + 6, + 14, + xdg_toplevel_requests, + 4, + xdg_toplevel_events, }; static const struct wl_message xdg_popup_requests[] = { - { "destroy", "", xdg_shell_types + 0 }, - { "grab", "ou", xdg_shell_types + 22 }, - { "reposition", "3ou", xdg_shell_types + 24 }, + {"destroy", "", xdg_shell_types + 0}, + {"grab", "ou", xdg_shell_types + 22}, + {"reposition", "3ou", xdg_shell_types + 24}, }; static const struct wl_message xdg_popup_events[] = { - { "configure", "iiii", xdg_shell_types + 0 }, - { "popup_done", "", xdg_shell_types + 0 }, - { "repositioned", "3u", xdg_shell_types + 0 }, + {"configure", "iiii", xdg_shell_types + 0}, + {"popup_done", "", xdg_shell_types + 0}, + {"repositioned", "3u", xdg_shell_types + 0}, }; const struct wl_interface xdg_popup_interface = { - "xdg_popup", 6, - 3, xdg_popup_requests, - 3, xdg_popup_events, + "xdg_popup", + 6, + 3, + xdg_popup_requests, + 3, + xdg_popup_events, }; static void setupXdgShellProtocol() { - xdg_shell_types[0] = NULL; - xdg_shell_types[1] = NULL; - xdg_shell_types[2] = NULL; - xdg_shell_types[3] = NULL; - xdg_shell_types[4] = &xdg_positioner_interface; - xdg_shell_types[5] = &xdg_surface_interface; - xdg_shell_types[6] = s_Wl.surfaceInterface; - xdg_shell_types[7] = &xdg_toplevel_interface; - xdg_shell_types[8] = &xdg_popup_interface; - xdg_shell_types[9] = &xdg_surface_interface; - xdg_shell_types[10] = &xdg_positioner_interface; - xdg_shell_types[11] = &xdg_toplevel_interface; - xdg_shell_types[12] = s_Wl.seatInterface; - xdg_shell_types[13] = NULL; - xdg_shell_types[14] = NULL; - xdg_shell_types[15] = NULL; - xdg_shell_types[16] = s_Wl.seatInterface; - xdg_shell_types[17] = NULL; - xdg_shell_types[18] = s_Wl.seatInterface; - xdg_shell_types[19] = NULL; - xdg_shell_types[20] = NULL; - xdg_shell_types[21] = s_Wl.outputInterface; - xdg_shell_types[22] = s_Wl.seatInterface; - xdg_shell_types[23] = NULL; - xdg_shell_types[24] = &xdg_positioner_interface; - xdg_shell_types[25] = NULL; + xdg_shell_types[0] = NULL; + xdg_shell_types[1] = NULL; + xdg_shell_types[2] = NULL; + xdg_shell_types[3] = NULL; + xdg_shell_types[4] = &xdg_positioner_interface; + xdg_shell_types[5] = &xdg_surface_interface; + xdg_shell_types[6] = s_Wl.surfaceInterface; + xdg_shell_types[7] = &xdg_toplevel_interface; + xdg_shell_types[8] = &xdg_popup_interface; + xdg_shell_types[9] = &xdg_surface_interface; + xdg_shell_types[10] = &xdg_positioner_interface; + xdg_shell_types[11] = &xdg_toplevel_interface; + xdg_shell_types[12] = s_Wl.seatInterface; + xdg_shell_types[13] = NULL; + xdg_shell_types[14] = NULL; + xdg_shell_types[15] = NULL; + xdg_shell_types[16] = s_Wl.seatInterface; + xdg_shell_types[17] = NULL; + xdg_shell_types[18] = s_Wl.seatInterface; + xdg_shell_types[19] = NULL; + xdg_shell_types[20] = NULL; + xdg_shell_types[21] = s_Wl.outputInterface; + xdg_shell_types[22] = s_Wl.seatInterface; + xdg_shell_types[23] = NULL; + xdg_shell_types[24] = &xdg_positioner_interface; + xdg_shell_types[25] = NULL; } static const struct xdg_wm_base_listener wmBaseListener = { - .ping = wmBaseHandlePing -}; + .ping = wmBaseHandlePing}; static const struct xdg_surface_listener xdgSurfaceListener = { - .configure = xdgSurfaceHandleConfigure -}; + .configure = xdgSurfaceHandleConfigure}; static const struct xdg_toplevel_listener xdgToplevelListener = { .configure = xdgToplevelHandleConfigure, .close = xdgToplevelHandleClose, .configure_bounds = nullptr, - .wm_capabilities = nullptr -}; + .wm_capabilities = nullptr}; #endif // PAL_HAS_WAYLAND #pragma endregion @@ -2464,7 +2465,7 @@ struct zxdg_decoration_manager_v1; struct zxdg_toplevel_decoration_v1; struct zxdg_toplevel_decoration_v1_listener { - void (*configure)( + void (*configure)( void*, struct zxdg_toplevel_decoration_v1*, uint32_t); @@ -2474,105 +2475,108 @@ const struct wl_interface zxdg_decoration_manager_v1_interface; const struct wl_interface zxdg_toplevel_decoration_v1_interface; static inline void zxdgDecorationManagerV1Destroy( - struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) + struct zxdg_decoration_manager_v1* zxdg_decoration_manager_v1) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) zxdg_decoration_manager_v1, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)zxdg_decoration_manager_v1, 0, // ZXDG_DECORATION_MANAGER_V1_DESTROY - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) zxdg_decoration_manager_v1), - WL_MARSHAL_FLAG_DESTROY); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)zxdg_decoration_manager_v1), + WL_MARSHAL_FLAG_DESTROY); } static inline struct zxdg_toplevel_decoration_v1* zxdgGetTopleveDecoration( - struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, - struct xdg_toplevel *toplevel) + struct zxdg_decoration_manager_v1* zxdg_decoration_manager_v1, + struct xdg_toplevel* toplevel) { - struct wl_proxy *id; - id = s_Wl.proxyMarshalFlags( - (struct wl_proxy *) zxdg_decoration_manager_v1, + struct wl_proxy* id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy*)zxdg_decoration_manager_v1, 1, // ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION, &zxdg_toplevel_decoration_v1_interface, - s_Wl.proxyGetVersion( - (struct wl_proxy *) zxdg_decoration_manager_v1), - 0, - NULL, - toplevel); + s_Wl.proxyGetVersion((struct wl_proxy*)zxdg_decoration_manager_v1), + 0, + NULL, + toplevel); - return (struct zxdg_toplevel_decoration_v1 *) id; + return (struct zxdg_toplevel_decoration_v1*)id; } static inline int zxdgToplevelDecorationV1AddListener( - struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, - const struct zxdg_toplevel_decoration_v1_listener *listener, - void *data) + struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1, + const struct zxdg_toplevel_decoration_v1_listener* listener, + void* data) { - return s_Wl.proxyAddListener( - (struct wl_proxy *) zxdg_toplevel_decoration_v1, - (void (**)(void)) listener, data); + return s_Wl.proxyAddListener( + (struct wl_proxy*)zxdg_toplevel_decoration_v1, + (void (**)(void))listener, + data); } static inline void zxdgToplevelDecorationV1Destroy( - struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) + struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) zxdg_toplevel_decoration_v1, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)zxdg_toplevel_decoration_v1, 0, // ZXDG_TOPLEVEL_DECORATION_V1_DESTROY - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) zxdg_toplevel_decoration_v1), - WL_MARSHAL_FLAG_DESTROY); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)zxdg_toplevel_decoration_v1), + WL_MARSHAL_FLAG_DESTROY); } static inline void zxdgToplevelDecorationV1SetMode( - struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1, uint32_t mode) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) zxdg_toplevel_decoration_v1, - 1, // ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) zxdg_toplevel_decoration_v1), - 0, - mode); + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)zxdg_toplevel_decoration_v1, + 1, // ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)zxdg_toplevel_decoration_v1), + 0, + mode); } -static const struct wl_interface *xdg_decoration_unstable_v1_types[] = { - NULL, - &zxdg_toplevel_decoration_v1_interface, - &xdg_toplevel_interface, +static const struct wl_interface* xdg_decoration_unstable_v1_types[] = { + NULL, + &zxdg_toplevel_decoration_v1_interface, + &xdg_toplevel_interface, }; static const struct wl_message zxdg_decoration_manager_v1_requests[] = { - { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, - { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 }, + {"destroy", "", xdg_decoration_unstable_v1_types + 0}, + {"get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1}, }; const struct wl_interface zxdg_decoration_manager_v1_interface = { - "zxdg_decoration_manager_v1", 1, - 2, zxdg_decoration_manager_v1_requests, - 0, NULL, + "zxdg_decoration_manager_v1", + 1, + 2, + zxdg_decoration_manager_v1_requests, + 0, + NULL, }; static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { - { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, - { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 }, - { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 }, + {"destroy", "", xdg_decoration_unstable_v1_types + 0}, + {"set_mode", "u", xdg_decoration_unstable_v1_types + 0}, + {"unset_mode", "", xdg_decoration_unstable_v1_types + 0}, }; static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { - { "configure", "u", xdg_decoration_unstable_v1_types + 0 }, + {"configure", "u", xdg_decoration_unstable_v1_types + 0}, }; const struct wl_interface zxdg_toplevel_decoration_v1_interface = { - "zxdg_toplevel_decoration_v1", 1, - 3, zxdg_toplevel_decoration_v1_requests, - 1, zxdg_toplevel_decoration_v1_events, + "zxdg_toplevel_decoration_v1", + 1, + 3, + zxdg_toplevel_decoration_v1_requests, + 1, + zxdg_toplevel_decoration_v1_events, }; -#endif //PAL_HAS_WAYLAND +#endif // PAL_HAS_WAYLAND #pragma endregion #pragma region Zwp-Pointer-Constraints @@ -2584,86 +2588,95 @@ struct zwp_pointer_constraints_v1; const struct wl_interface zwp_confined_pointer_v1_interface; static inline struct zwp_confined_pointer_v1* zwpPointerConstraintsConfine( - struct zwp_pointer_constraints_v1 *zwp_pointer_constraints_v1, - struct wl_surface *surface, - struct wl_pointer *pointer, - struct wl_region *region, + struct zwp_pointer_constraints_v1* zwp_pointer_constraints_v1, + struct wl_surface* surface, + struct wl_pointer* pointer, + struct wl_region* region, uint32_t lifetime) { - struct wl_proxy *id; - id = s_Wl.proxyMarshalFlags( - (struct wl_proxy *) zwp_pointer_constraints_v1, + struct wl_proxy* id; + id = s_Wl.proxyMarshalFlags( + (struct wl_proxy*)zwp_pointer_constraints_v1, 2, // ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER - &zwp_confined_pointer_v1_interface, - s_Wl.proxyGetVersion( - (struct wl_proxy *) zwp_pointer_constraints_v1), - 0, - NULL, - surface, - pointer, - region, - lifetime); + &zwp_confined_pointer_v1_interface, + s_Wl.proxyGetVersion((struct wl_proxy*)zwp_pointer_constraints_v1), + 0, + NULL, + surface, + pointer, + region, + lifetime); - return (struct zwp_confined_pointer_v1 *) id; + return (struct zwp_confined_pointer_v1*)id; } static inline void zwpConfinedPointerV1Destroy( - struct zwp_confined_pointer_v1 *zwp_confined_pointer_v1) + struct zwp_confined_pointer_v1* zwp_confined_pointer_v1) { - s_Wl.proxyMarshalFlags( - (struct wl_proxy *) zwp_confined_pointer_v1, + s_Wl.proxyMarshalFlags( + (struct wl_proxy*)zwp_confined_pointer_v1, 0, // ZWP_CONFINED_POINTER_V1_DESTROY - NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy *) zwp_confined_pointer_v1), - WL_MARSHAL_FLAG_DESTROY); + NULL, + s_Wl.proxyGetVersion((struct wl_proxy*)zwp_confined_pointer_v1), + WL_MARSHAL_FLAG_DESTROY); } -static const struct wl_interface *pointer_constraints_unstable_v1_types[14]; +static const struct wl_interface* pointer_constraints_unstable_v1_types[14]; static const struct wl_message zwp_pointer_constraints_v1_requests[] = { - { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, - { "lock_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 2 }, - { "confine_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 7 }, + {"destroy", "", pointer_constraints_unstable_v1_types + 0}, + {"lock_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 2}, + {"confine_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 7}, }; const struct wl_interface zwp_pointer_constraints_v1_interface = { - "zwp_pointer_constraints_v1", 1, - 3, zwp_pointer_constraints_v1_requests, - 0, NULL, + "zwp_pointer_constraints_v1", + 1, + 3, + zwp_pointer_constraints_v1_requests, + 0, + NULL, }; static const struct wl_message zwp_locked_pointer_v1_requests[] = { - { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, - { "set_cursor_position_hint", "ff", pointer_constraints_unstable_v1_types + 0 }, - { "set_region", "?o", pointer_constraints_unstable_v1_types + 12 }, + {"destroy", "", pointer_constraints_unstable_v1_types + 0}, + {"set_cursor_position_hint", + "ff", + pointer_constraints_unstable_v1_types + 0}, + {"set_region", "?o", pointer_constraints_unstable_v1_types + 12}, }; static const struct wl_message zwp_locked_pointer_v1_events[] = { - { "locked", "", pointer_constraints_unstable_v1_types + 0 }, - { "unlocked", "", pointer_constraints_unstable_v1_types + 0 }, + {"locked", "", pointer_constraints_unstable_v1_types + 0}, + {"unlocked", "", pointer_constraints_unstable_v1_types + 0}, }; const struct wl_interface zwp_locked_pointer_v1_interface = { - "zwp_locked_pointer_v1", 1, - 3, zwp_locked_pointer_v1_requests, - 2, zwp_locked_pointer_v1_events, + "zwp_locked_pointer_v1", + 1, + 3, + zwp_locked_pointer_v1_requests, + 2, + zwp_locked_pointer_v1_events, }; static const struct wl_message zwp_confined_pointer_v1_requests[] = { - { "destroy", "", pointer_constraints_unstable_v1_types + 0 }, - { "set_region", "?o", pointer_constraints_unstable_v1_types + 13 }, + {"destroy", "", pointer_constraints_unstable_v1_types + 0}, + {"set_region", "?o", pointer_constraints_unstable_v1_types + 13}, }; static const struct wl_message zwp_confined_pointer_v1_events[] = { - { "confined", "", pointer_constraints_unstable_v1_types + 0 }, - { "unconfined", "", pointer_constraints_unstable_v1_types + 0 }, + {"confined", "", pointer_constraints_unstable_v1_types + 0}, + {"unconfined", "", pointer_constraints_unstable_v1_types + 0}, }; const struct wl_interface zwp_confined_pointer_v1_interface = { - "zwp_confined_pointer_v1", 1, - 2, zwp_confined_pointer_v1_requests, - 2, zwp_confined_pointer_v1_events, + "zwp_confined_pointer_v1", + 1, + 2, + zwp_confined_pointer_v1_requests, + 2, + zwp_confined_pointer_v1_events, }; static void setupZwpPointerProtocol() @@ -3275,7 +3288,7 @@ static void xCacheMonitors() // get DPI float raw = crtc->width / 1920.0f; - float steps[] = { 1.0f, 1.2f, 1.5f, 1.75, 2.0f }; + float steps[] = {1.0f, 1.2f, 1.5f, 1.75, 2.0f}; float closest = steps[0]; float minDiff = fabsf(raw - steps[0]); @@ -4428,7 +4441,7 @@ static PalResult xGetMonitorInfo( // get dpi float raw = crtc->width / 1920.0f; - float steps[] = { 1.0f, 1.2f, 1.5f, 1.75, 2.0f }; + float steps[] = {1.0f, 1.2f, 1.5f, 1.75, 2.0f}; float closest = steps[0]; float minDiff = fabsf(raw - steps[0]); @@ -4756,11 +4769,14 @@ static PalResult xCreateWindow( bgPixel = 0; borderPixel = 0; + // clang-format off + colormap = s_X11.createColormap( - s_X11.display, - s_X11.root, - visual, + s_X11.display, + s_X11.root, + visual, AllocNone); + // clang-format on if (!colormap) { return PAL_RESULT_PLATFORM_FAILURE; @@ -4817,7 +4833,7 @@ static PalResult xCreateWindow( FROM_PAL_HANDLE(RROutput, monitor)); // check if its a monitor - if (outputInfo->connection != RR_Connected || + if (outputInfo->connection != RR_Connected || outputInfo->crtc == None) { s_X11.freeOutputInfo(outputInfo); continue; @@ -4837,7 +4853,7 @@ static PalResult xCreateWindow( // get DPI float raw = crtc->width / 1920.0f; - float steps[] = { 1.0f, 1.2f, 1.5f, 1.75, 2.0f }; + float steps[] = {1.0f, 1.2f, 1.5f, 1.75, 2.0f}; float closest = steps[0]; float minDiff = fabsf(raw - steps[0]); @@ -5094,19 +5110,16 @@ static PalResult xCreateWindow( s_X11.iconifyWindow(s_X11.display, window, s_X11.screen); } - s_X11.setWMProtocols( - s_X11.display, - window, - &s_X11Atoms.WM_DELETE_WINDOW, - 1); + s_X11 + .setWMProtocols(s_X11.display, window, &s_X11Atoms.WM_DELETE_WINDOW, 1); s_X11.flush(s_X11.display); // attach the window data to the window data->skipConfigure = true; data->skipState = true; - data->isAttached = false; // true for attached windows - data->dpi = dpi; // the current window monitor + data->isAttached = false; // true for attached windows + data->dpi = dpi; // the current window monitor data->window = TO_PAL_HANDLE(PalWindow, window); s_X11.saveContext(s_X11.display, window, s_X11.dataID, (XPointer)data); @@ -5146,7 +5159,7 @@ static void xDestroyWindow(PalWindow* window) if (data->colormap != None) { s_X11.freeColormap(s_X11.display, data->colormap); } - + data->used = false; } @@ -6083,7 +6096,7 @@ PalResult eglWlBackend(const int index) s_Egl.eglGetConfigs(display, eglConfigs, numConfigs, &numConfigs); s_Wl.eglFBConfig = eglConfigs[index]; - + return PAL_RESULT_SUCCESS; } @@ -6136,7 +6149,7 @@ static void wlCreateKeycodeTable() s_Keyboard.keycodes[XKB_KEY_bracketright] = PAL_KEYCODE_RBRACKET; } -static int createShmFile(Uint64 size) +static int createShmFile(Uint64 size) { char template[] = "/tmp/pal-shm-XXXXXX"; int fd = mkstemp(template); @@ -6156,7 +6169,7 @@ static struct wl_buffer* createShmBuffer( int width, int height, const Uint8* pixels, - bool cursor) + bool cursor) { int stride = width * 4; Uint64 size = stride * height; @@ -6203,13 +6216,7 @@ static struct wl_buffer* createShmBuffer( return nullptr; } - buffer = wlShmPoolCreateBuffer( - pool, - 0, - width, - height, - stride, - format); + buffer = wlShmPoolCreateBuffer(pool, 0, width, height, stride, format); if (!buffer) { return nullptr; @@ -6222,7 +6229,7 @@ static struct wl_buffer* createShmBuffer( } static void globalHandle( - void* data, + void* data, struct wl_registry* registry, uint32_t name, const char* interface, @@ -6257,41 +6264,27 @@ static void globalHandle( } if (strcmp(interface, "wl_compositor") == 0) { - s_Wl.compositor = wlRegistryBind( - registry, - name, - s_Wl.compositorInterface, - 4); + s_Wl.compositor = + wlRegistryBind(registry, name, s_Wl.compositorInterface, 4); } else if (strcmp(interface, "xdg_wm_base") == 0) { - s_Wl.xdgBase = wlRegistryBind( - registry, - name, - &xdg_wm_base_interface, - 1); + s_Wl.xdgBase = + wlRegistryBind(registry, name, &xdg_wm_base_interface, 1); xdgWmBaseAddListener(s_Wl.xdgBase, &wmBaseListener, nullptr); } else if (strcmp(interface, "wl_shm") == 0) { - s_Wl.shm = wlRegistryBind( - registry, - name, - s_Wl.shmInterface, - 1); + s_Wl.shm = wlRegistryBind(registry, name, s_Wl.shmInterface, 1); } else if (strcmp(interface, "wl_seat") == 0) { - s_Wl.seat = wlRegistryBind( - registry, - name, - s_Wl.seatInterface, - 5); + s_Wl.seat = wlRegistryBind(registry, name, s_Wl.seatInterface, 5); - wlSeatAddListener(s_Wl.seat , &seatListener, nullptr); + wlSeatAddListener(s_Wl.seat, &seatListener, nullptr); } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { s_Wl.decorationManager = wlRegistryBind( - registry, - name, + registry, + name, &zxdg_decoration_manager_v1_interface, 1); @@ -6299,21 +6292,20 @@ static void globalHandle( } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { s_Wl.pointerConstraints = wlRegistryBind( - registry, - name, + registry, + name, &zwp_pointer_constraints_v1_interface, 1); - } else if (strcmp(interface, "wl_output") == 0) { // wayland does not let use query monitors directly - // so we enumerate and store at init and update the + // so we enumerate and store at init and update the // cache when a monitor is added or removed MonitorData* monitorData = getFreeMonitorData(); if (!monitorData) { return; } - + PalMonitor* m = TO_PAL_HANDLE(PalMonitor, name); monitorData->monitor = m; s_Wl.monitorCount++; @@ -6321,7 +6313,7 @@ static void globalHandle( } static void globalRemove( - void* data, + void* data, struct wl_registry* registry, uint32_t name) { @@ -6334,9 +6326,9 @@ static void globalRemove( } static void wlOutputGeometry( - void* data, - struct wl_output* output, - int32_t x, + void* data, + struct wl_output* output, + int32_t x, int32_t y, int32_t, // we dont need physical size int32_t, // we dont need physical size @@ -6383,9 +6375,9 @@ static void wlOutputGeometry( } static void wlOutputMode( - void* data, - struct wl_output* output, - uint32_t flags, + void* data, + struct wl_output* output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) @@ -6399,9 +6391,9 @@ static void wlOutputMode( } static void wlOutputMonitorModes( - void* data, - struct wl_output* output, - uint32_t flags, + void* data, + struct wl_output* output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) @@ -6420,8 +6412,8 @@ static void wlOutputMonitorModes( } static void wlOutputScale( - void* data, - struct wl_output* output, + void* data, + struct wl_output* output, int32_t scale) { if (s_Wl.modesPhase) { @@ -6434,37 +6426,32 @@ static void wlOutputScale( } static void wlOutputDone( - void* data, + void* data, struct wl_output* output) { - } static const struct wl_registry_listener s_RegistryListener = { .global = globalHandle, - .global_remove = globalRemove -}; + .global_remove = globalRemove}; static const struct wl_output_listener s_OutputListener = { .geometry = wlOutputGeometry, .mode = wlOutputMode, .done = wlOutputDone, - .scale = wlOutputScale -}; + .scale = wlOutputScale}; static const struct wl_output_listener s_ModesListener = { .geometry = wlOutputGeometry, .mode = wlOutputMonitorModes, .done = wlOutputDone, - .scale = wlOutputScale -}; + .scale = wlOutputScale}; static const struct wl_output_listener s_DefaultModeListener = { .geometry = wlOutputGeometry, .mode = wlOutputMode, .done = wlOutputDone, - .scale = wlOutputScale -}; + .scale = wlOutputScale}; PalResult wlInitVideo() { @@ -6695,7 +6682,7 @@ void wlShutdownVideo() void wlUpdateVideo() { - // flush pending requests + // flush pending requests s_Mouse.tmpScrollX = 0; s_Mouse.tmpScrollY = 0; @@ -6729,7 +6716,7 @@ void wlUpdateVideo() } int fd = s_Wl.displayGetFd(s_Wl.display); - struct pollfd pfd = { fd, POLLIN, 0 }; + struct pollfd pfd = {fd, POLLIN, 0}; if (poll(&pfd, 1, 0) > 0) { // there are events ready to be read s_Wl.readEvents(s_Wl.display); @@ -6777,11 +6764,8 @@ PalResult wlGetMonitorInfo( { uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); // bind the monitor and get its information - struct wl_output* output = wlRegistryBind( - s_Wl.registry, - name, - s_Wl.outputInterface, - 3); + struct wl_output* output = + wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); if (!output) { return PAL_RESULT_INVALID_MONITOR; @@ -6810,11 +6794,8 @@ PalResult wlEnumerateMonitorModes( { uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); // bind the monitor and get its information - struct wl_output* output = wlRegistryBind( - s_Wl.registry, - name, - s_Wl.outputInterface, - 3); + struct wl_output* output = + wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); if (!output) { return PAL_RESULT_INVALID_MONITOR; @@ -6844,17 +6825,14 @@ PalResult wlGetCurrentMonitorMode( { uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); // bind the monitor and get its information - struct wl_output* output = wlRegistryBind( - s_Wl.registry, - name, - s_Wl.outputInterface, - 3); + struct wl_output* output = + wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); if (!output) { return PAL_RESULT_INVALID_MONITOR; } - // we dont want to add another listener just to get the + // we dont want to add another listener just to get the // default monitor display mode PalMonitorInfo tmpInfo; s_Wl.modesPhase = true; @@ -6933,9 +6911,7 @@ PalResult wlCreateWindow( return PAL_RESULT_PLATFORM_FAILURE; } - xdgSurface = xdgWmBaseGetXdgSurface( - s_Wl.xdgBase, - surface); + xdgSurface = xdgWmBaseGetXdgSurface(s_Wl.xdgBase, surface); if (!xdgSurface) { return PAL_RESULT_PLATFORM_FAILURE; @@ -6995,14 +6971,13 @@ PalResult wlCreateWindow( // decorated window if (!(info->style & PAL_WINDOW_STYLE_BORDERLESS)) { struct zxdg_toplevel_decoration_v1* decoration = nullptr; - decoration = zxdgGetTopleveDecoration( - s_Wl.decorationManager, - xdgToplevel); - + decoration = + zxdgGetTopleveDecoration(s_Wl.decorationManager, xdgToplevel); + zxdgToplevelDecorationV1SetMode( - decoration, + decoration, 2); // SERVER_SIDE_DECORATION - + data->decoration = decoration; } @@ -7033,7 +7008,7 @@ PalResult wlCreateWindow( wlRegionDestroy(region); wlSurfaceCommit(surface); } - + s_Wl.displayRoundtrip(s_Wl.display); *outWindow = data->window; return PAL_RESULT_SUCCESS; @@ -7284,11 +7259,8 @@ PalResult wlCreateCursor( return PAL_RESULT_PLATFORM_FAILURE; } - cursor->buffer = createShmBuffer( - info->width, - info->height, - info->pixels, - true); + cursor->buffer = + createShmBuffer(info->width, info->height, info->pixels, true); if (!cursor->buffer) { return PAL_RESULT_PLATFORM_FAILURE; @@ -7340,7 +7312,7 @@ PalResult wlCreateCursorFrom( if (!wlCursor) { return PAL_RESULT_PLATFORM_FAILURE; } - + WaylandCursor* cursor = nullptr; cursor = palAllocate(s_Video.allocator, sizeof(WaylandCursor), 0); if (!cursor) { @@ -7543,12 +7515,12 @@ PalResult PAL_CALL palInitVideo( } else { #if PAL_HAS_WAYLAND - PalResult ret = wlInitVideo(); - if (ret != PAL_RESULT_SUCCESS) { - return ret; - } - s_Video.backend = &s_wlBackend; - + PalResult ret = wlInitVideo(); + if (ret != PAL_RESULT_SUCCESS) { + return ret; + } + s_Video.backend = &s_wlBackend; + #endif // PAL_HAS_WAYLAND } @@ -7589,7 +7561,7 @@ void PAL_CALL palShutdownVideo() if (s_Egl.handle) { dlclose(s_Egl.handle); } - + s_Video.platformInstance = nullptr; memset(&s_Keyboard, 0, sizeof(Keyboard)); memset(&s_Mouse, 0, sizeof(Mouse)); @@ -7640,7 +7612,7 @@ PalResult PAL_CALL palSetFBConfig( return glxBackend(); } else if ( - backend == PAL_CONFIG_BACKEND_EGL || + backend == PAL_CONFIG_BACKEND_EGL || backend == PAL_CONFIG_BACKEND_GLES || backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { if (s_X11.display) { From 1eddff00fffe44dd614d4138b1df60a0345296f2 Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 15:52:44 +0000 Subject: [PATCH 29/51] format tests --- tests/attach_window_test.c | 7 +- tests/char_event_test.c | 5 +- tests/cursor_test.c | 5 +- tests/icon_test.c | 5 +- tests/input_window_test.c | 10 +-- tests/native_instance_test.c | 111 ++++++++++++++++-------------- tests/native_integration_test.c | 54 +++++++-------- tests/opengl_context_test.c | 5 +- tests/opengl_multi_context_test.c | 5 +- tests/system_cursor_test.c | 5 +- tests/window_test.c | 5 +- 11 files changed, 95 insertions(+), 122 deletions(-) diff --git a/tests/attach_window_test.c b/tests/attach_window_test.c index bbe8a16..25e0e92 100644 --- a/tests/attach_window_test.c +++ b/tests/attach_window_test.c @@ -126,8 +126,6 @@ static void* createX11Window() return nullptr; } - - static void* createWin32Window() { #ifdef _WIN32 @@ -264,10 +262,7 @@ bool attachWindowTest() PAL_EVENT_WINDOW_MOVE, PAL_DISPATCH_POLL); - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // we listen for key release events to attach and detach the window palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYUP, PAL_DISPATCH_POLL); diff --git a/tests/char_event_test.c b/tests/char_event_test.c index c405987..ea34b91 100644 --- a/tests/char_event_test.c +++ b/tests/char_event_test.c @@ -66,10 +66,7 @@ bool charEventTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYCHAR, PAL_DISPATCH_POLL); diff --git a/tests/cursor_test.c b/tests/cursor_test.c index 1560334..59620be 100644 --- a/tests/cursor_test.c +++ b/tests/cursor_test.c @@ -120,10 +120,7 @@ bool cursorTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); // polling - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // set the cursor palSetWindowCursor(window, cursor); diff --git a/tests/icon_test.c b/tests/icon_test.c index 390c20a..4682926 100644 --- a/tests/icon_test.c +++ b/tests/icon_test.c @@ -111,10 +111,7 @@ bool iconTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); // polling - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // set the icon result = palSetWindowIcon(window, icon); diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 05536e0..76ebcdd 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -366,7 +366,7 @@ static inline void onMouseWheel(const PalEvent* event) { Int32 dx, dy; // dx == low, dy == high palUnpackInt32(event->data, &dx, &dy); - + // get the raw wheel delta (float) float fdx, fdy; palGetRawMouseWheelDelta(&fdx, &fdy); @@ -374,10 +374,10 @@ static inline void onMouseWheel(const PalEvent* event) PalWindow* window = palUnpackPointer(event->data2); palLog(nullptr, "%s: Mouse Wheel: (%d, %d)", dispatchString, dx, dy); palLog( - nullptr, - "%s: Mouse Wheel Raw: (%.2f, %.2f)", - dispatchString, - fdx, + nullptr, + "%s: Mouse Wheel Raw: (%.2f, %.2f)", + dispatchString, + fdx, fdy); } diff --git a/tests/native_instance_test.c b/tests/native_instance_test.c index 9411f0f..8e3ea37 100644 --- a/tests/native_instance_test.c +++ b/tests/native_instance_test.c @@ -41,33 +41,39 @@ typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); typedef int (*wl_display_roundtrip_fn)(struct wl_display*); typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( - struct wl_proxy*, - uint32_t, - const struct wl_interface*, - uint32_t, - uint32_t, ...); + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, + uint32_t, + ...); typedef int (*wl_proxy_add_listener_fn)( struct wl_proxy*, - void (**)(void), void*); + void (**)(void), + void*); struct wl_interface { - const char *name; - int version; - int method_count; - const struct wl_message *methods; - int event_count; - const struct wl_message *events; + const char* name; + int version; + int method_count; + const struct wl_message* methods; + int event_count; + const struct wl_message* events; }; struct wl_registry_listener { - void (*global)(void*, - struct wl_registry*, - uint32_t, - const char*, - uint32_t); - - void (*global_remove)(void*, struct wl_registry*, uint32_t); + void (*global)( + void*, + struct wl_registry*, + uint32_t, + const char*, + uint32_t); + + void (*global_remove)( + void*, + struct wl_registry*, + uint32_t); }; static void* s_LibWayland; @@ -81,55 +87,55 @@ static wl_proxy_add_listener_fn s_wl_proxy_add_listener; static const struct wl_interface* registryInterface; static inline void* wlRegistryBind( - struct wl_registry *wl_registry, - uint32_t name, - const struct wl_interface* interface, + struct wl_registry* wl_registry, + uint32_t name, + const struct wl_interface* interface, uint32_t version) { - struct wl_proxy *id; - id = s_wl_proxy_marshal_flags( - (struct wl_proxy *)wl_registry, + struct wl_proxy* id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_registry, 0, // WL_REGISTRY_BIND - interface, - version, - 0, - name, - interface->name, - version, + interface, + version, + 0, + name, + interface->name, + version, NULL); - return (void *)id; + return (void*)id; } static inline int wlRegistryAddListener( - struct wl_registry *wl_registry, - const struct wl_registry_listener *listener, - void *data) + struct wl_registry* wl_registry, + const struct wl_registry_listener* listener, + void* data) { - return s_wl_proxy_add_listener( - (struct wl_proxy *) wl_registry, - (void (**)(void)) listener, data); + return s_wl_proxy_add_listener( + (struct wl_proxy*)wl_registry, + (void (**)(void))listener, + data); } -static inline struct wl_registry* wlDisplayGetRegistry( - struct wl_display *wl_display) +static inline struct wl_registry* +wlDisplayGetRegistry(struct wl_display* wl_display) { - struct wl_proxy *registry; - registry = s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_display, + struct wl_proxy* registry; + registry = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_display, 1, // WL_DISPLAY_GET_REGISTRY - registryInterface, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_display), - 0, - NULL); + registryInterface, + s_wl_proxy_get_version((struct wl_proxy*)wl_display), + 0, + NULL); - return (struct wl_registry *)registry; + return (struct wl_registry*)registry; } static bool s_Logged = false; static void globalHandle( - void* data, + void* data, struct wl_registry* registry, uint32_t name, const char* interface, @@ -142,7 +148,7 @@ static void globalHandle( } static void globalRemove( - void* data, + void* data, struct wl_registry* registry, uint32_t name) { @@ -154,8 +160,7 @@ static void globalRemove( static const struct wl_registry_listener s_RegistryListener = { .global = globalHandle, - .global_remove = globalRemove -}; + .global_remove = globalRemove}; static bool s_OnWayland = false; diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 5793cd0..674147c 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include // wayland is optional and might not be supported // so we stick to typedefs @@ -77,11 +77,12 @@ struct wl_interface; struct xdg_toplevel; typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( - struct wl_proxy*, - uint32_t, - const struct wl_interface*, - uint32_t, - uint32_t, ...); + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, + uint32_t, + ...); typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); typedef int (*wl_display_flush_fn)(struct wl_display*); @@ -91,17 +92,16 @@ static wl_proxy_get_version_fn s_wl_proxy_get_version; static wl_display_flush_fn s_wl_display_flush; static inline void xdgToplevelSetTitle( - struct xdg_toplevel *xdg_toplevel, - const char *title) + struct xdg_toplevel* xdg_toplevel, + const char* title) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) xdg_toplevel, + s_wl_proxy_marshal_flags( + (struct wl_proxy*)xdg_toplevel, 2, // XDG_TOPLEVEL_SET_TITLE - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) xdg_toplevel), - 0, - title); + NULL, + s_wl_proxy_get_version((struct wl_proxy*)xdg_toplevel), + 0, + title); } static XInternAtomFn s_XInternAtom; @@ -240,16 +240,14 @@ void setWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) } s_wl_proxy_marshal_flags = (wl_proxy_marshal_flags_fn)dlsym( - s_WaylandLib, + s_WaylandLib, "wl_proxy_marshal_flags"); - s_wl_proxy_get_version = (wl_proxy_get_version_fn)dlsym( - s_WaylandLib, - "wl_proxy_get_version"); + s_wl_proxy_get_version = + (wl_proxy_get_version_fn)dlsym(s_WaylandLib, "wl_proxy_get_version"); - s_wl_display_flush = (wl_display_flush_fn)dlsym( - s_WaylandLib, - "wl_display_flush"); + s_wl_display_flush = + (wl_display_flush_fn)dlsym(s_WaylandLib, "wl_display_flush"); struct xdg_toplevel* toplevel = nullptr; struct wl_display* display = nullptr; @@ -280,8 +278,8 @@ void setWindowTitleWin32(PalWindowHandleInfoEx* windowInfo) void getWindowTitleWin32(PalWindowHandleInfoEx* windowInfo) { GetWindowTextA( - (HWND)windowInfo->nativeWindow, - s_TitleBuffer, + (HWND)windowInfo->nativeWindow, + s_TitleBuffer, sizeof(s_TitleBuffer)); } @@ -362,7 +360,7 @@ bool nativeIntegrationTest() createInfo.show = true; createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; createInfo.title = "Native Integration Test"; - + // check if we support decorated windows (title bar, close etc) PalVideoFeatures64 features = palGetVideoFeaturesEx(); if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { @@ -384,10 +382,7 @@ bool nativeIntegrationTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // set the window title using native APIs PalWindowHandleInfoEx windowInfo = {0}; @@ -444,5 +439,4 @@ bool nativeIntegrationTest() palDestroyEventDriver(eventDriver); return true; - } \ No newline at end of file diff --git a/tests/opengl_context_test.c b/tests/opengl_context_test.c index 62ceb0e..5a1491f 100644 --- a/tests/opengl_context_test.c +++ b/tests/opengl_context_test.c @@ -201,10 +201,7 @@ bool openglContextTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // get window handle. You can use any window from any library // so long as you can get the window handle and display (if on X11, wayland) diff --git a/tests/opengl_multi_context_test.c b/tests/opengl_multi_context_test.c index 03faebd..70c3cd1 100644 --- a/tests/opengl_multi_context_test.c +++ b/tests/opengl_multi_context_test.c @@ -201,10 +201,7 @@ bool openglMultiContextTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // get window handle. You can use any window from any library // so long as you can get the window handle and display (if on X11, wayland) diff --git a/tests/system_cursor_test.c b/tests/system_cursor_test.c index 09a52e3..a0f4ec7 100644 --- a/tests/system_cursor_test.c +++ b/tests/system_cursor_test.c @@ -91,10 +91,7 @@ bool systemCursorTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); // polling - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // set the cursor palSetWindowCursor(window, cursor); diff --git a/tests/window_test.c b/tests/window_test.c index f3c75d1..90579c2 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -281,10 +281,7 @@ bool windowTest() PAL_EVENT_WINDOW_CLOSE, PAL_DISPATCH_POLL); - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_KEYDOWN, - PAL_DISPATCH_POLL); + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); // we set callback mode for modal begin and end. Since we want to capture // that instantly From 64cef1a97eb803965352238341be467a0360e16d Mon Sep 17 00:00:00 2001 From: nichcode Date: Fri, 14 Nov 2025 17:10:12 +0000 Subject: [PATCH 30/51] testing --- pal.lua | 26 +++++++- src/video/pal_video_linux.c | 101 ++++++++++++++++++++------------ tests/native_integration_test.c | 7 ++- 3 files changed, 92 insertions(+), 42 deletions(-) diff --git a/pal.lua b/pal.lua index ce17da0..ecf4681 100644 --- a/pal.lua +++ b/pal.lua @@ -86,13 +86,13 @@ project "PAL" files { "src/video/pal_video_linux.c" } -- check for wayland support. This is cross compiler - local paths = { + local waylandPaths = { "/usr/include/wayland-client.h", "/usr/include/x86_64-linux-gnu/wayland-client.h" } local found = false - for _, path in ipairs(paths) do + for _, path in ipairs(waylandPaths) do local file = io.open(path, "r") if file then file:close() @@ -106,6 +106,28 @@ project "PAL" else defines { "PAL_HAS_WAYLAND=0" } end + + -- -- check for X11 support. This is cross compiler + local XPaths = { + "/usr/include/X11/Xlib.h", + "/usr/include/x86_64-linux-gnu/X11/Xlib.h" + } + + found = false + for _, path in ipairs(XPaths) do + local file = io.open(path, "r") + if file then + file:close() + found = true + break + end + end + + if found then + defines { "PAL_HAS_X11=1" } + else + defines { "PAL_HAS_X11=0" } + end filter {} end diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 3866e8b..2fad858 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -34,8 +34,10 @@ freely, subject to the following restrictions: #include #include #include +#include // X11 headers +#if PAL_HAS_X11 #include #include #include @@ -44,6 +46,7 @@ freely, subject to the following restrictions: #include #include #include +#endif // PAL_HAS_X11 // Wayland headers #if PAL_HAS_WAYLAND @@ -70,6 +73,8 @@ freely, subject to the following restrictions: #define TO_PAL_HANDLE(type, val) ((type*)(UintPtr)(val)) #define FROM_PAL_HANDLE(type, handle) ((type)(UintPtr)(handle)) +#pragma region EGL Typedefs + typedef void* EGLConfig; typedef void* EGLSurface; typedef void* EGLContext; @@ -124,6 +129,8 @@ typedef EGLBoolean (*eglGetConfigsFn)( EGLint, EGLint*); +#pragma endregion + typedef struct { bool skipConfigure; bool skipState; @@ -139,8 +146,8 @@ typedef struct { PalWindow* window; // X11 only - XIC ic; - Colormap colormap; + void* ic; + unsigned long colormap; // Wayland only void* xdgSurface; @@ -207,6 +214,7 @@ typedef struct { // clang-format off void (*shutdownVideo)(); void (*updateVideo)(); + PalResult (*setFBConfig)(const int, PalFBConfigBackend); PalResult (*enumerateMonitors)(Int32*, PalMonitor**); PalResult (*getPrimaryMonitor)(PalMonitor**); PalResult (*getMonitorInfo)(PalMonitor*, PalMonitorInfo*); @@ -273,6 +281,7 @@ typedef struct { MonitorData* monitorData; const char* className; void* platformInstance; + void* display; } VideoLinux; static VideoLinux s_Video = {0}; @@ -285,8 +294,9 @@ static EGL s_Egl; // ================================================== #pragma region X11 Typedefs -#define X_INTERN(x) s_X11Atoms.x = s_X11.internAtom(s_X11.display, #x, False) +#if PAL_HAS_X11 +#define X_INTERN(x) s_X11Atoms.x = s_X11.internAtom(s_X11.display, #x, False) #define RANDR_SCREEN_CHANGE_EVENT 1040 // optionally, needed to create visual from FBConfig @@ -824,6 +834,7 @@ typedef struct { static X11 s_X11 = {0}; static X11Atoms s_X11Atoms = {0}; +#endif // PAL_HAS_X11 #pragma endregion // ================================================== @@ -2960,8 +2971,9 @@ static void createScancodeTable() // ================================================== #pragma region X11 API +#if PAL_HAS_X11 -static PalResult glxBackend() +static PalResult glxBackend(const int index) { // user choose GLX FBConfig backend if (!s_X11.glxHandle) { @@ -2976,7 +2988,7 @@ static PalResult glxBackend() s_X11.screen, &count); - GLXFBConfig fbConfig = configs[s_Video.pixelFormat]; + GLXFBConfig fbConfig = configs[index]; if (!fbConfig) { return PAL_RESULT_INVALID_GL_FBCONFIG; } @@ -3003,15 +3015,10 @@ static PalResult eglXBackend(int index) EGLDisplay display = EGL_NO_DISPLAY; display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_X11.display); - if (display == EGL_NO_DISPLAY) { return PAL_RESULT_PLATFORM_FAILURE; } - if (!s_Egl.eglInitialize(display, nullptr, nullptr)) { - return PAL_RESULT_PLATFORM_FAILURE; - } - EGLint numConfigs = 0; if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { return PAL_RESULT_PLATFORM_FAILURE; @@ -3049,10 +3056,8 @@ static PalResult eglXBackend(int index) if (!visualInfo) { return PAL_RESULT_INVALID_GL_FBCONFIG; } - + s_X11.visualInfo = visualInfo; - return PAL_RESULT_SUCCESS; - palFree(s_Video.allocator, eglConfigs); return PAL_RESULT_SUCCESS; } @@ -3814,6 +3819,7 @@ static PalResult xInitVideo() return PAL_RESULT_PLATFORM_FAILURE; } + s_Video.display = (void*)s_X11.display; return PAL_RESULT_SUCCESS; } @@ -3836,6 +3842,22 @@ static void xShutdownVideo() memset(&s_X11Atoms, 0, sizeof(X11Atoms)); } +PalResult xSetFBConfig( + const int index, + PalFBConfigBackend backend) +{ + if (backend == PAL_CONFIG_BACKEND_GLX) { + return glxBackend(index); + + } else if (backend == PAL_CONFIG_BACKEND_EGL || + backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { + return eglXBackend(index); + + } else { + return PAL_RESULT_INVALID_FBCONFIG_BACKEND; + } +} + static void xUpdateVideo() { XEvent event; @@ -6010,6 +6032,7 @@ PalResult xDetachWindow( static Backend s_XBackend = { .shutdownVideo = xShutdownVideo, .updateVideo = xUpdateVideo, + .setFBConfig = xSetFBConfig, .enumerateMonitors = xEnumerateMonitors, .getMonitorInfo = xGetMonitorInfo, .getPrimaryMonitor = xGetPrimaryMonitor, @@ -6060,6 +6083,7 @@ static Backend s_XBackend = { .attachWindow = xAttachWindow, .detachWindow = xDetachWindow}; +#endif // PAL_HAS_X11 #pragma endregion // ================================================== @@ -6632,6 +6656,7 @@ PalResult wlInitVideo() s_Video.platformInstance = nullptr; } + s_Video.display = (void*)s_Wl.display; s_Wl.registry = wlDisplayGetRegistry(s_Wl.display); wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); s_Wl.displayRoundtrip(s_Wl.display); @@ -6656,6 +6681,8 @@ PalResult wlInitVideo() } wlCreateKeycodeTable(); + + s_Video.display = (void*)s_Wl.display; return PAL_RESULT_SUCCESS; } @@ -6680,6 +6707,19 @@ void wlShutdownVideo() memset(&s_Wl, 0, sizeof(Wayland)); } +PalResult wlSetFBConfig( + const int index, + PalFBConfigBackend backend) +{ + if (backend == PAL_CONFIG_BACKEND_GLES || + backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { + return eglWlBackend(index); + + } else { + return PAL_RESULT_INVALID_FBCONFIG_BACKEND; + } +} + void wlUpdateVideo() { // flush pending requests @@ -7410,6 +7450,7 @@ PalResult wlDetachWindow( static Backend s_wlBackend = { .shutdownVideo = wlShutdownVideo, .updateVideo = wlUpdateVideo, + .setFBConfig = wlSetFBConfig, .enumerateMonitors = wlEnumerateMonitors, .getMonitorInfo = wlGetMonitorInfo, .getPrimaryMonitor = wlGetPrimaryMonitor, @@ -7507,11 +7548,15 @@ PalResult PAL_CALL palInitVideo( s_Video.className = "PAL"; if (x11) { +#if PAL_HAS_X11 PalResult ret = xInitVideo(); if (ret != PAL_RESULT_SUCCESS) { return ret; } s_Video.backend = &s_XBackend; +#else + return PAL_RESULT_PLATFORM_FAILURE; +#endif // PAL_HAS_X11 } else { #if PAL_HAS_WAYLAND @@ -7520,7 +7565,8 @@ PalResult PAL_CALL palInitVideo( return ret; } s_Video.backend = &s_wlBackend; - +#else + return PAL_RESULT_PLATFORM_FAILURE; #endif // PAL_HAS_WAYLAND } @@ -7563,6 +7609,7 @@ void PAL_CALL palShutdownVideo() } s_Video.platformInstance = nullptr; + s_Video.display = nullptr; memset(&s_Keyboard, 0, sizeof(Keyboard)); memset(&s_Mouse, 0, sizeof(Mouse)); s_Video.initialized = false; @@ -7602,25 +7649,12 @@ PalResult PAL_CALL palSetFBConfig( return PAL_RESULT_VIDEO_NOT_INITIALIZED; } - // X11 and wayland can used GLX and EGL + // X11 and wayland can only used GLX and EGL if (backend == PAL_CONFIG_BACKEND_WGL) { return PAL_RESULT_INVALID_FBCONFIG_BACKEND; } - // we try to create a colormap to see if the index is valid - if (backend == PAL_CONFIG_BACKEND_GLX) { - return glxBackend(); - - } else if ( - backend == PAL_CONFIG_BACKEND_EGL || - backend == PAL_CONFIG_BACKEND_GLES || - backend == PAL_CONFIG_BACKEND_PAL_OPENGL) { - if (s_X11.display) { - return eglXBackend(index); - } else { - return eglWlBackend(index); - } - } + return s_Video.backend->setFBConfig(index, backend); } PalResult PAL_CALL palEnumerateMonitors( @@ -8344,14 +8378,7 @@ void* PAL_CALL palGetInstance() return nullptr; } - if (s_X11.display) { - // we are on X11 - return (void*)s_X11.display; - } else { - return (void*)s_Wl.display; - } - - return nullptr; + return s_Video.display; } PalResult PAL_CALL palAttachWindow( diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 674147c..0fef15a 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -25,9 +25,6 @@ #include #include -// wayland is optional and might not be supported -// so we stick to typedefs - // X11 typedefs typedef Atom (*XInternAtomFn)( Display*, @@ -271,16 +268,20 @@ void getWindowTitleWayland(PalWindowHandleInfoEx* windowInfo) void setWindowTitleWin32(PalWindowHandleInfoEx* windowInfo) { +#ifdef _WIN32 const char* title = "Hello from native Win32 API"; SetWindowTextA((HWND)windowInfo->nativeWindow, title); +#endif // _WIN32 } void getWindowTitleWin32(PalWindowHandleInfoEx* windowInfo) { +#ifdef _WIN32 GetWindowTextA( (HWND)windowInfo->nativeWindow, s_TitleBuffer, sizeof(s_TitleBuffer)); +#endif // _WIN32 } void setWindowTitle(PalWindowHandleInfoEx* windowInfo) From e679b18f96a3755ba91ffdeda81be8232cd7e79c Mon Sep 17 00:00:00 2001 From: nichcode Date: Sun, 16 Nov 2025 19:02:10 +0000 Subject: [PATCH 31/51] add focus event wayland --- CHANGELOG.md | 2 + include/pal/pal_event.h | 27 +++ src/video/pal_video_linux.c | 364 ++++++++++++++++-------------------- tests/tests_main.c | 50 ++--- tests/window_test.c | 77 +++++--- 5 files changed, 265 insertions(+), 255 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 860f8d7..cc8cd46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,8 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. - **OpenGL:** Added **palGLSetInstance()** to set the instance or display handle. - **OpenGL:** Added **palGLGetBackend()** to get the opengl backend. +- **Event:** Added **PAL_EVENT_WINDOW_DECORATION_MODE** to `PalEventType` enum. +- **Event:** Added **PalDecorationMode** enum. ### Tests - Added native integration example: demonstrating **Native API Integration with PAL API**. see diff --git a/include/pal/pal_event.h b/include/pal/pal_event.h index b2edc93..4891360 100644 --- a/include/pal/pal_event.h +++ b/include/pal/pal_event.h @@ -100,6 +100,21 @@ typedef bool(PAL_CALL* PalPollFn)( void* userData, PalEvent* outEvent); +/** + * @enum PalDecorationMode + * @brief Decoration types. This is not a bitmask enum. + * + * All decoration types follow the format `PAL_DECORATION_MODE_**` for consistency and + * API use. + * + * @since 1.3 + * @ingroup pal_event + */ +typedef enum { + PAL_DECORATION_MODE_CLIENT_SIDE, + PAL_DECORATION_MODE_SERVER_SIDE +} PalDecorationMode; + /** * @enum PalEventType * @brief Event types. This is not a bitmask enum. @@ -348,6 +363,18 @@ typedef enum { */ PAL_EVENT_KEYCHAR, + /** + * PAL_EVENT_WINDOW_DECORATION_MODE + * + * event.data : negotiated decorations mode + * + * event.data2 : window + * + * Use inline helpers: + * - palUnpackPointer() + */ + PAL_EVENT_WINDOW_DECORATION_MODE, + PAL_EVENT_MAX } PalEventType; diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 2fad858..347e696 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -137,6 +137,9 @@ typedef struct { bool used; bool isAttached; bool skipIfAttached; + bool focused; + bool pushConfigureEvent; + bool pushStateEvent; int x; int y; Uint32 w; @@ -1554,7 +1557,11 @@ static void pointerHandleButton( if (mode != PAL_DISPATCH_NONE) { PalEvent event = {0}; event.type = type; - event.data = _button; + + // since we are not drawing decorations for users + // they need the serial in order to draw their decorations + // we put the serial at the upper 32 so ABI is preserved + event.data = palPackUint32(_button, serial); event.data2 = palPackPointer(window); palPushEvent(driver, &event); } @@ -2035,9 +2042,10 @@ static inline void xdgSurfaceAckConfigure( (struct wl_proxy*)xdg_surface, 4, // XDG_SURFACE_ACK_CONFIGURE NULL, - s_Wl.proxyGetVersion((struct wl_proxy*)xdg_surface), - 0, - serial); + s_Wl.proxyGetVersion( + (struct wl_proxy*)xdg_surface), + 0, + serial); } static void wmBaseHandlePing( @@ -2058,54 +2066,58 @@ static void xdgSurfaceHandleConfigure( // push and resolve any pending events if (!winData->skipConfigure) { - winData->skipConfigure = true; - - if (winData->eglWindow) { - s_Wl.eglWindowResize( - winData->eglWindow, - winData->w, - winData->h, - 0, - 0); + if (winData->pushConfigureEvent) { + if (winData->eglWindow) { + s_Wl.eglWindowResize( + winData->eglWindow, + winData->w, + winData->h, + 0, + 0); - } else { - // create a new buffer with the new size - struct wl_buffer* buffer = nullptr; - buffer = createShmBuffer(winData->w, winData->h, nullptr, false); - if (!buffer) { - return; - } + } else { + // create a new buffer with the new size + struct wl_buffer* buffer = nullptr; + buffer = createShmBuffer(winData->w, winData->h, nullptr, false); + if (!buffer) { + return; + } - struct wl_surface* _surface = nullptr; - _surface = (struct wl_surface*)winData->window; + struct wl_surface* _surface = nullptr; + _surface = (struct wl_surface*)winData->window; - wlSurfaceAttach(_surface, buffer, 0, 0); - wlSurfaceDamageBuffer(_surface, 0, 0, winData->w, winData->h); - wlSurfaceCommit(_surface); + wlSurfaceAttach(_surface, buffer, 0, 0); + wlSurfaceDamageBuffer(_surface, 0, 0, winData->w, winData->h); + wlSurfaceCommit(_surface); - // destroy old buffer - wlBufferDestroy(winData->buffer); - winData->buffer = buffer; - } + // destroy old buffer + wlBufferDestroy(winData->buffer); + winData->buffer = buffer; + } - // push a window resize event - if (s_Video.eventDriver) { - PalEventType type = PAL_EVENT_WINDOW_SIZE; - PalDispatchMode mode = PAL_DISPATCH_NONE; - mode = palGetEventDispatchMode(s_Video.eventDriver, type); - if (mode != PAL_DISPATCH_NONE) { - PalEvent event = {0}; - event.type = type; - event.data = palPackUint32(winData->w, winData->h); - event.data2 = palPackPointer(winData->window); - palPushEvent(s_Video.eventDriver, &event); + // push a window resize event + if (s_Video.eventDriver) { + PalEventType type = PAL_EVENT_WINDOW_SIZE; + PalDispatchMode mode = PAL_DISPATCH_NONE; + mode = palGetEventDispatchMode(s_Video.eventDriver, type); + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = palPackUint32(winData->w, winData->h); + event.data2 = palPackPointer(winData->window); + palPushEvent(s_Video.eventDriver, &event); + } } + + winData->pushConfigureEvent = false; } } // pending state if (!winData->skipState) { - winData->skipState = true; + if (!winData->pushStateEvent) { + return; + } // push a window state event // we dont recreate buffers over here @@ -2122,6 +2134,8 @@ static void xdgSurfaceHandleConfigure( palPushEvent(s_Video.eventDriver, &event); } } + + winData->pushStateEvent = false; } } @@ -2133,26 +2147,56 @@ static void xdgToplevelHandleConfigure( struct wl_array* states) { WindowData* winData = (WindowData*)data; - - if (!winData->skipState) { - uint32_t* state; - wl_array_for_each(state, states) - { - // we need only maximized - if (*state == 1) { // XDG_TOPLEVEL_STATE_MAXIMIZED - if (winData->state != PAL_WINDOW_STATE_MAXIMIZED) { - winData->state = PAL_WINDOW_STATE_MAXIMIZED; - winData->skipState = false; - } + uint32_t* state; + bool activated = false; + wl_array_for_each(state, states) { + // we need only maximized + if (*state == 1) { // XDG_TOPLEVEL_STATE_MAXIMIZED + if (winData->state != PAL_WINDOW_STATE_MAXIMIZED) { + winData->state = PAL_WINDOW_STATE_MAXIMIZED; + winData->pushStateEvent = true; } + + } else if (*state == 4) { // XDG_TOPLEVEL_STATE_ACTIVATED + activated = true; } } - if (!winData->skipConfigure) { - if (width > 0 && height > 0) { - winData->w = width; - winData->h = height; - winData->skipConfigure = false; + if (width > 0 && height > 0) { + if (width != winData->w || height != winData->h) { + // size change + winData->pushConfigureEvent = true; + } + + winData->w = width; + winData->h = height; + } + + if (activated && !winData->focused) { + // focus gained + winData->focused = true; + + } else if (!activated && winData->focused) { + // focus lost + winData->focused = false; + + } else { + // discard double focus gained and double focus lost + return; + } + + if (s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + PalDispatchMode mode = PAL_DISPATCH_NONE; + PalEventType type = PAL_EVENT_WINDOW_FOCUS; + mode = palGetEventDispatchMode(driver, type); + + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = winData->focused; + event.data2 = palPackPointer(winData->window); + palPushEvent(driver, &event); } } } @@ -2162,7 +2206,6 @@ static void xdgToplevelHandleClose( struct xdg_toplevel* toplevel) { WindowData* winData = (WindowData*)data; - if (s_Video.eventDriver) { PalEventType type = PAL_EVENT_WINDOW_CLOSE; PalDispatchMode mode = PAL_DISPATCH_NONE; @@ -2496,7 +2539,7 @@ static inline void zxdgDecorationManagerV1Destroy( WL_MARSHAL_FLAG_DESTROY); } -static inline struct zxdg_toplevel_decoration_v1* zxdgGetTopleveDecoration( +static inline struct zxdg_toplevel_decoration_v1* zxdgGetToplevelDecoration( struct zxdg_decoration_manager_v1* zxdg_decoration_manager_v1, struct xdg_toplevel* toplevel) { @@ -2587,129 +2630,37 @@ const struct wl_interface zxdg_toplevel_decoration_v1_interface = { zxdg_toplevel_decoration_v1_events, }; -#endif // PAL_HAS_WAYLAND -#pragma endregion - -#pragma region Zwp-Pointer-Constraints -#if PAL_HAS_WAYLAND - -struct zwp_confined_pointer_v1; -struct zwp_pointer_constraints_v1; - -const struct wl_interface zwp_confined_pointer_v1_interface; - -static inline struct zwp_confined_pointer_v1* zwpPointerConstraintsConfine( - struct zwp_pointer_constraints_v1* zwp_pointer_constraints_v1, - struct wl_surface* surface, - struct wl_pointer* pointer, - struct wl_region* region, - uint32_t lifetime) +void zxdgDecorationHandleConfigure( + void* data, + struct zxdg_toplevel_decoration_v1* dec, + uint32_t mode) { - struct wl_proxy* id; - id = s_Wl.proxyMarshalFlags( - (struct wl_proxy*)zwp_pointer_constraints_v1, - 2, // ZWP_POINTER_CONSTRAINTS_V1_CONFINE_POINTER - &zwp_confined_pointer_v1_interface, - s_Wl.proxyGetVersion((struct wl_proxy*)zwp_pointer_constraints_v1), - 0, - NULL, - surface, - pointer, - region, - lifetime); - - return (struct zwp_confined_pointer_v1*)id; -} + if (s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + PalDispatchMode dispatchMode = PAL_DISPATCH_NONE; + PalEventType type = PAL_EVENT_WINDOW_DECORATION_MODE; + dispatchMode = palGetEventDispatchMode(driver, type); + + if (dispatchMode != PAL_DISPATCH_NONE) { + PalDecorationMode decorMode = PAL_DECORATION_MODE_SERVER_SIDE; + if (mode == 0 || mode == 1) { + // client side decoration + decorMode = PAL_DECORATION_MODE_CLIENT_SIDE; + } -static inline void zwpConfinedPointerV1Destroy( - struct zwp_confined_pointer_v1* zwp_confined_pointer_v1) -{ - s_Wl.proxyMarshalFlags( - (struct wl_proxy*)zwp_confined_pointer_v1, - 0, // ZWP_CONFINED_POINTER_V1_DESTROY - NULL, - s_Wl.proxyGetVersion((struct wl_proxy*)zwp_confined_pointer_v1), - WL_MARSHAL_FLAG_DESTROY); + PalEvent event = {0}; + event.type = type; + event.data = decorMode; + event.data2 = palPackPointer(data); + palPushEvent(driver, &event); + } + } } -static const struct wl_interface* pointer_constraints_unstable_v1_types[14]; - -static const struct wl_message zwp_pointer_constraints_v1_requests[] = { - {"destroy", "", pointer_constraints_unstable_v1_types + 0}, - {"lock_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 2}, - {"confine_pointer", "noo?ou", pointer_constraints_unstable_v1_types + 7}, -}; - -const struct wl_interface zwp_pointer_constraints_v1_interface = { - "zwp_pointer_constraints_v1", - 1, - 3, - zwp_pointer_constraints_v1_requests, - 0, - NULL, -}; - -static const struct wl_message zwp_locked_pointer_v1_requests[] = { - {"destroy", "", pointer_constraints_unstable_v1_types + 0}, - {"set_cursor_position_hint", - "ff", - pointer_constraints_unstable_v1_types + 0}, - {"set_region", "?o", pointer_constraints_unstable_v1_types + 12}, -}; - -static const struct wl_message zwp_locked_pointer_v1_events[] = { - {"locked", "", pointer_constraints_unstable_v1_types + 0}, - {"unlocked", "", pointer_constraints_unstable_v1_types + 0}, -}; - -const struct wl_interface zwp_locked_pointer_v1_interface = { - "zwp_locked_pointer_v1", - 1, - 3, - zwp_locked_pointer_v1_requests, - 2, - zwp_locked_pointer_v1_events, -}; - -static const struct wl_message zwp_confined_pointer_v1_requests[] = { - {"destroy", "", pointer_constraints_unstable_v1_types + 0}, - {"set_region", "?o", pointer_constraints_unstable_v1_types + 13}, +static struct zxdg_toplevel_decoration_v1_listener decorationListener = { + .configure = zxdgDecorationHandleConfigure }; -static const struct wl_message zwp_confined_pointer_v1_events[] = { - {"confined", "", pointer_constraints_unstable_v1_types + 0}, - {"unconfined", "", pointer_constraints_unstable_v1_types + 0}, -}; - -const struct wl_interface zwp_confined_pointer_v1_interface = { - "zwp_confined_pointer_v1", - 1, - 2, - zwp_confined_pointer_v1_requests, - 2, - zwp_confined_pointer_v1_events, -}; - -static void setupZwpPointerProtocol() -{ - // clang-format off - pointer_constraints_unstable_v1_types[0] = NULL; - pointer_constraints_unstable_v1_types[1] = NULL; - pointer_constraints_unstable_v1_types[2] = &zwp_locked_pointer_v1_interface; - pointer_constraints_unstable_v1_types[3] = s_Wl.surfaceInterface; - pointer_constraints_unstable_v1_types[4] = s_Wl.pointerInterface; - pointer_constraints_unstable_v1_types[5] = s_Wl.regionInterface; - pointer_constraints_unstable_v1_types[6] = NULL; - pointer_constraints_unstable_v1_types[7] = &zwp_confined_pointer_v1_interface; - pointer_constraints_unstable_v1_types[8] = s_Wl.surfaceInterface; - pointer_constraints_unstable_v1_types[9] = s_Wl.pointerInterface; - pointer_constraints_unstable_v1_types[10] = s_Wl.regionInterface; - pointer_constraints_unstable_v1_types[11] = NULL; - pointer_constraints_unstable_v1_types[12] = s_Wl.regionInterface; - pointer_constraints_unstable_v1_types[13] = s_Wl.regionInterface; - // clang-format on -} - #endif // PAL_HAS_WAYLAND #pragma endregion @@ -6314,13 +6265,6 @@ static void globalHandle( s_Video.features64 |= PAL_VIDEO_FEATURE64_DECORATED_WINDOW; - } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { - s_Wl.pointerConstraints = wlRegistryBind( - registry, - name, - &zwp_pointer_constraints_v1_interface, - 1); - } else if (strcmp(interface, "wl_output") == 0) { // wayland does not let use query monitors directly // so we enumerate and store at init and update the @@ -6645,7 +6589,6 @@ PalResult wlInitVideo() s_Wl.checkFeatures = true; s_Wl.monitorCount = 0; setupXdgShellProtocol(); - setupZwpPointerProtocol(); // check if user supplied their own display if (s_Video.platformInstance) { @@ -6656,6 +6599,10 @@ PalResult wlInitVideo() s_Video.platformInstance = nullptr; } + if (!s_Wl.display) { + return PAL_RESULT_PLATFORM_FAILURE; + } + s_Video.display = (void*)s_Wl.display; s_Wl.registry = wlDisplayGetRegistry(s_Wl.display); wlRegistryAddListener(s_Wl.registry, &s_RegistryListener, nullptr); @@ -6944,6 +6891,7 @@ PalResult wlCreateWindow( memset(data, 0, sizeof(WindowData)); data->used = true; + data->focused = false; // create surface surface = wlCompositorCreateSurface(s_Wl.compositor); @@ -6952,7 +6900,6 @@ PalResult wlCreateWindow( } xdgSurface = xdgWmBaseGetXdgSurface(s_Wl.xdgBase, surface); - if (!xdgSurface) { return PAL_RESULT_PLATFORM_FAILURE; } @@ -6974,10 +6921,25 @@ PalResult wlCreateWindow( wlSurfaceAddListener(surface, &surfaceListener, data); xdgToplevelAddListener(xdgToplevel, &xdgToplevelListener, data); xdgSurfaceAddListener(xdgSurface, &xdgSurfaceListener, data); - wlSurfaceCommit(surface); + + // decorated window + if (!(info->style & PAL_WINDOW_STYLE_BORDERLESS)) { + struct zxdg_toplevel_decoration_v1* decoration = nullptr; + decoration = + zxdgGetToplevelDecoration(s_Wl.decorationManager, xdgToplevel); + + zxdgToplevelDecorationV1AddListener( + decoration, + &decorationListener, + surface); + + zxdgToplevelDecorationV1SetMode(decoration, 2); + data->decoration = decoration; + } data->skipState = true; data->skipConfigure = true; + wlSurfaceCommit(surface); s_Wl.displayRoundtrip(s_Wl.display); if (info->maximized && info->show) { @@ -7008,19 +6970,6 @@ PalResult wlCreateWindow( data->state = PAL_WINDOW_STATE_MINIMIZED; } - // decorated window - if (!(info->style & PAL_WINDOW_STYLE_BORDERLESS)) { - struct zxdg_toplevel_decoration_v1* decoration = nullptr; - decoration = - zxdgGetTopleveDecoration(s_Wl.decorationManager, xdgToplevel); - - zxdgToplevelDecorationV1SetMode( - decoration, - 2); // SERVER_SIDE_DECORATION - - data->decoration = decoration; - } - if (s_Wl.eglFBConfig) { data->eglWindow = s_Wl.eglWindowCreate(surface, data->w, data->h); if (!data->eglWindow) { @@ -7050,6 +6999,23 @@ PalResult wlCreateWindow( } s_Wl.displayRoundtrip(s_Wl.display); + if (!s_Wl.decorationManager && s_Video.eventDriver) { + PalEventDriver* driver = s_Video.eventDriver; + PalDispatchMode mode = PAL_DISPATCH_NONE; + PalEventType type = PAL_EVENT_WINDOW_DECORATION_MODE; + mode = palGetEventDispatchMode(driver, type); + + if (mode != PAL_DISPATCH_NONE) { + PalEvent event = {0}; + event.type = type; + event.data = PAL_DECORATION_MODE_CLIENT_SIDE; + event.data2 = palPackPointer(data->window); + palPushEvent(driver, &event); + } + } + + data->skipState = false; + data->skipConfigure = false; *outWindow = data->window; return PAL_RESULT_SUCCESS; } diff --git a/tests/tests_main.c b/tests/tests_main.c index 141878c..8213dd4 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,48 +9,48 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - registerTest("Logger Test", loggerTest); - registerTest("Time Test", timeTest); - registerTest("User Event Test", userEventTest); - registerTest("Event Test", eventTest); + // registerTest("Logger Test", loggerTest); + // registerTest("Time Test", timeTest); + // registerTest("User Event Test", userEventTest); + // registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - registerTest("System Test", systemTest); + // registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - registerTest("Thread Test", threadTest); - registerTest("TLS Test", tlsTest); - registerTest("Mutex Test", mutexTest); - registerTest("Condvar Test", condvarTest); + // registerTest("Thread Test", threadTest); + // registerTest("TLS Test", tlsTest); + // registerTest("Mutex Test", mutexTest); + // registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO - registerTest("Video Test", videoTest); - registerTest("Monitor Test", monitorTest); - registerTest("Monitor Mode Test", monitorModeTest); + // registerTest("Video Test", videoTest); + // registerTest("Monitor Test", monitorTest); + // registerTest("Monitor Mode Test", monitorModeTest); registerTest("Window Test", windowTest); - registerTest("Icon Test", iconTest); - registerTest("Cursor Test", cursorTest); - registerTest("Input Window Test", inputWindowTest); - registerTest("System Cursor Test", systemCursorTest); - registerTest("Attach Window Test", attachWindowTest); - registerTest("Character Event Test", charEventTest); - registerTest("Native Integration Test", nativeIntegrationTest); - registerTest("Native Instance Test", nativeInstanceTest); + // registerTest("Icon Test", iconTest); + // registerTest("Cursor Test", cursorTest); + // registerTest("Input Window Test", inputWindowTest); + // registerTest("System Cursor Test", systemCursorTest); + // registerTest("Attach Window Test", attachWindowTest); + // registerTest("Character Event Test", charEventTest); + // registerTest("Native Integration Test", nativeIntegrationTest); + // registerTest("Native Instance Test", nativeInstanceTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - registerTest("Opengl Test", openglTest); - registerTest("Opengl FBConfig Test", openglFBConfigTest); - registerTest("Opengl Context Test", openglContextTest); - registerTest("Opengl Multi Context Test", openglMultiContextTest); + // registerTest("Opengl Test", openglTest); + // registerTest("Opengl FBConfig Test", openglFBConfigTest); + // registerTest("Opengl Context Test", openglContextTest); + // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); diff --git a/tests/window_test.c b/tests/window_test.c index 90579c2..d5b2ad5 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -182,6 +182,43 @@ bool windowTest() return false; } + PalDispatchMode dispatchMode = PAL_DISPATCH_NONE; +#if DISPATCH_MODE_POLL + dispatchMode = PAL_DISPATCH_POLL; +#else + dispatchMode = PAL_DISPATCH_CALLBACK; +#endif // DISPATCH_MODE_POLL + + // set dispatch mode for all events. + for (Uint32 e = 0; e < PAL_EVENT_KEYDOWN; e++) { + palSetEventDispatchMode(eventDriver, e, dispatchMode); + } + + // we set window close to poll + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_CLOSE, + PAL_DISPATCH_POLL); + + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); + + // we set callback mode for modal begin and end. Since we want to capture + // that instantly + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_MODAL_BEGIN, + PAL_DISPATCH_CALLBACK); + + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_MODAL_END, + PAL_DISPATCH_CALLBACK); + + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_DECORATION_MODE, + PAL_DISPATCH_POLL); + // initialize the video system. We pass the event driver to recieve video // related events the video system does not copy the event driver, it must // be valid till the video system is shutdown @@ -263,37 +300,6 @@ bool windowTest() } } #endif // MAKE_TRANSPARENT - PalDispatchMode dispatchMode = PAL_DISPATCH_NONE; -#if DISPATCH_MODE_POLL - dispatchMode = PAL_DISPATCH_POLL; -#else - dispatchMode = PAL_DISPATCH_CALLBACK; -#endif // DISPATCH_MODE_POLL - - // set dispatch mode for all events. - for (Uint32 e = 0; e < PAL_EVENT_KEYDOWN; e++) { - palSetEventDispatchMode(eventDriver, e, dispatchMode); - } - - // we set window close to poll - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_WINDOW_CLOSE, - PAL_DISPATCH_POLL); - - palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); - - // we set callback mode for modal begin and end. Since we want to capture - // that instantly - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_WINDOW_MODAL_BEGIN, - PAL_DISPATCH_CALLBACK); - - palSetEventDispatchMode( - eventDriver, - PAL_EVENT_WINDOW_MODAL_END, - PAL_DISPATCH_CALLBACK); running = true; while (running) { @@ -351,6 +357,15 @@ bool windowTest() } break; } + + case PAL_EVENT_WINDOW_DECORATION_MODE: { + if (event.data == PAL_DECORATION_MODE_CLIENT_SIDE) { + palLog(nullptr, "Window Decoration Mode: Client Side"); + } else { + palLog(nullptr, "Window Decoration Mode: Server Side"); + } + break; + } } } From 42e23f7fa7487770cd6d5188e882667248232ea5 Mon Sep 17 00:00:00 2001 From: nichcode Date: Sun, 16 Nov 2025 19:22:38 +0000 Subject: [PATCH 32/51] update mouse button event --- CHANGELOG.md | 5 +++++ include/pal/pal_event.h | 4 ++-- tests/input_window_test.c | 8 ++++++-- tests/tests_main.c | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc8cd46..1073622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,11 @@ see **native_instance_test.c**. to set the instance or display before initializing. Failure to do this fails. This is a runtime behavior change. The tests are updated in the repo. +- **Pal mouse button event** - The `event.data` now packs both the button and +wayland seat serial (If on wayland). Existing code that reads as a single value +without unpacking will see different values. +This is a runtime behavior change. The **input_window_test.c** has been updated in the repo. + - **palJoinThread()** - ABI remains unchanged but now takes the address of a pointer variable for the return value of the thread. PAL internally reinterpreted into a pointer-to-pointer. This is for ABI stability. \ No newline at end of file diff --git a/include/pal/pal_event.h b/include/pal/pal_event.h index 4891360..c02d42c 100644 --- a/include/pal/pal_event.h +++ b/include/pal/pal_event.h @@ -274,7 +274,7 @@ typedef enum { /** * PAL_EVENT_MOUSE_BUTTONDOWN * - * event.data : mouse button + * event.data : lower 32 bits = button, upper 32 bits = serial * * event.data2 : window * @@ -286,7 +286,7 @@ typedef enum { /** * PAL_EVENT_MOUSE_BUTTONUP * - * event.data : mouse button + * event.data : lower 32 bits = button, upper 32 bits = serial * * event.data2 : window * diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 76ebcdd..81e3104 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -330,19 +330,23 @@ static inline void onKeyup(const PalEvent* event) static inline void onMouseButtondown(const PalEvent* event) { + Uint32 button, serial; // button == low, serial == high + palUnpackUint32(event->data, &button, &serial); PalWindow* window = palUnpackPointer(event->data2); // get mouse button name - const char* name = s_MouseButtonNames[event->data]; + const char* name = s_MouseButtonNames[button]; palLog(nullptr, "%s: Mouse Button pressed: %s", dispatchString, name); } static inline void onMouseButtonup(const PalEvent* event) { + Uint32 button, serial; // button == low, serial == high + palUnpackUint32(event->data, &button, &serial); PalWindow* window = palUnpackPointer(event->data2); // get mouse button name - const char* name = s_MouseButtonNames[event->data]; + const char* name = s_MouseButtonNames[button]; palLog(nullptr, "%s: Mouse Button released: %s", dispatchString, name); } diff --git a/tests/tests_main.c b/tests/tests_main.c index 8213dd4..bfb8a6d 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -29,10 +29,10 @@ int main(int argc, char** argv) // registerTest("Video Test", videoTest); // registerTest("Monitor Test", monitorTest); // registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); + // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); - // registerTest("Input Window Test", inputWindowTest); + registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); From 6eef350ef614d14493d7384bbf6cad3be89dece7 Mon Sep 17 00:00:00 2001 From: nichcode Date: Mon, 17 Nov 2025 19:52:06 +0000 Subject: [PATCH 33/51] custom decoration --- CHANGELOG.md | 3 + src/video/pal_video_linux.c | 27 +- src/video/pal_video_win32.c | 2 +- tests/custom_decoration_test.c | 962 +++++++++++++++++++++++++++++++++ tests/native_instance_test.c | 12 +- tests/tests.h | 1 + tests/tests.lua | 3 +- tests/tests_main.c | 3 +- tests/window_test.c | 4 +- 9 files changed, 1005 insertions(+), 12 deletions(-) create mode 100644 tests/custom_decoration_test.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 1073622..92170a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,9 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - Added native instance example: demonstrating **Native Instance or Display Integration with PAL API**. see **native_instance_test.c**. +- Added custom decoration example: demonstrating **Custom Window Decoration**. +see **custom_decoration_test.c**. + ### Notes - **No ABI changes** - existing code remains compatible. diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 347e696..69d8a6d 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -57,7 +57,6 @@ freely, subject to the following restrictions: #include #include #include -#include #include #include @@ -1437,7 +1436,11 @@ static void pointerHandleEnter( wl_fixed_t surface_y) { WindowData* data = findWindowData((PalWindow*)surface); - if (data && data->cursor) { + if (!data) { + return; + } + + if (data->cursor) { // our window WaylandCursor* cursor = data->cursor; wlPointerSetCursor( @@ -2733,6 +2736,7 @@ static WindowData* findWindowData(PalWindow* window) return &s_Video.windowData[i]; } } + return nullptr; } static void resetMonitorData() @@ -2782,6 +2786,7 @@ static MonitorData* findMonitorData(PalMonitor* monitor) return &s_Video.monitorData[i]; } } + return nullptr; } static void freeMonitorData(PalMonitor* monitor) @@ -4107,7 +4112,7 @@ static void xUpdateVideo() if (mode != PAL_DISPATCH_NONE) { PalEvent event = {0}; event.type = type; - event.data = button; + event.data = palPackUint32(button, 0); event.data2 = palPackPointer(window); palPushEvent(driver, &event); } @@ -6641,6 +6646,15 @@ void wlShutdownVideo() } s_Wl.xkbContextUnref(s_Wl.inputContext); + if (s_Wl.compositor) { + // if compositor was found, all this will be as well + // since we check all at init + s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.compositor); + s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.xdgBase); + s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.shm); + s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.seat); + } + if (!s_Video.platformInstance) { // opened by PAL s_Wl.displayDisconnect(s_Wl.display); @@ -6915,7 +6929,12 @@ PalResult wlCreateWindow( appID = s_Video.className; } - xdgToplevelSetTitle(xdgToplevel, info->title); + const char* title = info->title; + if (!title) { + title = ""; + } + + xdgToplevelSetTitle(xdgToplevel, title); xdgToplevelSetAppId(xdgToplevel, appID); wlSurfaceAddListener(surface, &surfaceListener, data); diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index 54ebf4a..d7bfebe 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -553,7 +553,7 @@ LRESULT CALLBACK videoProc( if (mode != PAL_DISPATCH_NONE) { PalEvent event = {0}; event.type = type; - event.data = button; + event.data = palPackUint32(button, 0); event.data2 = palPackPointer((PalWindow*)hwnd); palPushEvent(driver, &event); } diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c new file mode 100644 index 0000000..8964e2b --- /dev/null +++ b/tests/custom_decoration_test.c @@ -0,0 +1,962 @@ +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 200112L // for linux +#include "pal/pal_video.h" +#include "tests.h" + +#if defined(__linux__) +#include +#include +#include +#include +#include +#include + +#define WINDOW_TITLE "Custom Decoration Test" +#define WL_MARSHAL_FLAG_DESTROY 1 << 0 +#define TITLEBAR_HEIGHT 30 +#define BUTTON_SIZE 15 +#define BUTTON_POSY 8 +#define BUTTON_OFFSET 20 + +struct wl_display; +struct wl_registry; +struct wl_proxy; +struct wl_interface; +struct wl_seat; +struct wl_compositor; +struct wl_subcompositor; +struct wl_shm; +struct wl_buffer; +struct wl_surface; +struct wl_subsurface; +struct xdg_toplevel; + +typedef struct { + int fd; + struct wl_buffer* buffer; + struct wl_surface* surface; + struct wl_subsurface* subsurface; + Uint32* pixels; + Uint64 size; +} WaylandDecoration; + +static struct wl_display *s_Display = nullptr; +static struct wl_registry *s_Registry = nullptr; +static struct wl_compositor *s_Compositor = nullptr; +static struct wl_subcompositor* s_Subcompositor = nullptr; +static struct wl_shm *s_Shm = nullptr; +static struct wl_surface *s_Surface = nullptr; +static struct wl_seat* s_Seat = nullptr; + +static WaylandDecoration s_Decoration = {0}; + +typedef struct wl_display* (*wl_display_connect_fn)(const char*); +typedef void (*wl_display_disconnect_fn)(struct wl_display*); +typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); +typedef int (*wl_display_roundtrip_fn)(struct wl_display*); + +typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( + struct wl_proxy*, + uint32_t, + const struct wl_interface*, + uint32_t, + uint32_t, + ...); + +typedef int (*wl_proxy_add_listener_fn)( + struct wl_proxy*, + void (**)(void), + void*); + +typedef void (*wl_proxy_destroy_fn)(struct wl_proxy*); + +struct wl_interface { + const char* name; + int version; + int method_count; + const struct wl_message* methods; + int event_count; + const struct wl_message* events; +}; + +struct wl_registry_listener { + void (*global)( + void*, + struct wl_registry*, + uint32_t, + const char*, + uint32_t); + + void (*global_remove)( + void*, + struct wl_registry*, + uint32_t); +}; + +static void* s_LibWayland; +static wl_display_connect_fn s_wl_display_connect; +static wl_display_disconnect_fn s_wl_display_disconnect; +static wl_display_roundtrip_fn s_wl_display_roundtrip; +static wl_proxy_get_version_fn s_wl_proxy_get_version; +static wl_proxy_marshal_flags_fn s_wl_proxy_marshal_flags; +static wl_proxy_add_listener_fn s_wl_proxy_add_listener; +static wl_proxy_destroy_fn s_wl_proxy_destroy; + +static const struct wl_interface* registryInterface; +static const struct wl_interface* seatInterface; +static const struct wl_interface* compositorInterface; +static const struct wl_interface* subCompositorInterface; +static const struct wl_interface* shmInterface; +static const struct wl_interface* shmPoolInterface; +static const struct wl_interface* surfaceInterface; +static const struct wl_interface* bufferInterface; +static const struct wl_interface* subsurfaceInterface; + +static inline void* wlRegistryBind( + struct wl_registry* wl_registry, + uint32_t name, + const struct wl_interface* interface, + uint32_t version) +{ + struct wl_proxy* id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_registry, + 0, // WL_REGISTRY_BIND + interface, + version, + 0, + name, + interface->name, + version, + NULL); + + return (void*)id; +} + +static inline int wlRegistryAddListener( + struct wl_registry* wl_registry, + const struct wl_registry_listener* listener, + void* data) +{ + return s_wl_proxy_add_listener( + (struct wl_proxy*)wl_registry, + (void (**)(void))listener, + data); +} + +static inline struct wl_registry* wlDisplayGetRegistry( + struct wl_display* wl_display) +{ + struct wl_proxy* registry; + registry = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_display, + 1, // WL_DISPLAY_GET_REGISTRY + registryInterface, + s_wl_proxy_get_version((struct wl_proxy*)wl_display), + 0, + NULL); + + return (struct wl_registry*)registry; +} + +static inline struct wl_surface* wlCompositorCreateSurface( + struct wl_compositor *wl_compositor) +{ + struct wl_proxy *id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_compositor, + 0, // WL_COMPOSITOR_CREATE_SURFACE, + surfaceInterface, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_compositor), + 0, + NULL); + + return (struct wl_surface*) id; +} + +static inline void wlSurfaceCommit(struct wl_surface *wl_surface) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_surface, + 6, // WL_SURFACE_COMMIT + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_surface), + 0); +} + +static inline void wlSurfaceDestroy(struct wl_surface *wl_surface) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_surface, + 0, // WL_SURFACE_DESTROY + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_surface), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct wl_shm_pool* wlShmCreatePool( + struct wl_shm *wl_shm, + int32_t fd, + int32_t size) +{ + struct wl_proxy *id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_shm, + 0, // WL_SHM_CREATE_POOL + shmPoolInterface, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_shm), + 0, + NULL, + fd, + size); + + return (struct wl_shm_pool *) id; +} + +static inline void wlShmPoolDestroy(struct wl_shm_pool *wl_shm_pool) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_shm_pool, + 1, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_shm_pool), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct wl_buffer* wlShmPoolCreateBuffer( + struct wl_shm_pool *wl_shm_pool, + int32_t offset, + int32_t width, + int32_t height, + int32_t stride, + uint32_t format) +{ + struct wl_proxy *id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_shm_pool, + 0, + bufferInterface, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_shm_pool), + 0, + NULL, + offset, + width, + height, + stride, + format); + + return (struct wl_buffer *) id; +} + +static inline void wlBufferDestroy(struct wl_buffer *wl_buffer) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_buffer, + 0, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_buffer), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void wlSurfaceAttach( + struct wl_surface *wl_surface, + struct wl_buffer *buffer, + int32_t x, + int32_t y) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_surface, + 1, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_surface), + 0, + buffer, + x, + y); +} + +static inline void wlSurfaceDamageBuffer( + struct wl_surface *wl_surface, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_surface, + 9, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_surface), + 0, + x, + y, + width, + height); +} + +static inline void wlSubcompositorDestroy( + struct wl_subcompositor *wl_subcompositor) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_subcompositor, + 0, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_subcompositor), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct wl_subsurface * wlSubcompositorGetSubsurface( + struct wl_subcompositor *wl_subcompositor, + struct wl_surface *surface, + struct wl_surface *parent) +{ + struct wl_proxy *id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_subcompositor, + 1, + subsurfaceInterface, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_subcompositor), + 0, + NULL, + surface, + parent); + + return (struct wl_subsurface *) id; +} + +static inline void wlSubsurfaceDestroy( + struct wl_subsurface *wl_subsurface) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_subsurface, + 0, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_subsurface), + WL_MARSHAL_FLAG_DESTROY); +} + +static inline void wlSubsurfaceSetPosition( + struct wl_subsurface *wl_subsurface, + int32_t x, + int32_t y) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_subsurface, + 1, NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_subsurface), + 0, + x, + y); +} + +static inline void wlSubsurfaceSetDesync( + struct wl_subsurface *wl_subsurface) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) wl_subsurface, + 5, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) wl_subsurface), + 0); +} + +static void globalHandle( + void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) +{ + if (strcmp(interface, "wl_seat") == 0) { + s_Seat = wlRegistryBind(registry, name, seatInterface, 5); + + } else if (strcmp(interface, "wl_compositor") == 0) { + s_Compositor = wlRegistryBind(registry, name, compositorInterface, 4); + + } else if (strcmp(interface, "wl_subcompositor") == 0) { + s_Subcompositor = wlRegistryBind(registry, name, subCompositorInterface, 1); + + } else if (strcmp(interface, "wl_shm") == 0) { + s_Shm = wlRegistryBind(registry, name, shmInterface, 1); + } +} + +static void globalRemove( + void* data, + struct wl_registry* registry, + uint32_t name) +{ + +} + +static const struct wl_registry_listener s_RegistryListener = { + .global = globalHandle, + .global_remove = globalRemove}; + +// xdg-shell protocol +static inline void xdgToplevelMove( + struct xdg_toplevel *xdg_toplevel, + struct wl_seat *seat, + uint32_t serial) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) xdg_toplevel, + 5, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) xdg_toplevel), + 0, + seat, + serial); +} + +static inline void xdgToplevelResize( + struct xdg_toplevel *xdg_toplevel, + struct wl_seat *seat, + uint32_t serial, + uint32_t edges) +{ + s_wl_proxy_marshal_flags( + (struct wl_proxy *) xdg_toplevel, + 6, + NULL, + s_wl_proxy_get_version( + (struct wl_proxy *) xdg_toplevel), + 0, + seat, + serial, + edges); +} + +static void openDisplayWayland() +{ + const char* session = getenv("XDG_SESSION_TYPE"); + if (session) { + if (strcmp(session, "x11") == 0) { + return; + } + } + + s_LibWayland = dlopen("libwayland-client.so.0", RTLD_LAZY); + if (!s_LibWayland) { + return; + } + + // clang-format off + s_wl_display_connect = (wl_display_connect_fn)dlsym( + s_LibWayland, + "wl_display_connect"); + + s_wl_display_disconnect = (wl_display_disconnect_fn)dlsym( + s_LibWayland, + "wl_display_disconnect"); + + s_wl_display_roundtrip = (wl_display_roundtrip_fn)dlsym( + s_LibWayland, + "wl_display_roundtrip"); + + s_wl_proxy_marshal_flags = (wl_proxy_marshal_flags_fn)dlsym( + s_LibWayland, + "wl_proxy_marshal_flags"); + + s_wl_proxy_get_version = (wl_proxy_get_version_fn)dlsym( + s_LibWayland, + "wl_proxy_get_version"); + + s_wl_proxy_add_listener = (wl_proxy_add_listener_fn)dlsym( + s_LibWayland, + "wl_proxy_add_listener"); + + s_wl_proxy_destroy = (wl_proxy_destroy_fn)dlsym( + s_LibWayland, + "wl_proxy_destroy"); + + registryInterface = dlsym(s_LibWayland, "wl_registry_interface"); + seatInterface = dlsym(s_LibWayland, "wl_seat_interface"); + compositorInterface = dlsym(s_LibWayland, "wl_compositor_interface"); + subCompositorInterface = dlsym(s_LibWayland, "wl_subcompositor_interface"); + shmInterface = dlsym(s_LibWayland, "wl_shm_interface"); + shmPoolInterface = dlsym(s_LibWayland, "wl_shm_pool_interface"); + surfaceInterface = dlsym(s_LibWayland, "wl_surface_interface"); + bufferInterface = dlsym(s_LibWayland, "wl_buffer_interface"); + subsurfaceInterface = dlsym(s_LibWayland, "wl_subsurface_interface"); + + struct wl_display* display = s_wl_display_connect(nullptr); + if (display) { + struct wl_registry* registry = wlDisplayGetRegistry(display); + wlRegistryAddListener(registry, &s_RegistryListener, nullptr); + s_wl_display_roundtrip(display); + s_Display = display; + } + + if (!s_Compositor || !s_Subcompositor || !s_Shm || !s_Seat) { + palLog(nullptr, "Failed to get globals"); + return; + } +} + +static void closeDisplayWayland() +{ + if (s_Compositor) { + s_wl_proxy_destroy((struct wl_proxy *)s_Compositor); + s_wl_proxy_destroy((struct wl_proxy *)s_Shm); + s_wl_proxy_destroy((struct wl_proxy *)s_Seat); + wlSubcompositorDestroy(s_Subcompositor); + } + + s_wl_display_disconnect((struct wl_display*)s_Display); + dlclose(s_LibWayland); +} + +static int createShmFile(Uint64 size) +{ + char template[] = "/tmp/pal-shm-XXXXXX"; + int fd = mkstemp(template); + if (fd < 0) { + return -1; + } + + unlink(template); + if (ftruncate(fd, size) < 0) { + return -1; + } + + return fd; +} + +static struct wl_buffer* createShmBuffer( + int width, + int height, + int* outFd, + Uint64* outSize, + Uint32** outPixels) +{ + int stride = width * 4; + Uint64 size = stride * height; + struct wl_buffer* buffer = nullptr; + struct wl_shm_pool* pool = nullptr; + void* data = nullptr; + + int fd = createShmFile(size); + if (fd == -1) { + return nullptr; + } + + data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + return nullptr; + } + + pool = wlShmCreatePool(s_Shm, fd, size); + if (!pool) { + return nullptr; + } + + buffer = wlShmPoolCreateBuffer(pool, 0, width, height, stride, 1); + if (!buffer) { + return nullptr; + } + + wlShmPoolDestroy(pool); + + *outPixels = (Uint32*)data; + *outFd = fd; + *outSize = size; + return buffer; +} + +void fillRect( + uint32_t *pixels, + int stride, + int x, + int y, + int w, + int h, + uint32_t color) +{ + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + pixels[(y + j)* stride + (x + i)] = color; + } + } +} + +// a simple 8x8 bitmap font +// exchange with your font +// Each byte = one row of 8 pixels, MSB = leftmost pixel +static const Uint8 s_FontBasic[128][8] = { + ['A'] = {0x18,0x24,0x42,0x42,0x7E,0x42,0x42,0x42}, + ['B'] = {0x7C,0x42,0x42,0x7C,0x42,0x42,0x42,0x7C}, + ['C'] = {0x3C,0x42,0x40,0x40,0x40,0x40,0x42,0x3C}, + ['D'] = {0x78,0x44,0x42,0x42,0x42,0x42,0x44,0x78}, + ['E'] = {0x7E,0x40,0x40,0x7C,0x40,0x40,0x40,0x7E}, + ['F'] = {0x7E,0x40,0x40,0x7C,0x40,0x40,0x40,0x40}, + ['G'] = {0x3C,0x42,0x40,0x40,0x4E,0x42,0x42,0x3C}, + ['H'] = {0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0x42}, + ['I'] = {0x3E,0x08,0x08,0x08,0x08,0x08,0x08,0x3E}, + ['J'] = {0x1E,0x04,0x04,0x04,0x04,0x44,0x44,0x38}, + ['K'] = {0x42,0x44,0x48,0x70,0x48,0x44,0x42,0x42}, + ['L'] = {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x7E}, + ['M'] = {0x42,0x66,0x5A,0x5A,0x42,0x42,0x42,0x42}, + ['N'] = {0x42,0x62,0x52,0x4A,0x46,0x42,0x42,0x42}, + ['O'] = {0x3C,0x42,0x42,0x42,0x42,0x42,0x42,0x3C}, + ['P'] = {0x7C,0x42,0x42,0x7C,0x40,0x40,0x40,0x40}, + ['Q'] = {0x3C,0x42,0x42,0x42,0x42,0x4A,0x44,0x3A}, + ['R'] = {0x7C,0x42,0x42,0x7C,0x48,0x44,0x42,0x42}, + ['S'] = {0x3C,0x42,0x40,0x3C,0x02,0x02,0x42,0x3C}, + ['T'] = {0x7F,0x49,0x08,0x08,0x08,0x08,0x08,0x08}, + ['U'] = {0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3C}, + ['V'] = {0x42,0x42,0x42,0x42,0x42,0x24,0x24,0x18}, + ['W'] = {0x42,0x42,0x42,0x5A,0x5A,0x5A,0x66,0x42}, + ['X'] = {0x42,0x42,0x24,0x18,0x18,0x24,0x42,0x42}, + ['Y'] = {0x42,0x42,0x24,0x18,0x08,0x08,0x08,0x08}, + ['Z'] = {0x7E,0x02,0x04,0x08,0x10,0x20,0x40,0x7E}, + + ['a'] = {0x00,0x00,0x3C,0x02,0x3E,0x42,0x46,0x3A}, + ['b'] = {0x40,0x40,0x5C,0x62,0x42,0x42,0x62,0x5C}, + ['c'] = {0x00,0x00,0x3C,0x42,0x40,0x40,0x42,0x3C}, + ['d'] = {0x02,0x02,0x3A,0x46,0x42,0x42,0x46,0x3A}, + ['e'] = {0x00,0x00,0x3C,0x42,0x7E,0x40,0x42,0x3C}, + ['f'] = {0x0C,0x12,0x10,0x3C,0x10,0x10,0x10,0x10}, + ['g'] = {0x00,0x00,0x3A,0x46,0x46,0x3E,0x02,0x3C}, + ['h'] = {0x40,0x40,0x5C,0x62,0x42,0x42,0x42,0x42}, + ['i'] = {0x08,0x00,0x18,0x08,0x08,0x08,0x08,0x1C}, + ['j'] = {0x04,0x00,0x0C,0x04,0x04,0x04,0x44,0x38}, + ['k'] = {0x40,0x40,0x44,0x48,0x70,0x48,0x44,0x42}, + ['l'] = {0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x1C}, + ['m'] = {0x00,0x00,0x6C,0x52,0x52,0x42,0x42,0x42}, + ['n'] = {0x00,0x00,0x5C,0x62,0x42,0x42,0x42,0x42}, + ['o'] = {0x00,0x00,0x3C,0x42,0x42,0x42,0x42,0x3C}, + ['p'] = {0x00,0x00,0x5C,0x62,0x42,0x62,0x40,0x40}, + ['q'] = {0x00,0x00,0x3A,0x46,0x42,0x46,0x02,0x02}, + ['r'] = {0x00,0x00,0x5C,0x62,0x40,0x40,0x40,0x40}, + ['s'] = {0x00,0x00,0x3E,0x40,0x3C,0x02,0x42,0x3C}, + ['t'] = {0x10,0x10,0x3C,0x10,0x10,0x12,0x0C,0x00}, + ['u'] = {0x00,0x00,0x42,0x42,0x42,0x42,0x46,0x3A}, + ['v'] = {0x00,0x00,0x42,0x42,0x42,0x24,0x24,0x18}, + ['w'] = {0x00,0x00,0x42,0x42,0x42,0x52,0x52,0x2C}, + ['x'] = {0x00,0x00,0x42,0x24,0x18,0x18,0x24,0x42}, + ['y'] = {0x00,0x00,0x42,0x42,0x46,0x3A,0x02,0x3C}, + ['z'] = {0x00,0x00,0x7E,0x04,0x08,0x10,0x20,0x7E}, +}; + +void drawCharacter( + uint32_t *pixels, + int stride, + int x, + int y, + char c, + uint32_t color) +{ + if (c < 0 || c > 127) return; // not in out font + + const uint8_t *bitmap = s_FontBasic[(int)c]; + for (int row = 0; row < 8; row++) { + uint8_t bits = bitmap[row]; + for (int col = 0; col < 8; col++) { + if (bits & (1 << (7-col))) { + int px = x + col; + int py = y + row; + if (px < stride && py < TITLEBAR_HEIGHT) { + pixels[py*stride + px] = color; + } + } + } + } +} + +void drawText( + uint32_t *pixels, + int stride, + int x, + int y, + const char *text, + uint32_t color) +{ + while (*text) { + drawCharacter(pixels, stride, x, y, *text, color); + x += 8; // advance 8 pixels per character + text++; + } +} + +#endif // __linux__ + +static PalWindowHandleInfoEx s_WinHandle; + +static void createDecoration() +{ + s_Decoration.surface = wlCompositorCreateSurface(s_Compositor); + if (!s_Decoration.surface) { + palLog(nullptr, "Failed to create wayland surface"); + return; + } + + s_Decoration.subsurface = wlSubcompositorGetSubsurface( + s_Subcompositor, + s_Decoration.surface, + (struct wl_surface*)s_WinHandle.nativeWindow + ); + + if (!s_Decoration.subsurface) { + palLog(nullptr, "Failed to create wayland subsurface"); + return; + } + + // make it soo that the decoration updates independently of the window + wlSubsurfaceSetDesync(s_Decoration.subsurface); + wlSubsurfaceSetPosition(s_Decoration.subsurface, 0, 0); + + // create decoration shm buffer + int width = 640; + s_Decoration.buffer = createShmBuffer( + width, // might be different depending on compositor + TITLEBAR_HEIGHT, + &s_Decoration.fd, + &s_Decoration.size, + &s_Decoration.pixels); + + if (!s_Decoration.buffer) { + palLog(nullptr, "Failed to create wayland buffer"); + return; + } + + // write pixels + // title bar color (dark grey) + fillRect( + s_Decoration.pixels, + width, + 0, + 0, + width, + TITLEBAR_HEIGHT, + 0x002F3030); + + // Close button + // this is just a rectangle for this simple example + // to show CSD works with PAL + fillRect( + s_Decoration.pixels, + width, + width - BUTTON_SIZE, + BUTTON_POSY, + BUTTON_SIZE, + TITLEBAR_HEIGHT / 2, // half of the size of the title bar + 0x00AA3333); + + // Maximize button + fillRect( + s_Decoration.pixels, + width, + width - (BUTTON_OFFSET + BUTTON_SIZE), + BUTTON_POSY, + BUTTON_SIZE, + TITLEBAR_HEIGHT / 2, // half of the size of the title bar + 0x0033AA33); + + // Minimize button + fillRect( + s_Decoration.pixels, + width, + width - (BUTTON_OFFSET * 2 + BUTTON_SIZE), + BUTTON_POSY, + BUTTON_SIZE, + TITLEBAR_HEIGHT / 2, // half of the size of the title bar + 0x0033AAAA); + + wlSurfaceAttach(s_Decoration.surface, s_Decoration.buffer, 0, 0); + wlSurfaceDamageBuffer(s_Decoration.surface, 0, 0, width, TITLEBAR_HEIGHT); + wlSurfaceCommit(s_Decoration.surface); + + // wayland requires the main surface to be committed as well + wlSurfaceCommit(s_WinHandle.nativeWindow); + + // draw window title + // this example does not support unicode characters + int textWidth = strlen(WINDOW_TITLE) * 8; // 8x8 font + int x = (width - textWidth) / 2; + + drawText( + s_Decoration.pixels, + width, + x, + 12, + WINDOW_TITLE, + 0x00FFFFFF); + + // set opaque regions for optimazation + // we wont do this in this example +} + +static void destroyDecoration() +{ + wlSubsurfaceDestroy(s_Decoration.subsurface); + wlSurfaceDestroy(s_Decoration.surface); + wlBufferDestroy(s_Decoration.buffer); + munmap((void*)s_Decoration.pixels, s_Decoration.size); + close(s_Decoration.fd); +} + +static void PAL_CALL onEvent( + void* userData, + const PalEvent* event) +{ + if (event->type == PAL_EVENT_MOUSE_BUTTONDOWN) { + Uint32 button, serial; + palUnpackUint32(event->data, &button, &serial); + + if (button == PAL_MOUSE_BUTTON_LEFT) { + // check if the mouse is on the title bar + // and move the window + xdgToplevelMove(s_WinHandle.nativeHandle2, s_Seat, serial); + } + } +} + +bool customDecorationTest() +{ + palLog(nullptr, ""); + palLog(nullptr, "==========================================="); + palLog(nullptr, "Custom Decoration Test"); + palLog(nullptr, "Press Escape or click close button to close Test"); + palLog(nullptr, "==========================================="); + palLog(nullptr, ""); + + openDisplayWayland(); + if (!s_Display) { + // not on wayland + return false; + } + + PalResult result; + + // create an event driver + PalEventDriver* eventDriver = nullptr; + PalEventDriverCreateInfo eventDriverCreateInfo = {0}; + eventDriverCreateInfo.callback = onEvent; + + result = palCreateEventDriver(&eventDriverCreateInfo, &eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to create event driver: %s", error); + return false; + } + + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_CLOSE, + PAL_DISPATCH_POLL); + + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_WINDOW_DECORATION_MODE, + PAL_DISPATCH_POLL); + + palSetEventDispatchMode(eventDriver, PAL_EVENT_KEYDOWN, PAL_DISPATCH_POLL); + + // we use PAL_DISPATCH_CALLBACK for the mouse button to get + // real time events which we then use for moving and resizing + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_MOUSE_BUTTONDOWN, + PAL_DISPATCH_CALLBACK); + + // tell the video system to use out instance rather + // than creating a new one + palSetPreferredInstance((void*)s_Display); + + // initialize the video system. We pass the event driver to recieve video + // related events the video system does not copy the event driver, it must + // be valid till the video system is shutdown + result = palInitVideo(nullptr, eventDriver); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to initialize video: %s", error); + return false; + } + + PalWindow* window = nullptr; + PalWindowCreateInfo createInfo = {0}; + createInfo.height = 480; + createInfo.width = 640; + createInfo.show = true; + createInfo.style = PAL_WINDOW_STYLE_BORDERLESS; + createInfo.title = WINDOW_TITLE; + + result = palCreateWindow(&createInfo, &window); + if (result != PAL_RESULT_SUCCESS) { + const char* error = palFormatResult(result); + palLog(nullptr, "Failed to create window: %s", error); + return false; + } + + // get native handles + s_WinHandle = palGetWindowHandleInfoEx(window); + createDecoration(); + + bool running = true; + while (running) { + // update the video system to push video events + palUpdateVideo(); + + PalEvent event; + while (palPollEvent(eventDriver, &event)) { + switch (event.type) { + case PAL_EVENT_WINDOW_CLOSE: { + running = false; + break; + } + + case PAL_EVENT_KEYDOWN: { + PalKeycode keycode = 0; + palUnpackUint32(event.data, &keycode, nullptr); + if (keycode == PAL_KEYCODE_ESCAPE) { + running = false; + } + break; + } + + case PAL_EVENT_WINDOW_DECORATION_MODE: { + if (event.data == PAL_DECORATION_MODE_CLIENT_SIDE) { + palLog(nullptr, "Window Decoration Mode: Client Side"); + + } else { + palLog(nullptr, "Window Decoration Mode: Server Side"); + } + break; + } + } + } + + } + + // destroy decoration + destroyDecoration(); + + // destroy the window + palDestroyWindow(window); + + // shutdown the video system + palShutdownVideo(); + + // destroy the event driver + palDestroyEventDriver(eventDriver); + + closeDisplayWayland(); + + return true; +} \ No newline at end of file diff --git a/tests/native_instance_test.c b/tests/native_instance_test.c index 8e3ea37..dd4f2be 100644 --- a/tests/native_instance_test.c +++ b/tests/native_instance_test.c @@ -39,6 +39,7 @@ typedef struct wl_display* (*wl_display_connect_fn)(const char*); typedef void (*wl_display_disconnect_fn)(struct wl_display*); typedef uint32_t (*wl_proxy_get_version_fn)(struct wl_proxy*); typedef int (*wl_display_roundtrip_fn)(struct wl_display*); +typedef void (*wl_proxy_destroy_fn)(struct wl_proxy*); typedef struct wl_proxy* (*wl_proxy_marshal_flags_fn)( struct wl_proxy*, @@ -83,6 +84,8 @@ static wl_display_roundtrip_fn s_wl_display_roundtrip; static wl_proxy_get_version_fn s_wl_proxy_get_version; static wl_proxy_marshal_flags_fn s_wl_proxy_marshal_flags; static wl_proxy_add_listener_fn s_wl_proxy_add_listener; +static wl_proxy_destroy_fn s_wl_proxy_destroy; +static struct wl_registry* s_Registry; static const struct wl_interface* registryInterface; @@ -230,12 +233,16 @@ void* openDisplayWayland() s_LibWayland, "wl_proxy_add_listener"); + s_wl_proxy_destroy = (wl_proxy_destroy_fn)dlsym( + s_LibWayland, + "wl_proxy_destroy"); + registryInterface = dlsym(s_LibWayland, "wl_registry_interface"); struct wl_display* display = s_wl_display_connect(nullptr); if (display) { - struct wl_registry* registry = wlDisplayGetRegistry(display); - wlRegistryAddListener(registry, &s_RegistryListener, nullptr); + s_Registry = wlDisplayGetRegistry(display); + wlRegistryAddListener(s_Registry, &s_RegistryListener, nullptr); s_wl_display_roundtrip(display); } @@ -246,6 +253,7 @@ void* openDisplayWayland() void closeDisplayWayland(void* instance) { #ifdef __linux__ + s_wl_proxy_destroy((struct wl_proxy*)s_Registry); s_wl_display_disconnect((struct wl_display*)instance); dlclose(s_LibWayland); #endif // __linux__ diff --git a/tests/tests.h b/tests/tests.h index 1b7e226..65eb69c 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -40,6 +40,7 @@ bool attachWindowTest(); bool charEventTest(); bool nativeIntegrationTest(); bool nativeInstanceTest(); +bool customDecorationTest(); // opengl test bool openglTest(); diff --git a/tests/tests.lua b/tests/tests.lua index 2cd994a..1c19695 100644 --- a/tests/tests.lua +++ b/tests/tests.lua @@ -43,7 +43,8 @@ project "tests" "attach_window_test.c", "char_event_test.c", "native_integration_test.c", - "native_instance_test.c" + "native_instance_test.c", + "custom_decoration_test.c" } end diff --git a/tests/tests_main.c b/tests/tests_main.c index bfb8a6d..f99c358 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -32,12 +32,13 @@ int main(int argc, char** argv) // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); - registerTest("Input Window Test", inputWindowTest); + // registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); // registerTest("Native Instance Test", nativeInstanceTest); + registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid diff --git a/tests/window_test.c b/tests/window_test.c index d5b2ad5..56abc3a 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -229,9 +229,6 @@ bool windowTest() return false; } - // get video system features - PalVideoFeatures64 features = palGetVideoFeaturesEx(); - // fill the create info struct createInfo.monitor = nullptr; // use default monitor createInfo.height = 480; @@ -240,6 +237,7 @@ bool windowTest() createInfo.style = PAL_WINDOW_STYLE_RESIZABLE; // check if we support decorated windows (title bar, close etc) + PalVideoFeatures64 features = palGetVideoFeaturesEx(); if (!(features & PAL_VIDEO_FEATURE64_DECORATED_WINDOW)) { // if we dont support, we need to create a borderless window // and create the decorations ourselves From a8cbb5644bf7ca165eee6e2c5f4f464d2bef4932 Mon Sep 17 00:00:00 2001 From: nichcode Date: Mon, 17 Nov 2025 20:20:16 +0000 Subject: [PATCH 34/51] responsive decorations --- src/video/pal_video_linux.c | 7 ++--- tests/custom_decoration_test.c | 50 +++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 69d8a6d..a1b3466 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -1408,9 +1408,7 @@ static void surfaceHandleEnter( struct wl_surface* surface, struct wl_output* output) { - int a = 10; - // TODO: - + palLog(nullptr, "Surface Enter"); } static void surfaceHandleLeave( @@ -1418,8 +1416,7 @@ static void surfaceHandleLeave( struct wl_surface* surface, struct wl_output* output) { - int b = 10; - // TODO: + palLog(nullptr, "Surface Leave"); } static struct wl_surface_listener surfaceListener = { diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index 8964e2b..69cab0d 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -33,10 +33,16 @@ struct xdg_toplevel; typedef struct { int fd; + + // cache mouse position + int mouseX; + int mouseY; + struct wl_buffer* buffer; struct wl_surface* surface; struct wl_subsurface* subsurface; Uint32* pixels; + PalEventDriver* driver; // a pointer to the event driver Uint64 size; } WaylandDecoration; @@ -800,6 +806,8 @@ static void createDecoration() // set opaque regions for optimazation // we wont do this in this example + // this does not include any hover effects and any shadows + // and any fancy stuff } static void destroyDecoration() @@ -822,8 +830,41 @@ static void PAL_CALL onEvent( if (button == PAL_MOUSE_BUTTON_LEFT) { // check if the mouse is on the title bar // and move the window - xdgToplevelMove(s_WinHandle.nativeHandle2, s_Seat, serial); + // use the cache mouse position to check if the mouse + // is in the title bar + int x = s_Decoration.mouseX; + int y = s_Decoration.mouseY; + // width should reflect window width + if (x >= 0 && x < 640 && y >= 0 && y < TITLEBAR_HEIGHT) { + xdgToplevelMove(s_WinHandle.nativeHandle2, s_Seat, serial); + + // Optionally check for another click and maximize the window + // maybe set a bool or query mouse button state + // we will skip it for this example + } + + // we skip maximize and minimize button for simplicity + // we just deal with the close button + int buttonX = 640 - BUTTON_SIZE; + if (x >= buttonX && + x < buttonX + BUTTON_SIZE && + y >= BUTTON_POSY && + y < BUTTON_POSY + BUTTON_SIZE) { + // inside close button + // trigger a window close event + + PalEvent event = {0}; + event.data2 = palPackPointer(s_WinHandle.nativeWindow); + event.type = PAL_EVENT_WINDOW_CLOSE; + palPushEvent(s_Decoration.driver, &event); + } } + + } else if (event->type == PAL_EVENT_MOUSE_MOVE) { + Int32 x, y; + palUnpackInt32(event->data, &x, &y); + s_Decoration.mouseX = x; + s_Decoration.mouseY = y; } } @@ -833,6 +874,7 @@ bool customDecorationTest() palLog(nullptr, "==========================================="); palLog(nullptr, "Custom Decoration Test"); palLog(nullptr, "Press Escape or click close button to close Test"); + palLog(nullptr, "This only implements close button and window movement"); palLog(nullptr, "==========================================="); palLog(nullptr, ""); @@ -875,6 +917,11 @@ bool customDecorationTest() PAL_EVENT_MOUSE_BUTTONDOWN, PAL_DISPATCH_CALLBACK); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_MOUSE_MOVE, + PAL_DISPATCH_CALLBACK); + // tell the video system to use out instance rather // than creating a new one palSetPreferredInstance((void*)s_Display); @@ -906,6 +953,7 @@ bool customDecorationTest() // get native handles s_WinHandle = palGetWindowHandleInfoEx(window); + s_Decoration.driver = eventDriver; createDecoration(); bool running = true; From 3c2b155c65dad8989236d4a0932f0870ae932b41 Mon Sep 17 00:00:00 2001 From: nichcode Date: Mon, 17 Nov 2025 22:57:32 +0000 Subject: [PATCH 35/51] fix monitor queries --- src/video/pal_video_linux.c | 324 +++++++++++++++--------------------- 1 file changed, 136 insertions(+), 188 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index a1b3466..95a29b2 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -167,7 +167,12 @@ typedef struct { int y; Uint32 w; Uint32 h; + Uint32 refreshRate; + PalOrientation orientation; PalMonitor* monitor; + void* output; + PalMonitorMode mode; // wayland only sends current + char name[32]; } MonitorData; typedef struct { @@ -947,7 +952,6 @@ typedef void (*wl_egl_window_resize_fn)( typedef struct { bool checkFeatures; - bool modesPhase; int monitorCount; void* handle; @@ -1022,12 +1026,6 @@ typedef struct { wl_egl_window_resize_fn eglWindowResize; } Wayland; -typedef struct { - int count; - int maxCount; - PalMonitorMode* modes; -} MonitorModesData; - typedef struct { struct wl_buffer* buffer; struct wl_surface* surface; @@ -6205,6 +6203,101 @@ static struct wl_buffer* createShmBuffer( return buffer; } +static void wlOutputGeometry( + void* data, + struct wl_output* output, + int32_t x, + int32_t y, + int32_t, // we dont need physical size + int32_t, // we dont need physical size + int32_t, // we dont need subpixel + const char* make, + const char* model, + int32_t transform) +{ + MonitorData* monitorData = data; + monitorData->x = x; + monitorData->y = y; + + switch (transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_180: { + monitorData->orientation = PAL_ORIENTATION_LANDSCAPE; + break; + } + + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: { + monitorData->orientation = PAL_ORIENTATION_PORTRAIT; + break; + } + + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: { + monitorData->orientation = PAL_ORIENTATION_LANDSCAPE_FLIPPED; + break; + } + + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: { + monitorData->orientation = PAL_ORIENTATION_PORTRAIT_FLIPPED; + break; + } + } + + snprintf(monitorData->name, 32, "%s %s", make, model); +} + +static void wlOutputMode( + void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ + MonitorData* monitorData = data; + if (flags & WL_OUTPUT_MODE_CURRENT) { + monitorData->w = width; + monitorData->h = height; + monitorData->refreshRate = (refresh + 500) / 1000; + + // wayland only sends the current mode + monitorData->mode.bpp = 0; + monitorData->mode.width = width; + monitorData->mode.height = height; + monitorData->mode.refreshRate = (refresh + 500) / 1000; + } +} + +static void wlOutputScale( + void* data, + struct wl_output* output, + int32_t scale) +{ + MonitorData* monitorData = data; + float dpi = (float)scale * 96.0f; + monitorData->dpi = (Uint32)dpi; +} + +static void wlOutputDone( + void* data, + struct wl_output* output) +{ +} + +static const struct wl_output_listener s_OutputListener = { + .geometry = wlOutputGeometry, + .mode = wlOutputMode, + .done = wlOutputDone, + .scale = wlOutputScale}; + +static const struct wl_output_listener s_DefaultModeListener = { + .geometry = wlOutputGeometry, + .mode = wlOutputMode, + .done = wlOutputDone, + .scale = wlOutputScale}; + static void globalHandle( void* data, struct wl_registry* registry, @@ -6277,6 +6370,11 @@ static void globalHandle( } PalMonitor* m = TO_PAL_HANDLE(PalMonitor, name); + struct wl_output* output = nullptr; + output = wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); + wlOutputAddListener(output, &s_OutputListener, monitorData); + + monitorData->output = (void*)output; monitorData->monitor = m; s_Wl.monitorCount++; } @@ -6291,138 +6389,15 @@ static void globalRemove( MonitorData* monitorData = findMonitorData(m); if (monitorData) { monitorData->used = false; + s_Wl.proxyDestroy((struct wl_proxy*)monitorData->output); s_Wl.monitorCount--; } } -static void wlOutputGeometry( - void* data, - struct wl_output* output, - int32_t x, - int32_t y, - int32_t, // we dont need physical size - int32_t, // we dont need physical size - int32_t, // we dont need subpixel - const char* make, - const char* model, - int32_t transform) -{ - if (s_Wl.modesPhase) { - return; - } - - PalMonitorInfo* info = (PalMonitorInfo*)data; - info->x = x; - info->y = y; - - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_180: { - info->orientation = PAL_ORIENTATION_LANDSCAPE; - break; - } - - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: { - info->orientation = PAL_ORIENTATION_PORTRAIT; - break; - } - - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: { - info->orientation = PAL_ORIENTATION_LANDSCAPE_FLIPPED; - break; - } - - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: { - info->orientation = PAL_ORIENTATION_PORTRAIT_FLIPPED; - break; - } - } - - snprintf(info->name, 32, "%s %s", make, model); -} - -static void wlOutputMode( - void* data, - struct wl_output* output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ - PalMonitorInfo* info = (PalMonitorInfo*)data; - if (flags & WL_OUTPUT_MODE_CURRENT) { - info->width = width; - info->height = height; - info->refreshRate = (refresh + 500) / 1000; - } -} - -static void wlOutputMonitorModes( - void* data, - struct wl_output* output, - uint32_t flags, - int32_t width, - int32_t height, - int32_t refresh) -{ - MonitorModesData* modesInfo = (MonitorModesData*)data; - if (modesInfo->modes) { - if (modesInfo->count < modesInfo->maxCount) { - PalMonitorMode* mode = &modesInfo->modes[modesInfo->count]; - mode->width = width; - mode->height = height; - mode->refreshRate = (refresh + 500) / 1000; - mode->bpp = 0; // default - } - } - modesInfo->count++; -} - -static void wlOutputScale( - void* data, - struct wl_output* output, - int32_t scale) -{ - if (s_Wl.modesPhase) { - return; - } - - PalMonitorInfo* info = (PalMonitorInfo*)data; - float dpi = (float)scale * 96.0f; - info->dpi = (Uint32)dpi; -} - -static void wlOutputDone( - void* data, - struct wl_output* output) -{ -} - static const struct wl_registry_listener s_RegistryListener = { .global = globalHandle, .global_remove = globalRemove}; -static const struct wl_output_listener s_OutputListener = { - .geometry = wlOutputGeometry, - .mode = wlOutputMode, - .done = wlOutputDone, - .scale = wlOutputScale}; - -static const struct wl_output_listener s_ModesListener = { - .geometry = wlOutputGeometry, - .mode = wlOutputMonitorModes, - .done = wlOutputDone, - .scale = wlOutputScale}; - -static const struct wl_output_listener s_DefaultModeListener = { - .geometry = wlOutputGeometry, - .mode = wlOutputMode, - .done = wlOutputDone, - .scale = wlOutputScale}; - PalResult wlInitVideo() { // load wayland libray @@ -6587,7 +6562,6 @@ PalResult wlInitVideo() // clang-format on // initialize wayland - s_Wl.modesPhase = false; s_Wl.checkFeatures = true; s_Wl.monitorCount = 0; setupXdgShellProtocol(); @@ -6708,11 +6682,11 @@ void wlUpdateVideo() } } - s_Wl.displayFlush(s_Wl.display); while (s_Wl.prepareRead(s_Wl.display) != 0) { s_Wl.dispatchPending(s_Wl.display); } + s_Wl.displayFlush(s_Wl.display); int fd = s_Wl.displayGetFd(s_Wl.display); struct pollfd pfd = {fd, POLLIN, 0}; if (poll(&pfd, 1, 0) > 0) { @@ -6725,7 +6699,6 @@ void wlUpdateVideo() // dispatch events that were read s_Wl.dispatchPending(s_Wl.display); - s_Wl.displayFlush(s_Wl.display); } PalResult wlEnumerateMonitors( @@ -6760,27 +6733,21 @@ PalResult wlGetMonitorInfo( PalMonitor* monitor, PalMonitorInfo* info) { - uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); - // bind the monitor and get its information - struct wl_output* output = - wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); - - if (!output) { + MonitorData* monitorData = findMonitorData(monitor); + if (!monitorData) { return PAL_RESULT_INVALID_MONITOR; } - s_Wl.modesPhase = false; - wlOutputAddListener(output, &s_OutputListener, info); - s_Wl.displayRoundtrip(s_Wl.display); - s_Wl.proxyDestroy((struct wl_proxy*)output); + info->dpi = monitorData->dpi; + info->x = monitorData->x; + info->y = monitorData->y; + info->width = monitorData->w; + info->height = monitorData->h; + info->refreshRate = monitorData->refreshRate; + info->orientation = monitorData->orientation; - PalMonitor* primary = nullptr; - wlGetPrimaryMonitor(&primary); - if (monitor == primary) { - info->primary = true; - } else { - info->primary = false; - } + info->primary = false; // no way to query + strcpy(info->name, monitorData->name); return PAL_RESULT_SUCCESS; } @@ -6790,28 +6757,21 @@ PalResult wlEnumerateMonitorModes( Int32* count, PalMonitorMode* modes) { - uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); - // bind the monitor and get its information - struct wl_output* output = - wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); - - if (!output) { + MonitorData* monitorData = findMonitorData(monitor); + if (!monitorData) { return PAL_RESULT_INVALID_MONITOR; } - MonitorModesData data; - data.count = 0; - data.modes = modes; - data.maxCount = modes ? *count : 0; - - // we need only modes information - s_Wl.modesPhase = true; - wlOutputAddListener(output, &s_ModesListener, &data); - s_Wl.displayRoundtrip(s_Wl.display); - s_Wl.proxyDestroy((struct wl_proxy*)output); + if (modes && *count > 0) { + PalMonitorMode* mode = &modes[0]; + mode->bpp = monitorData->mode.bpp; + mode->width = monitorData->mode.width; + mode->height = monitorData->mode.height; + mode->refreshRate = monitorData->mode.refreshRate; + } if (!modes) { - *count = data.count; + *count = 1; // wayland only gives the active mode } return PAL_RESULT_SUCCESS; @@ -6821,28 +6781,16 @@ PalResult wlGetCurrentMonitorMode( PalMonitor* monitor, PalMonitorMode* mode) { - uint32_t name = FROM_PAL_HANDLE(uint32_t, monitor); - // bind the monitor and get its information - struct wl_output* output = - wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); - - if (!output) { + MonitorData* monitorData = findMonitorData(monitor); + if (!monitorData) { return PAL_RESULT_INVALID_MONITOR; } - // we dont want to add another listener just to get the - // default monitor display mode - PalMonitorInfo tmpInfo; - s_Wl.modesPhase = true; - wlOutputAddListener(output, &s_OutputListener, &tmpInfo); - s_Wl.displayRoundtrip(s_Wl.display); - s_Wl.proxyDestroy((struct wl_proxy*)output); - - // fill out display mode with the one we got back - mode->bpp = 0; // default - mode->refreshRate = tmpInfo.refreshRate; - mode->width = tmpInfo.width; - mode->height = tmpInfo.height; + // this is the same as the current mode + mode->bpp = monitorData->mode.bpp; + mode->width = monitorData->mode.width; + mode->height = monitorData->mode.height; + mode->refreshRate = monitorData->mode.refreshRate; return PAL_RESULT_SUCCESS; } @@ -6910,6 +6858,7 @@ PalResult wlCreateWindow( return PAL_RESULT_PLATFORM_FAILURE; } + wlSurfaceAddListener(surface, &surfaceListener, data); xdgSurface = xdgWmBaseGetXdgSurface(s_Wl.xdgBase, surface); if (!xdgSurface) { return PAL_RESULT_PLATFORM_FAILURE; @@ -6934,7 +6883,6 @@ PalResult wlCreateWindow( xdgToplevelSetTitle(xdgToplevel, title); xdgToplevelSetAppId(xdgToplevel, appID); - wlSurfaceAddListener(surface, &surfaceListener, data); xdgToplevelAddListener(xdgToplevel, &xdgToplevelListener, data); xdgSurfaceAddListener(xdgSurface, &xdgSurfaceListener, data); From 41135eb53f42544a0466b2442cd4b59859b6bbcd Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 09:46:46 +0000 Subject: [PATCH 36/51] update changelog --- CHANGELOG.md | 13 ++++++++--- src/video/pal_video_linux.c | 41 +++++++++++++++++++++++++--------- tests/custom_decoration_test.c | 5 ++++- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92170a4..10a1922 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,10 +78,10 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. - **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. - **Video:** Added **PAL_CONFIG_BACKEND_GLES** to `PalFBConfigBackend` enum. -- **Video:** Added **palSetPreferredInstance()** to use native instance or display wth PAL video. +- **Video:** Added **palSetPreferredInstance()** to set the native instance or display PAL video should use rather than creating a new one. - **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. -- **OpenGL:** Added **palGLSetInstance()** to set the instance or display handle. +- **OpenGL:** Added **palGLSetInstance()** o set the native instance or display PAL opengl should use. This must be set before calling **palInitGL()**. - **OpenGL:** Added **palGLGetBackend()** to get the opengl backend. - **Event:** Added **PAL_EVENT_WINDOW_DECORATION_MODE** to `PalEventType` enum. - **Event:** Added **PalDecorationMode** enum. @@ -110,4 +110,11 @@ This is a runtime behavior change. The **input_window_test.c** has been updated - **palJoinThread()** - ABI remains unchanged but now takes the address of a pointer variable for the return value of the thread. -PAL internally reinterpreted into a pointer-to-pointer. This is for ABI stability. \ No newline at end of file +PAL internally reinterpreted into a pointer-to-pointer. This is for ABI stability. + +Example – Join thread and get the return value +```c +void* retval; +palJoinThread(thread, &retval); +``` + diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 95a29b2..ba31e4e 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -168,9 +168,9 @@ typedef struct { Uint32 w; Uint32 h; Uint32 refreshRate; + Uint32 wlName; PalOrientation orientation; PalMonitor* monitor; - void* output; PalMonitorMode mode; // wayland only sends current char name[32]; } MonitorData; @@ -1042,6 +1042,7 @@ static Wayland s_Wl = {0}; #if PAL_HAS_WAYLAND static WindowData* findWindowData(PalWindow* window); +static MonitorData* findMonitorData(PalMonitor* monitor); static inline Uint64 getTime() { @@ -1406,6 +1407,19 @@ static void surfaceHandleEnter( struct wl_surface* surface, struct wl_output* output) { + WindowData* data = userData; + MonitorData* monitorData = findMonitorData((PalMonitor*)output); + if (!monitorData) { + return; + } + + if (data->dpi == 0) { + data->dpi = monitorData->dpi; + return; + } + + // TODO: check and push dpi + palLog(nullptr, "Surface Enter"); } @@ -6369,13 +6383,12 @@ static void globalHandle( return; } - PalMonitor* m = TO_PAL_HANDLE(PalMonitor, name); struct wl_output* output = nullptr; output = wlRegistryBind(s_Wl.registry, name, s_Wl.outputInterface, 3); wlOutputAddListener(output, &s_OutputListener, monitorData); - monitorData->output = (void*)output; - monitorData->monitor = m; + monitorData->wlName = name; + monitorData->monitor = (PalMonitor*)output; s_Wl.monitorCount++; } } @@ -6385,12 +6398,14 @@ static void globalRemove( struct wl_registry* registry, uint32_t name) { - PalMonitor* m = TO_PAL_HANDLE(PalMonitor, name); - MonitorData* monitorData = findMonitorData(m); - if (monitorData) { - monitorData->used = false; - s_Wl.proxyDestroy((struct wl_proxy*)monitorData->output); - s_Wl.monitorCount--; + for (int i = 0; i < s_Video.maxMonitorData; ++i) { + if (s_Video.monitorData[i].used && + s_Video.monitorData[i].wlName == name) { + MonitorData* data = &s_Video.monitorData[i]; + data->used = false; + s_Wl.proxyDestroy((struct wl_proxy*)data->monitor); + s_Wl.monitorCount--; + } } } @@ -6978,6 +6993,12 @@ PalResult wlCreateWindow( } } + // Since wayland does not have a way to set unique data + // to a surface without taking control from users + // we might implement a simple hash map to do that + // but at the moment a linear search is fine + // FIXME: Implement a window hash map + data->skipState = false; data->skipConfigure = false; *outWindow = data->window; diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index 69cab0d..cdcfac6 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -874,7 +874,10 @@ bool customDecorationTest() palLog(nullptr, "==========================================="); palLog(nullptr, "Custom Decoration Test"); palLog(nullptr, "Press Escape or click close button to close Test"); - palLog(nullptr, "This only implements close button and window movement"); + + palLog(nullptr, + "This only implements close and window movement for simplicity"); + palLog(nullptr, "==========================================="); palLog(nullptr, ""); From 7f89561000bf151e7012bbc04e990a1bf2ba6b56 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 10:36:55 +0000 Subject: [PATCH 37/51] add dpi event wayland --- src/video/pal_video_linux.c | 81 ++++++++++++++++++++++++++++++++-- tests/custom_decoration_test.c | 9 ++++ tests/window_test.c | 4 +- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index ba31e4e..e646818 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -71,6 +71,7 @@ freely, subject to the following restrictions: #define TO_PAL_HANDLE(type, val) ((type*)(UintPtr)(val)) #define FROM_PAL_HANDLE(type, handle) ((type)(UintPtr)(handle)) +#define MAX_SPAN_MONITORS 4 #pragma region EGL Typedefs @@ -130,6 +131,11 @@ typedef EGLBoolean (*eglGetConfigsFn)( #pragma endregion +typedef struct { + void* monitor; + int dpi; +} SpanMonitor; + typedef struct { bool skipConfigure; bool skipState; @@ -143,6 +149,7 @@ typedef struct { int y; Uint32 w; int dpi; + int monitorCount; Uint32 h; PalWindowState state; PalWindow* window; @@ -158,6 +165,7 @@ typedef struct { void* decoration; void* cursor; void* eglWindow; + SpanMonitor monitors[MAX_SPAN_MONITORS]; } WindowData; typedef struct { @@ -1413,14 +1421,63 @@ static void surfaceHandleEnter( return; } + // wayland sends multiple surface enter events + // if the surface spans multiple monitors + // we get all and return the highest dpi + // this assumes a surface can only span 4 monitors + // at the sametime but this might be wrong + // FIXME: check if we need more + if (data->monitorCount < MAX_SPAN_MONITORS) { + SpanMonitor* span = &data->monitors[data->monitorCount]; + span->monitor = output; + span->dpi = monitorData->dpi; + data->monitorCount++; + } + if (data->dpi == 0) { - data->dpi = monitorData->dpi; + // this is triggered when the window is created + // we cache the DPI and skip the event + data->dpi = monitorData->dpi; + return; + } + + // the code below should be skipped if users are not + // interested in DPI changed events + PalDispatchMode mode = PAL_DISPATCH_NONE; + PalEventType type = PAL_EVENT_MONITOR_DPI_CHANGED; + if (!s_Video.eventDriver) { + return; + } + + PalEventDriver* driver = s_Video.eventDriver; + mode = palGetEventDispatchMode(driver, type); + if (mode == PAL_DISPATCH_NONE) { return; } - // TODO: check and push dpi + // get the highest dpi and check if the it has changed + int maxDPI = 96; // baseline + for (int i = 0; i < data->monitorCount; i++) { + if (!data->monitors[i].monitor) { + // not a valid index. continue + continue; + } + + if (data->monitors[i].dpi > maxDPI) { + // new highest + maxDPI = data->monitors[i].dpi; + } + } + + if (maxDPI != data->dpi) { + data->dpi = maxDPI; - palLog(nullptr, "Surface Enter"); + PalEvent event = {0}; + event.type = type; + event.data = maxDPI; + event.data2 = palPackPointer((void*)data->window); + palPushEvent(driver, &event); + } } static void surfaceHandleLeave( @@ -1428,7 +1485,23 @@ static void surfaceHandleLeave( struct wl_surface* surface, struct wl_output* output) { - palLog(nullptr, "Surface Leave"); + // remove the monitor from our span monitor list + WindowData* data = userData; + MonitorData* monitorData = findMonitorData((PalMonitor*)output); + if (!monitorData) { + return; + } + + for (int i = 0; i < data->monitorCount; i++) { + if (data->monitors[i].monitor == (void*)output) { + // found our monitor + // we might want to pack the array but its just 4 monitors + // so we leave it like that + data->monitors[i].monitor = nullptr; + data->monitorCount--; + break; + } + } } static struct wl_surface_listener surfaceListener = { diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index cdcfac6..735112f 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -865,6 +865,9 @@ static void PAL_CALL onEvent( palUnpackInt32(event->data, &x, &y); s_Decoration.mouseX = x; s_Decoration.mouseY = y; + + } else if (event->type == PAL_EVENT_MONITOR_DPI_CHANGED) { + palLog(nullptr, "Monitor DPI: %d", event->data); } } @@ -884,6 +887,7 @@ bool customDecorationTest() openDisplayWayland(); if (!s_Display) { // not on wayland + palLog(nullptr, "Not on wayland platform"); return false; } @@ -925,6 +929,11 @@ bool customDecorationTest() PAL_EVENT_MOUSE_MOVE, PAL_DISPATCH_CALLBACK); + palSetEventDispatchMode( + eventDriver, + PAL_EVENT_MONITOR_DPI_CHANGED, + PAL_DISPATCH_CALLBACK); + // tell the video system to use out instance rather // than creating a new one palSetPreferredInstance((void*)s_Display); diff --git a/tests/window_test.c b/tests/window_test.c index 56abc3a..0b6281a 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -102,7 +102,7 @@ static inline void onWindowModalEnd(const PalEvent* event) static inline void onMonitorDPI(const PalEvent* event) { PalWindow* window = palUnpackPointer(event->data2); - palLog(nullptr, "%s: Display DPI: %d", dispatchString, event->data); + palLog(nullptr, "%s: Monitor DPI: %d", dispatchString, event->data); } static inline void onMonitorList(const PalEvent* event) @@ -110,7 +110,7 @@ static inline void onMonitorList(const PalEvent* event) PalWindow* window = palUnpackPointer(event->data2); palLog( nullptr, - "%s: Display (monitor) List has been changed", + "%s: Monitor List has been changed", dispatchString); } From b9a711ab0d1098a7126af82e94dcbb5a0cdd35f7 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 13:51:58 +0000 Subject: [PATCH 38/51] add dynamic platform error linux --- src/opengl/pal_opengl_linux.c | 23 ++++++++++++++++ src/opengl/pal_opengl_win32.c | 13 +++++++++ src/pal_core.c | 50 ++++++++++++++++++++++++++++++++-- src/system/pal_system_linux.c | 8 ++++++ src/system/pal_system_win32.c | 4 +++ src/thread/pal_thread_linux.c | 6 ++++ src/thread/pal_thread_win32.c | 8 ++++++ src/video/pal_video_linux.c | 35 +++++++++++++++++++++++- src/video/pal_video_win32.c | 25 +++++++++++++++++ tests/custom_decoration_test.c | 9 +++--- 10 files changed, 174 insertions(+), 7 deletions(-) diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index 7c5dc92..78dc1e7 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -25,6 +25,8 @@ freely, subject to the following restrictions: // Includes // ================================================== +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 200112L #include "pal/pal_opengl.h" #include @@ -340,6 +342,8 @@ static void freeContextData(PalGLContext* context) } } +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -355,6 +359,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } if (!s_GL.platformDisplay) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -370,6 +375,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.handle = dlopen("libEGL.so", RTLD_LAZY); if (!s_GL.handle) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -447,6 +453,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) !s_GL.eglQueryString || !s_GL.eglGetConfigs || !s_GL.eglCreateWindowSurface) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -464,16 +471,19 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } if (!s_GL.eglBindAPI(s_GL.apiType)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } EGLDisplay display = s_GL.eglGetDisplay(s_GL.platformDisplay); EGLDisplay * tmpDisplay = EGL_NO_DISPLAY; if (display == EGL_NO_DISPLAY) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } if (!s_GL.eglInitialize(display, nullptr, nullptr)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -494,10 +504,12 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) // create a default display and use that tmpDisplay = s_GL.eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } if (!s_GL.eglInitialize(tmpDisplay, nullptr, nullptr)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } else { @@ -507,12 +519,14 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.eglChooseConfig(tmpDisplay, attribs, &config, 1, &numConfigs); if (!config) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } s_GL.eglGetConfigAttrib(tmpDisplay, config, EGL_RENDERABLE_TYPE, &type); if (!(type & s_GL.apiTypeBit)) { // we must support the required API + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -521,6 +535,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) if (s_GL.apiType == EGL_OPENGL_API) { if (!(type & EGL_OPENGL_ES2_BIT)) { // FIXME: create a dummy window if EGL_OPENGL_ES2_BIT + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -537,6 +552,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) pBufferAttribs); if (surface == EGL_NO_SURFACE) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -567,6 +583,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } if (context == EGL_NO_CONTEXT) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -726,6 +743,7 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( // get the number of configs and filter the ones for opengl desktop EGLint numConfigs = 0; if (!s_GL.eglGetConfigs(s_GL.display, nullptr, 0, &numConfigs)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1011,6 +1029,7 @@ PalResult PAL_CALL palCreateGLContext( // we need to get the EGL config from the user supplied index EGLint numConfigs = 0; if (!s_GL.eglGetConfigs(s_GL.display, nullptr, 0, &numConfigs)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1126,6 +1145,7 @@ PalResult PAL_CALL palCreateGLContext( return PAL_RESULT_INVALID_GL_VERSION; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1153,6 +1173,7 @@ PalResult PAL_CALL palCreateGLContext( return PAL_RESULT_INVALID_GL_FBCONFIG; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1233,6 +1254,7 @@ PalResult PAL_CALL palMakeContextCurrent( return PAL_RESULT_INVALID_GL_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1284,6 +1306,7 @@ PalResult PAL_CALL palSwapBuffers( return PAL_RESULT_INVALID_GL_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/opengl/pal_opengl_win32.c b/src/opengl/pal_opengl_win32.c index f4a23f7..88a23e8 100644 --- a/src/opengl/pal_opengl_win32.c +++ b/src/opengl/pal_opengl_win32.c @@ -239,6 +239,8 @@ static inline bool checkExtension( } } +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -287,12 +289,14 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) 0); if (!s_Wgl.window) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } s_Gdi.handle = LoadLibraryA("gdi32.dll"); s_Wgl.opengl = LoadLibraryA("opengl32.dll"); if (!s_Gdi.handle || !s_Wgl.opengl) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -344,6 +348,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) !s_Gdi.describePixelFormat || !s_Gdi.swapBuffers || !s_Gdi.setPixelFormat) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -351,6 +356,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) !s_Wgl.wglCreateContext || !s_Wgl.wglDeleteContext || !s_Wgl.wglMakeCurrent) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -373,6 +379,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_Wgl.context = s_Wgl.wglCreateContext(s_Wgl.hdc); if (!s_Wgl.wglMakeCurrent(s_Wgl.hdc, s_Wgl.context)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -565,6 +572,7 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( 1, &configAttrib, &nativeCount)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -911,6 +919,7 @@ PalResult PAL_CALL palCreateGLContext( if (error == ERROR_INVALID_PROFILE_ARB) { return PAL_RESULT_INVALID_GL_PROFILE; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -919,6 +928,7 @@ PalResult PAL_CALL palCreateGLContext( // create context with legacy wgl functions context = s_Wgl.wglCreateContext(hdc); if (!context) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -927,6 +937,7 @@ PalResult PAL_CALL palCreateGLContext( if (!s_Wgl.wglShareLists(share, context)) { s_Wgl.wglDeleteContext(context); ReleaseDC((HWND)info->window->window, hdc); + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -970,6 +981,7 @@ PalResult PAL_CALL palMakeContextCurrent( return PAL_RESULT_INVALID_GL_CONTEXT; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1019,6 +1031,7 @@ PalResult PAL_CALL palSwapBuffers( return PAL_RESULT_INVALID_GL_FBCONFIG; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/pal_core.c b/src/pal_core.c index ed2015b..42dcee0 100644 --- a/src/pal_core.c +++ b/src/pal_core.c @@ -26,12 +26,14 @@ freely, subject to the following restrictions: // ================================================== #ifdef __linux__ +#define _GNU_SOURCE #define _POSIX_C_SOURCE 200112L #include #include #include #include #include +#include #endif // __linux__ #include "pal/pal_core.h" @@ -81,6 +83,7 @@ static pthread_once_t s_TLSCreation = PTHREAD_ONCE_INIT; typedef struct { char tmp[PAL_LOG_MSG_SIZE]; char buffer[PAL_LOG_MSG_SIZE]; + char platformResultDesc[PAL_LOG_MSG_SIZE]; wchar_t wideBuffer[PAL_LOG_MSG_SIZE]; bool isLogging; } LogTLSData; @@ -233,6 +236,41 @@ static inline void writeToConsole(LogTLSData* data) #endif // _WIN32 } +void palSetLastPlatformError() +{ + LogTLSData* data = getLogTlsData(); + memset(data->platformResultDesc, 0, PAL_LOG_MSG_SIZE); + +#ifdef __linux__ + if (errno == 0) { + return; + } + +#if defined(__GLIBC__) + char* ret = strerror_r(errno, data->platformResultDesc, PAL_LOG_MSG_SIZE); + if (ret != data->platformResultDesc) { + snprintf(data->platformResultDesc, PAL_LOG_MSG_SIZE, "%s", ret); + } +#else + strerror_r(errno, data->platformResultDesc, PAL_LOG_MSG_SIZE); +#endif // __GLIBC__ +#else + DWORD error = getLastError(); + if (error == 0) { + return; + } + + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + error, + 0, + data->platformResultDesc, + PAL_LOG_MSG_SIZE, + nullptr); +#endif // __linux__ +} + // ================================================== // Public API // ================================================== @@ -264,8 +302,16 @@ const char* PAL_CALL palFormatResult(PalResult result) case PAL_RESULT_OUT_OF_MEMORY: return "Out of memory"; - case PAL_RESULT_PLATFORM_FAILURE: - return "Platform error"; + case PAL_RESULT_PLATFORM_FAILURE: { + LogTLSData* data = getLogTlsData(); + if (!data) { + return "Platform error"; + } else if (data && data->platformResultDesc[0] == 0) { + return "Platform error"; + } else { + return data->platformResultDesc; + } + } case PAL_RESULT_INVALID_ALLOCATOR: return "Invalif allocator"; diff --git a/src/system/pal_system_linux.c b/src/system/pal_system_linux.c index c108f71..ce76354 100644 --- a/src/system/pal_system_linux.c +++ b/src/system/pal_system_linux.c @@ -25,6 +25,8 @@ freely, subject to the following restrictions: // Includes // ================================================== +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 200112L #include "pal/pal_system.h" #include @@ -65,6 +67,8 @@ static Uint32 parseCache(const char* path) return cacheSize; } +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -90,6 +94,7 @@ PalResult PAL_CALL palGetPlatformInfo(PalPlatformInfo* info) FILE* file = fopen("/etc/os-release", "r"); if (!file) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -118,6 +123,7 @@ PalResult PAL_CALL palGetPlatformInfo(PalPlatformInfo* info) struct statvfs stats; if (statvfs("/", &stats) != 0) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -141,6 +147,7 @@ PalResult PAL_CALL palGetCPUInfo( FILE* file = fopen("/proc/cpuinfo", "r"); if (!file) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -220,6 +227,7 @@ PalResult PAL_CALL palGetCPUInfo( // get architecture struct utsname arch; if (uname(&arch) != 0) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/src/system/pal_system_win32.c b/src/system/pal_system_win32.c index 2e2a66e..4f0ef33 100644 --- a/src/system/pal_system_win32.c +++ b/src/system/pal_system_win32.c @@ -125,6 +125,8 @@ static inline bool isVersionWin32( return osVersion->build >= build; } +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -140,6 +142,7 @@ PalResult PAL_CALL palGetPlatformInfo(PalPlatformInfo* info) // get windows build, version and combine them if (!getVersionWin32(&info->version)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -277,6 +280,7 @@ PalResult PAL_CALL palGetCPUInfo( if (!ret) { palFree(allocator, buffer); + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/src/thread/pal_thread_linux.c b/src/thread/pal_thread_linux.c index 5909a07..b889f3c 100644 --- a/src/thread/pal_thread_linux.c +++ b/src/thread/pal_thread_linux.c @@ -57,6 +57,8 @@ struct PalCondVar { // Internal API // ================================================== +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -82,6 +84,7 @@ PalResult PAL_CALL palCreateThread( pthread_t thread; if (info->stackSize == 0) { if (pthread_create(&thread, nullptr, info->entry, info->arg) != 0) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -91,6 +94,7 @@ PalResult PAL_CALL palCreateThread( pthread_attr_setstacksize(&attr, info->stackSize); if (pthread_create(&thread, nullptr, info->entry, info->arg) != 0) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } pthread_attr_destroy(&attr); @@ -123,6 +127,7 @@ PalResult PAL_CALL palJoinThread( if (ret == 0) { return PAL_RESULT_SUCCESS; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -437,6 +442,7 @@ PalResult PAL_CALL palWaitCondVar( } else if (ret == ETIMEDOUT) { return PAL_RESULT_TIMEOUT; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/thread/pal_thread_win32.c b/src/thread/pal_thread_win32.c index 20dc15b..1cfc03c 100644 --- a/src/thread/pal_thread_win32.c +++ b/src/thread/pal_thread_win32.c @@ -82,6 +82,8 @@ static DWORD WINAPI threadEntryToWin32(LPVOID arg) return (DWORD)(uintptr_t)ret; } +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -135,6 +137,7 @@ PalResult PAL_CALL palCreateThread( return PAL_RESULT_ACCESS_DENIED; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -331,6 +334,7 @@ PalResult PAL_CALL palSetThreadPriority( return PAL_RESULT_ACCESS_DENIED; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -355,6 +359,7 @@ PalResult PAL_CALL palSetThreadAffinity( return PAL_RESULT_INVALID_ARGUMENT; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -401,6 +406,7 @@ PalResult PAL_CALL palSetThreadName( return PAL_RESULT_ACCESS_DENIED; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -537,6 +543,7 @@ PalResult PAL_CALL palWaitCondVar( if (error == ERROR_TIMEOUT) { return PAL_RESULT_TIMEOUT; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -564,6 +571,7 @@ PalResult PAL_CALL palWaitCondVarTimeout( if (error == ERROR_TIMEOUT) { return PAL_RESULT_TIMEOUT; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index e646818..fa6e836 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -3004,6 +3004,8 @@ static void createScancodeTable() s_Keyboard.scancodes[0x07E] = PAL_SCANCODE_RSUPER; } +void palSetLastPlatformError(); + // ================================================== // X11 API // ================================================== @@ -3016,6 +3018,7 @@ static PalResult glxBackend(const int index) // user choose GLX FBConfig backend if (!s_X11.glxHandle) { // Rare + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } // clang-format off @@ -3048,17 +3051,20 @@ static PalResult eglXBackend(int index) { // user choose EGL FBConfig backend if (!s_Egl.handle) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } EGLDisplay display = EGL_NO_DISPLAY; display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_X11.display); if (display == EGL_NO_DISPLAY) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } EGLint numConfigs = 0; if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3467,12 +3473,14 @@ static PalResult xInitVideo() // load X11 library s_X11.handle = dlopen("libX11.so", RTLD_LAZY); if (!s_X11.handle) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } // libXCursor is needed s_X11.libCursor = dlopen("libXcursor.so", RTLD_LAZY); if (!s_X11.libCursor) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3792,8 +3800,8 @@ static PalResult xInitVideo() s_Video.platformInstance = nullptr; } - s_X11.display = s_X11.openDisplay(nullptr); if (!s_X11.display) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3854,6 +3862,7 @@ static PalResult xInitVideo() s_X11.setLocaleModifiers(""); s_X11.im = s_X11.openIM(s_X11.display, nullptr, nullptr, nullptr); if (s_X11.im == None) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4410,6 +4419,7 @@ static PalResult xGetPrimaryMonitor(PalMonitor** outMonitor) return PAL_RESULT_SUCCESS; } + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4704,6 +4714,7 @@ static PalResult xSetMonitorMode( s_X11.freeScreenResources(resources); if (ret != Success) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4798,6 +4809,7 @@ static PalResult xSetMonitorOrientation( s_X11.freeScreenResources(resources); if (ret != Success) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4839,6 +4851,7 @@ static PalResult xCreateWindow( // clang-format on if (!colormap) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4986,6 +4999,7 @@ static PalResult xCreateWindow( &attrs); if (window == None) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -5195,6 +5209,7 @@ static PalResult xCreateWindow( nullptr); if (!data->ic) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -5814,6 +5829,7 @@ PalResult xCreateCursor( Cursor cursor = s_X11.cursorImageLoadCursor(s_X11.display, image); if (!cursor) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6135,6 +6151,7 @@ PalResult eglWlBackend(const int index) { // user choose EGL FBConfig backend if (!s_Egl.handle) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6142,11 +6159,13 @@ PalResult eglWlBackend(const int index) display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_Wl.display); if (display == EGL_NO_DISPLAY) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } EGLint numConfigs = 0; if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6499,6 +6518,7 @@ PalResult wlInitVideo() !s_Wl.xkbCommon || !s_Wl.libCursor || !s_Wl.libWaylandEgl) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6664,6 +6684,7 @@ PalResult wlInitVideo() } if (!s_Wl.display) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6676,18 +6697,21 @@ PalResult wlInitVideo() s_Wl.displayRoundtrip(s_Wl.display); if (!s_Wl.compositor || !s_Wl.xdgBase || !s_Wl.shm) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } // create an input context s_Wl.inputContext = s_Wl.xkbContextNew(XKB_CONTEXT_NO_FLAGS); if (!s_Wl.inputContext) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } // get the current theme s_Wl.cursorTheme = s_Wl.cursorThemeLoad(nullptr, 32, s_Wl.shm); if (!s_Wl.cursorTheme) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6943,17 +6967,20 @@ PalResult wlCreateWindow( // create surface surface = wlCompositorCreateSurface(s_Wl.compositor); if (!surface) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } wlSurfaceAddListener(surface, &surfaceListener, data); xdgSurface = xdgWmBaseGetXdgSurface(s_Wl.xdgBase, surface); if (!xdgSurface) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } xdgToplevel = xdgSurfaceGetToplevel(xdgSurface); if (!xdgSurface) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7025,6 +7052,7 @@ PalResult wlCreateWindow( if (s_Wl.eglFBConfig) { data->eglWindow = s_Wl.eglWindowCreate(surface, data->w, data->h); if (!data->eglWindow) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7033,6 +7061,7 @@ PalResult wlCreateWindow( struct wl_buffer* buffer = nullptr; buffer = createShmBuffer(data->w, data->h, nullptr, false); if (!buffer) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7320,6 +7349,7 @@ PalResult wlCreateCursor( cursor->surface = wlCompositorCreateSurface(s_Wl.compositor); if (!cursor->surface) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7327,6 +7357,7 @@ PalResult wlCreateCursor( createShmBuffer(info->width, info->height, info->pixels, true); if (!cursor->buffer) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7374,6 +7405,7 @@ PalResult wlCreateCursorFrom( struct wl_cursor* wlCursor = nullptr; wlCursor = s_Wl.cursorThemeGetCursor(s_Wl.cursorTheme, cursorType); if (!wlCursor) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7385,6 +7417,7 @@ PalResult wlCreateCursorFrom( cursor->surface = wlCompositorCreateSurface(s_Wl.compositor); if (!cursor->surface) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index d7bfebe..6c3d07b 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -775,6 +775,7 @@ static inline PalResult setMonitorMode( return PAL_RESULT_INVALID_MONITOR; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1050,6 +1051,8 @@ static WindowData* getFreeWindowData() return nullptr; } +void palSetLastPlatformError(); + // ================================================== // Public API // ================================================== @@ -1113,6 +1116,7 @@ PalResult PAL_CALL palInitVideo( nullptr); if (!s_Video.hiddenWindow) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1126,6 +1130,7 @@ PalResult PAL_CALL palInitVideo( rid.usUsage = 0x02; rid.usUsagePage = 0x01; if (!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1414,6 +1419,7 @@ PalResult PAL_CALL palGetPrimaryMonitor(PalMonitor** outMonitor) HMONITOR monitor = nullptr; monitor = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY); if (!monitor) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1441,6 +1447,7 @@ PalResult PAL_CALL palGetMonitorInfo( return PAL_RESULT_INVALID_MONITOR; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1521,6 +1528,7 @@ PalResult PAL_CALL palEnumerateMonitorModes( return PAL_RESULT_INVALID_MONITOR; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1589,6 +1597,7 @@ PalResult PAL_CALL palGetCurrentMonitorMode( return PAL_RESULT_INVALID_MONITOR; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1651,6 +1660,7 @@ PalResult PAL_CALL palSetMonitorOrientation( return PAL_RESULT_INVALID_MONITOR; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1768,6 +1778,7 @@ PalResult PAL_CALL palCreateWindow( MONITOR_DEFAULTTOPRIMARY); if (!monitor) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1816,6 +1827,7 @@ PalResult PAL_CALL palCreateWindow( nullptr); if (!handle) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2027,6 +2039,7 @@ PalResult PAL_CALL palFlashWindow( return PAL_RESULT_INVALID_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2119,6 +2132,7 @@ PalResult PAL_CALL palGetWindowMonitor( return PAL_RESULT_INVALID_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2399,6 +2413,7 @@ PalResult PAL_CALL palSetWindowOpacity( } else { // FIXME: check for child windows + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2483,6 +2498,7 @@ PalResult PAL_CALL palSetWindowStyle( return PAL_RESULT_INVALID_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2538,6 +2554,7 @@ PalResult PAL_CALL palSetWindowPos( return PAL_RESULT_INVALID_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2575,6 +2592,7 @@ PalResult PAL_CALL palSetWindowSize( return PAL_RESULT_INVALID_ARGUMENT; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2600,6 +2618,7 @@ PalResult PAL_CALL palSetFocusWindow(PalWindow* window) return PAL_RESULT_ACCESS_DENIED; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2651,6 +2670,7 @@ PalResult PAL_CALL palCreateIcon( if (!bitmap) { ReleaseDC(nullptr, hdc); + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } ReleaseDC(nullptr, hdc); @@ -2687,6 +2707,7 @@ PalResult PAL_CALL palCreateIcon( HICON icon = CreateIconIndirect(&iconInfo); if (!icon) { s_Video.deleteObject(bitmap); + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2768,6 +2789,7 @@ PalResult PAL_CALL palCreateCursor( if (!bitmap) { ReleaseDC(nullptr, hdc); + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } ReleaseDC(nullptr, hdc); @@ -2806,6 +2828,7 @@ PalResult PAL_CALL palCreateCursor( HCURSOR cursor = CreateIconIndirect(&iconInfo); if (!cursor) { s_Video.deleteObject(bitmap); + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2860,6 +2883,7 @@ PalResult PAL_CALL palCreateCursorFrom( } if (!cursor) { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2988,6 +3012,7 @@ PalResult PAL_CALL palSetWindowCursor( return PAL_RESULT_INVALID_WINDOW; } else { + palSetLastPlatformError(); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index 735112f..d57a0cd 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -13,10 +13,10 @@ #define WINDOW_TITLE "Custom Decoration Test" #define WL_MARSHAL_FLAG_DESTROY 1 << 0 -#define TITLEBAR_HEIGHT 30 -#define BUTTON_SIZE 15 +#define TITLEBAR_HEIGHT 34 +#define BUTTON_SIZE 24 #define BUTTON_POSY 8 -#define BUTTON_OFFSET 20 +#define BUTTON_OFFSET 30 struct wl_display; struct wl_registry; @@ -795,12 +795,13 @@ static void createDecoration() // this example does not support unicode characters int textWidth = strlen(WINDOW_TITLE) * 8; // 8x8 font int x = (width - textWidth) / 2; + int y = (TITLEBAR_HEIGHT - 8) / 2; drawText( s_Decoration.pixels, width, x, - 12, + y, WINDOW_TITLE, 0x00FFFFFF); From 4135c9deeecb587231040b0b7b087959e81fff2d Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 15:22:21 +0000 Subject: [PATCH 39/51] win32 mouse button fix --- CHANGELOG.md | 5 ++++- src/pal_core.c | 2 +- src/video/pal_video_linux.c | 3 ++- src/video/pal_video_win32.c | 7 ++++--- tests/custom_decoration_test.c | 14 +++++++++++--- tests/input_window_test.c | 2 ++ tests/tests_main.c | 4 ++-- 7 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a1922..3cb5fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - No API or ABI changes - existing code remains compatible. - Safe upgrade from **v1.1.0** - just rebuild your project after updating. -## [1.3.0] - 2025-11- +## [1.3.0] - 2025-11-21 ### Features - **Video:** Added Wayland-based backend support. @@ -79,10 +79,13 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. - **Video:** Added **PAL_CONFIG_BACKEND_GLES** to `PalFBConfigBackend` enum. - **Video:** Added **palSetPreferredInstance()** to set the native instance or display PAL video should use rather than creating a new one. + - **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. + - **OpenGL:** Added **palGLSetInstance()** o set the native instance or display PAL opengl should use. This must be set before calling **palInitGL()**. - **OpenGL:** Added **palGLGetBackend()** to get the opengl backend. + - **Event:** Added **PAL_EVENT_WINDOW_DECORATION_MODE** to `PalEventType` enum. - **Event:** Added **PalDecorationMode** enum. diff --git a/src/pal_core.c b/src/pal_core.c index 42dcee0..49eab0b 100644 --- a/src/pal_core.c +++ b/src/pal_core.c @@ -255,7 +255,7 @@ void palSetLastPlatformError() strerror_r(errno, data->platformResultDesc, PAL_LOG_MSG_SIZE); #endif // __GLIBC__ #else - DWORD error = getLastError(); + DWORD error = GetLastError(); if (error == 0) { return; } diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index fa6e836..3fac760 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -72,6 +72,7 @@ freely, subject to the following restrictions: #define TO_PAL_HANDLE(type, val) ((type*)(UintPtr)(val)) #define FROM_PAL_HANDLE(type, handle) ((type)(UintPtr)(handle)) #define MAX_SPAN_MONITORS 4 +#define NULL_BUTTON_SERIAL 0xffffffffU #pragma region EGL Typedefs @@ -4203,7 +4204,7 @@ static void xUpdateVideo() if (mode != PAL_DISPATCH_NONE) { PalEvent event = {0}; event.type = type; - event.data = palPackUint32(button, 0); + event.data = palPackUint32(button, NULL_BUTTON_SERIAL); event.data2 = palPackPointer(window); palPushEvent(driver, &event); } diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index 6c3d07b..441a3d4 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -54,6 +54,7 @@ freely, subject to the following restrictions: #define MAX_MODE_COUNT 128 #define NULL_ORIENTATION 5 #define WINDOW_NAME_SIZE 256 +#define NULL_BUTTON_SERIAL 0xffffffffU typedef HRESULT(WINAPI* GetDpiForMonitorFn)( HMONITOR, @@ -170,6 +171,8 @@ static Keyboard s_Keyboard = {0}; // Internal API // ================================================== +void palSetLastPlatformError(); + LRESULT CALLBACK videoProc( HWND hwnd, UINT msg, @@ -553,7 +556,7 @@ LRESULT CALLBACK videoProc( if (mode != PAL_DISPATCH_NONE) { PalEvent event = {0}; event.type = type; - event.data = palPackUint32(button, 0); + event.data = palPackUint32(button, NULL_BUTTON_SERIAL); event.data2 = palPackPointer((PalWindow*)hwnd); palPushEvent(driver, &event); } @@ -1051,8 +1054,6 @@ static WindowData* getFreeWindowData() return nullptr; } -void palSetLastPlatformError(); - // ================================================== // Public API // ================================================== diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index d57a0cd..c82d5b9 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -1,9 +1,10 @@ + +#if defined(__linux__) #define _GNU_SOURCE #define _POSIX_C_SOURCE 200112L // for linux #include "pal/pal_video.h" #include "tests.h" -#if defined(__linux__) #include #include #include @@ -700,8 +701,6 @@ void drawText( } } -#endif // __linux__ - static PalWindowHandleInfoEx s_WinHandle; static void createDecoration() @@ -1020,4 +1019,13 @@ bool customDecorationTest() closeDisplayWayland(); return true; +} + +#endif // __linux__ + +#include "tests.h" + +bool customDecorationTest() +{ + return false; } \ No newline at end of file diff --git a/tests/input_window_test.c b/tests/input_window_test.c index 81e3104..0abbee3 100644 --- a/tests/input_window_test.c +++ b/tests/input_window_test.c @@ -2,6 +2,8 @@ #include "pal/pal_video.h" #include "tests.h" +#define DISPATCH_MODE_POLL 0 + static const char* s_KeyNames[PAL_KEYCODE_MAX] = { [PAL_KEYCODE_UNKNOWN] = "Unknown", diff --git a/tests/tests_main.c b/tests/tests_main.c index f99c358..b9c01df 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -32,13 +32,13 @@ int main(int argc, char** argv) // registerTest("Window Test", windowTest); // registerTest("Icon Test", iconTest); // registerTest("Cursor Test", cursorTest); - // registerTest("Input Window Test", inputWindowTest); + registerTest("Input Window Test", inputWindowTest); // registerTest("System Cursor Test", systemCursorTest); // registerTest("Attach Window Test", attachWindowTest); // registerTest("Character Event Test", charEventTest); // registerTest("Native Integration Test", nativeIntegrationTest); // registerTest("Native Instance Test", nativeInstanceTest); - registerTest("Custom Decoration Test", customDecorationTest); + // registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid From 8bf6f23a1f1f04cf730865daee305779d7c2e32a Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 15:31:19 +0000 Subject: [PATCH 40/51] win32 test done --- include/pal/pal_event.h | 4 +- src/pal_core.c | 4 +- src/video/pal_video_linux.c | 42 ++-- tests/custom_decoration_test.c | 365 ++++++++++++++++----------------- tests/tests_main.c | 52 ++--- tests/window_test.c | 5 +- 6 files changed, 226 insertions(+), 246 deletions(-) diff --git a/include/pal/pal_event.h b/include/pal/pal_event.h index c02d42c..f85a7b0 100644 --- a/include/pal/pal_event.h +++ b/include/pal/pal_event.h @@ -104,8 +104,8 @@ typedef bool(PAL_CALL* PalPollFn)( * @enum PalDecorationMode * @brief Decoration types. This is not a bitmask enum. * - * All decoration types follow the format `PAL_DECORATION_MODE_**` for consistency and - * API use. + * All decoration types follow the format `PAL_DECORATION_MODE_**` for + * consistency and API use. * * @since 1.3 * @ingroup pal_event diff --git a/src/pal_core.c b/src/pal_core.c index 49eab0b..71bc6b0 100644 --- a/src/pal_core.c +++ b/src/pal_core.c @@ -28,12 +28,12 @@ freely, subject to the following restrictions: #ifdef __linux__ #define _GNU_SOURCE #define _POSIX_C_SOURCE 200112L +#include #include #include #include #include #include -#include #endif // __linux__ #include "pal/pal_core.h" @@ -240,7 +240,7 @@ void palSetLastPlatformError() { LogTLSData* data = getLogTlsData(); memset(data->platformResultDesc, 0, PAL_LOG_MSG_SIZE); - + #ifdef __linux__ if (errno == 0) { return; diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 3fac760..b1f1981 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -30,11 +30,11 @@ freely, subject to the following restrictions: #include "pal/pal_video.h" #include +#include #include #include #include #include -#include // X11 headers #if PAL_HAS_X11 @@ -2128,10 +2128,9 @@ static inline void xdgSurfaceAckConfigure( (struct wl_proxy*)xdg_surface, 4, // XDG_SURFACE_ACK_CONFIGURE NULL, - s_Wl.proxyGetVersion( - (struct wl_proxy*)xdg_surface), - 0, - serial); + s_Wl.proxyGetVersion((struct wl_proxy*)xdg_surface), + 0, + serial); } static void wmBaseHandlePing( @@ -2164,7 +2163,8 @@ static void xdgSurfaceHandleConfigure( } else { // create a new buffer with the new size struct wl_buffer* buffer = nullptr; - buffer = createShmBuffer(winData->w, winData->h, nullptr, false); + buffer = + createShmBuffer(winData->w, winData->h, nullptr, false); if (!buffer) { return; } @@ -2235,7 +2235,8 @@ static void xdgToplevelHandleConfigure( WindowData* winData = (WindowData*)data; uint32_t* state; bool activated = false; - wl_array_for_each(state, states) { + wl_array_for_each(state, states) + { // we need only maximized if (*state == 1) { // XDG_TOPLEVEL_STATE_MAXIMIZED if (winData->state != PAL_WINDOW_STATE_MAXIMIZED) { @@ -2744,8 +2745,7 @@ void zxdgDecorationHandleConfigure( } static struct zxdg_toplevel_decoration_v1_listener decorationListener = { - .configure = zxdgDecorationHandleConfigure -}; + .configure = zxdgDecorationHandleConfigure}; #endif // PAL_HAS_WAYLAND #pragma endregion @@ -3101,7 +3101,7 @@ static PalResult eglXBackend(int index) if (!visualInfo) { return PAL_RESULT_INVALID_GL_FBCONFIG; } - + s_X11.visualInfo = visualInfo; palFree(s_Video.allocator, eglConfigs); return PAL_RESULT_SUCCESS; @@ -6733,10 +6733,10 @@ void wlShutdownVideo() if (s_Wl.compositor) { // if compositor was found, all this will be as well // since we check all at init - s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.compositor); - s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.xdgBase); - s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.shm); - s_Wl.proxyDestroy((struct wl_proxy *)s_Wl.seat); + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.compositor); + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.xdgBase); + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.shm); + s_Wl.proxyDestroy((struct wl_proxy*)s_Wl.seat); } if (!s_Video.platformInstance) { @@ -6762,7 +6762,7 @@ PalResult wlSetFBConfig( } else { return PAL_RESULT_INVALID_FBCONFIG_BACKEND; - } + } } void wlUpdateVideo() @@ -6899,7 +6899,7 @@ PalResult wlGetCurrentMonitorMode( return PAL_RESULT_INVALID_MONITOR; } - // this is the same as the current mode + // this is the same as the current mode mode->bpp = monitorData->mode.bpp; mode->width = monitorData->mode.width; mode->height = monitorData->mode.height; @@ -7009,8 +7009,8 @@ PalResult wlCreateWindow( zxdgGetToplevelDecoration(s_Wl.decorationManager, xdgToplevel); zxdgToplevelDecorationV1AddListener( - decoration, - &decorationListener, + decoration, + &decorationListener, surface); zxdgToplevelDecorationV1SetMode(decoration, 2); @@ -7612,8 +7612,8 @@ PalResult PAL_CALL palInitVideo( return ret; } s_Video.backend = &s_XBackend; -#else - return PAL_RESULT_PLATFORM_FAILURE; +#else + return PAL_RESULT_PLATFORM_FAILURE; #endif // PAL_HAS_X11 } else { @@ -7624,7 +7624,7 @@ PalResult PAL_CALL palInitVideo( } s_Video.backend = &s_wlBackend; #else - return PAL_RESULT_PLATFORM_FAILURE; + return PAL_RESULT_PLATFORM_FAILURE; #endif // PAL_HAS_WAYLAND } diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index c82d5b9..898e624 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #define WINDOW_TITLE "Custom Decoration Test" #define WL_MARSHAL_FLAG_DESTROY 1 << 0 @@ -47,12 +47,12 @@ typedef struct { Uint64 size; } WaylandDecoration; -static struct wl_display *s_Display = nullptr; -static struct wl_registry *s_Registry = nullptr; -static struct wl_compositor *s_Compositor = nullptr; +static struct wl_display* s_Display = nullptr; +static struct wl_registry* s_Registry = nullptr; +static struct wl_compositor* s_Compositor = nullptr; static struct wl_subcompositor* s_Subcompositor = nullptr; -static struct wl_shm *s_Shm = nullptr; -static struct wl_surface *s_Surface = nullptr; +static struct wl_shm* s_Shm = nullptr; +static struct wl_surface* s_Surface = nullptr; static struct wl_seat* s_Seat = nullptr; static WaylandDecoration s_Decoration = {0}; @@ -151,8 +151,8 @@ static inline int wlRegistryAddListener( data); } -static inline struct wl_registry* wlDisplayGetRegistry( - struct wl_display* wl_display) +static inline struct wl_registry* +wlDisplayGetRegistry(struct wl_display* wl_display) { struct wl_proxy* registry; registry = s_wl_proxy_marshal_flags( @@ -166,219 +166,204 @@ static inline struct wl_registry* wlDisplayGetRegistry( return (struct wl_registry*)registry; } -static inline struct wl_surface* wlCompositorCreateSurface( - struct wl_compositor *wl_compositor) +static inline struct wl_surface* +wlCompositorCreateSurface(struct wl_compositor* wl_compositor) { - struct wl_proxy *id; - id = s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_compositor, + struct wl_proxy* id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_compositor, 0, // WL_COMPOSITOR_CREATE_SURFACE, - surfaceInterface, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_compositor), - 0, - NULL); + surfaceInterface, + s_wl_proxy_get_version((struct wl_proxy*)wl_compositor), + 0, + NULL); - return (struct wl_surface*) id; + return (struct wl_surface*)id; } -static inline void wlSurfaceCommit(struct wl_surface *wl_surface) +static inline void wlSurfaceCommit(struct wl_surface* wl_surface) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_surface, + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_surface, 6, // WL_SURFACE_COMMIT - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_surface), - 0); + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_surface), + 0); } -static inline void wlSurfaceDestroy(struct wl_surface *wl_surface) +static inline void wlSurfaceDestroy(struct wl_surface* wl_surface) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_surface, + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_surface, 0, // WL_SURFACE_DESTROY - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_surface), - WL_MARSHAL_FLAG_DESTROY); + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_surface), + WL_MARSHAL_FLAG_DESTROY); } static inline struct wl_shm_pool* wlShmCreatePool( - struct wl_shm *wl_shm, - int32_t fd, + struct wl_shm* wl_shm, + int32_t fd, int32_t size) { - struct wl_proxy *id; - id = s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_shm, + struct wl_proxy* id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_shm, 0, // WL_SHM_CREATE_POOL - shmPoolInterface, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_shm), - 0, - NULL, - fd, - size); - - return (struct wl_shm_pool *) id; + shmPoolInterface, + s_wl_proxy_get_version((struct wl_proxy*)wl_shm), + 0, + NULL, + fd, + size); + + return (struct wl_shm_pool*)id; } -static inline void wlShmPoolDestroy(struct wl_shm_pool *wl_shm_pool) +static inline void wlShmPoolDestroy(struct wl_shm_pool* wl_shm_pool) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_shm_pool, - 1, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_shm_pool), - WL_MARSHAL_FLAG_DESTROY); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_shm_pool, + 1, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_shm_pool), + WL_MARSHAL_FLAG_DESTROY); } static inline struct wl_buffer* wlShmPoolCreateBuffer( - struct wl_shm_pool *wl_shm_pool, - int32_t offset, - int32_t width, - int32_t height, - int32_t stride, + struct wl_shm_pool* wl_shm_pool, + int32_t offset, + int32_t width, + int32_t height, + int32_t stride, uint32_t format) { - struct wl_proxy *id; - id = s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_shm_pool, - 0, - bufferInterface, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_shm_pool), - 0, - NULL, - offset, - width, - height, - stride, - format); - - return (struct wl_buffer *) id; + struct wl_proxy* id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_shm_pool, + 0, + bufferInterface, + s_wl_proxy_get_version((struct wl_proxy*)wl_shm_pool), + 0, + NULL, + offset, + width, + height, + stride, + format); + + return (struct wl_buffer*)id; } -static inline void wlBufferDestroy(struct wl_buffer *wl_buffer) +static inline void wlBufferDestroy(struct wl_buffer* wl_buffer) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_buffer, - 0, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_buffer), - WL_MARSHAL_FLAG_DESTROY); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_buffer, + 0, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_buffer), + WL_MARSHAL_FLAG_DESTROY); } static inline void wlSurfaceAttach( - struct wl_surface *wl_surface, - struct wl_buffer *buffer, - int32_t x, + struct wl_surface* wl_surface, + struct wl_buffer* buffer, + int32_t x, int32_t y) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_surface, - 1, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_surface), - 0, - buffer, - x, - y); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_surface, + 1, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_surface), + 0, + buffer, + x, + y); } static inline void wlSurfaceDamageBuffer( - struct wl_surface *wl_surface, - int32_t x, - int32_t y, - int32_t width, + struct wl_surface* wl_surface, + int32_t x, + int32_t y, + int32_t width, int32_t height) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_surface, - 9, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_surface), - 0, - x, - y, - width, - height); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_surface, + 9, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_surface), + 0, + x, + y, + width, + height); } -static inline void wlSubcompositorDestroy( - struct wl_subcompositor *wl_subcompositor) +static inline void +wlSubcompositorDestroy(struct wl_subcompositor* wl_subcompositor) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_subcompositor, - 0, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_subcompositor), - WL_MARSHAL_FLAG_DESTROY); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_subcompositor, + 0, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_subcompositor), + WL_MARSHAL_FLAG_DESTROY); } -static inline struct wl_subsurface * wlSubcompositorGetSubsurface( - struct wl_subcompositor *wl_subcompositor, - struct wl_surface *surface, - struct wl_surface *parent) +static inline struct wl_subsurface* wlSubcompositorGetSubsurface( + struct wl_subcompositor* wl_subcompositor, + struct wl_surface* surface, + struct wl_surface* parent) { - struct wl_proxy *id; - id = s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_subcompositor, - 1, - subsurfaceInterface, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_subcompositor), - 0, - NULL, - surface, - parent); - - return (struct wl_subsurface *) id; + struct wl_proxy* id; + id = s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_subcompositor, + 1, + subsurfaceInterface, + s_wl_proxy_get_version((struct wl_proxy*)wl_subcompositor), + 0, + NULL, + surface, + parent); + + return (struct wl_subsurface*)id; } -static inline void wlSubsurfaceDestroy( - struct wl_subsurface *wl_subsurface) +static inline void wlSubsurfaceDestroy(struct wl_subsurface* wl_subsurface) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_subsurface, - 0, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_subsurface), - WL_MARSHAL_FLAG_DESTROY); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_subsurface, + 0, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_subsurface), + WL_MARSHAL_FLAG_DESTROY); } static inline void wlSubsurfaceSetPosition( - struct wl_subsurface *wl_subsurface, - int32_t x, + struct wl_subsurface* wl_subsurface, + int32_t x, int32_t y) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_subsurface, - 1, NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_subsurface), - 0, - x, - y); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_subsurface, + 1, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_subsurface), + 0, + x, + y); } -static inline void wlSubsurfaceSetDesync( - struct wl_subsurface *wl_subsurface) +static inline void wlSubsurfaceSetDesync(struct wl_subsurface* wl_subsurface) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) wl_subsurface, - 5, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) wl_subsurface), - 0); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)wl_subsurface, + 5, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)wl_subsurface), + 0); } static void globalHandle( @@ -395,7 +380,8 @@ static void globalHandle( s_Compositor = wlRegistryBind(registry, name, compositorInterface, 4); } else if (strcmp(interface, "wl_subcompositor") == 0) { - s_Subcompositor = wlRegistryBind(registry, name, subCompositorInterface, 1); + s_Subcompositor = + wlRegistryBind(registry, name, subCompositorInterface, 1); } else if (strcmp(interface, "wl_shm") == 0) { s_Shm = wlRegistryBind(registry, name, shmInterface, 1); @@ -407,7 +393,6 @@ static void globalRemove( struct wl_registry* registry, uint32_t name) { - } static const struct wl_registry_listener s_RegistryListener = { @@ -416,37 +401,35 @@ static const struct wl_registry_listener s_RegistryListener = { // xdg-shell protocol static inline void xdgToplevelMove( - struct xdg_toplevel *xdg_toplevel, - struct wl_seat *seat, + struct xdg_toplevel* xdg_toplevel, + struct wl_seat* seat, uint32_t serial) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) xdg_toplevel, - 5, + s_wl_proxy_marshal_flags( + (struct wl_proxy*)xdg_toplevel, + 5, NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) xdg_toplevel), - 0, - seat, - serial); + s_wl_proxy_get_version((struct wl_proxy*)xdg_toplevel), + 0, + seat, + serial); } static inline void xdgToplevelResize( - struct xdg_toplevel *xdg_toplevel, - struct wl_seat *seat, - uint32_t serial, + struct xdg_toplevel* xdg_toplevel, + struct wl_seat* seat, + uint32_t serial, uint32_t edges) { - s_wl_proxy_marshal_flags( - (struct wl_proxy *) xdg_toplevel, - 6, - NULL, - s_wl_proxy_get_version( - (struct wl_proxy *) xdg_toplevel), - 0, - seat, - serial, - edges); + s_wl_proxy_marshal_flags( + (struct wl_proxy*)xdg_toplevel, + 6, + NULL, + s_wl_proxy_get_version((struct wl_proxy*)xdg_toplevel), + 0, + seat, + serial, + edges); } static void openDisplayWayland() diff --git a/tests/tests_main.c b/tests/tests_main.c index b9c01df..a551b67 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,49 +9,49 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - // registerTest("Logger Test", loggerTest); - // registerTest("Time Test", timeTest); - // registerTest("User Event Test", userEventTest); - // registerTest("Event Test", eventTest); + registerTest("Logger Test", loggerTest); + registerTest("Time Test", timeTest); + registerTest("User Event Test", userEventTest); + registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - // registerTest("System Test", systemTest); + registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - // registerTest("Thread Test", threadTest); - // registerTest("TLS Test", tlsTest); - // registerTest("Mutex Test", mutexTest); - // registerTest("Condvar Test", condvarTest); + registerTest("Thread Test", threadTest); + registerTest("TLS Test", tlsTest); + registerTest("Mutex Test", mutexTest); + registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO - // registerTest("Video Test", videoTest); - // registerTest("Monitor Test", monitorTest); - // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); - // registerTest("Icon Test", iconTest); - // registerTest("Cursor Test", cursorTest); + registerTest("Video Test", videoTest); + registerTest("Monitor Test", monitorTest); + registerTest("Monitor Mode Test", monitorModeTest); + registerTest("Window Test", windowTest); + registerTest("Icon Test", iconTest); + registerTest("Cursor Test", cursorTest); registerTest("Input Window Test", inputWindowTest); - // registerTest("System Cursor Test", systemCursorTest); - // registerTest("Attach Window Test", attachWindowTest); - // registerTest("Character Event Test", charEventTest); - // registerTest("Native Integration Test", nativeIntegrationTest); - // registerTest("Native Instance Test", nativeInstanceTest); - // registerTest("Custom Decoration Test", customDecorationTest); + registerTest("System Cursor Test", systemCursorTest); + registerTest("Attach Window Test", attachWindowTest); + registerTest("Character Event Test", charEventTest); + registerTest("Native Integration Test", nativeIntegrationTest); + registerTest("Native Instance Test", nativeInstanceTest); + registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - // registerTest("Opengl Test", openglTest); - // registerTest("Opengl FBConfig Test", openglFBConfigTest); - // registerTest("Opengl Context Test", openglContextTest); - // registerTest("Opengl Multi Context Test", openglMultiContextTest); + registerTest("Opengl Test", openglTest); + registerTest("Opengl FBConfig Test", openglFBConfigTest); + registerTest("Opengl Context Test", openglContextTest); + registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); diff --git a/tests/window_test.c b/tests/window_test.c index 0b6281a..7210dab 100644 --- a/tests/window_test.c +++ b/tests/window_test.c @@ -108,10 +108,7 @@ static inline void onMonitorDPI(const PalEvent* event) static inline void onMonitorList(const PalEvent* event) { PalWindow* window = palUnpackPointer(event->data2); - palLog( - nullptr, - "%s: Monitor List has been changed", - dispatchString); + palLog(nullptr, "%s: Monitor List has been changed", dispatchString); } static void PAL_CALL onEvent( From 3aa8fab213b3a1b75ef6736ceabd99476639e6bb Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 16:06:03 +0000 Subject: [PATCH 41/51] update platform error handling win32 --- src/opengl/pal_opengl_win32.c | 32 +++++++++++-------- src/pal_core.c | 17 ++++------ src/system/pal_system_win32.c | 8 +++-- src/thread/pal_thread_win32.c | 15 ++++----- src/video/pal_video_win32.c | 58 ++++++++++++++++++++--------------- tests/tests_main.c | 52 +++++++++++++++---------------- 6 files changed, 99 insertions(+), 83 deletions(-) diff --git a/src/opengl/pal_opengl_win32.c b/src/opengl/pal_opengl_win32.c index 88a23e8..0eb79f0 100644 --- a/src/opengl/pal_opengl_win32.c +++ b/src/opengl/pal_opengl_win32.c @@ -239,7 +239,7 @@ static inline bool checkExtension( } } -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // Public API @@ -289,14 +289,16 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) 0); if (!s_Wgl.window) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } s_Gdi.handle = LoadLibraryA("gdi32.dll"); s_Wgl.opengl = LoadLibraryA("opengl32.dll"); if (!s_Gdi.handle || !s_Wgl.opengl) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -348,7 +350,8 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) !s_Gdi.describePixelFormat || !s_Gdi.swapBuffers || !s_Gdi.setPixelFormat) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -356,7 +359,8 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) !s_Wgl.wglCreateContext || !s_Wgl.wglDeleteContext || !s_Wgl.wglMakeCurrent) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -379,7 +383,8 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_Wgl.context = s_Wgl.wglCreateContext(s_Wgl.hdc); if (!s_Wgl.wglMakeCurrent(s_Wgl.hdc, s_Wgl.context)) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -572,7 +577,8 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( 1, &configAttrib, &nativeCount)) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -919,7 +925,7 @@ PalResult PAL_CALL palCreateGLContext( if (error == ERROR_INVALID_PROFILE_ARB) { return PAL_RESULT_INVALID_GL_PROFILE; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -928,7 +934,8 @@ PalResult PAL_CALL palCreateGLContext( // create context with legacy wgl functions context = s_Wgl.wglCreateContext(hdc); if (!context) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -937,7 +944,8 @@ PalResult PAL_CALL palCreateGLContext( if (!s_Wgl.wglShareLists(share, context)) { s_Wgl.wglDeleteContext(context); ReleaseDC((HWND)info->window->window, hdc); - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -981,7 +989,7 @@ PalResult PAL_CALL palMakeContextCurrent( return PAL_RESULT_INVALID_GL_CONTEXT; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1031,7 +1039,7 @@ PalResult PAL_CALL palSwapBuffers( return PAL_RESULT_INVALID_GL_FBCONFIG; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/pal_core.c b/src/pal_core.c index 71bc6b0..f9b5de8 100644 --- a/src/pal_core.c +++ b/src/pal_core.c @@ -236,34 +236,29 @@ static inline void writeToConsole(LogTLSData* data) #endif // _WIN32 } -void palSetLastPlatformError() +void palSetLastPlatformError(Uint32 e) { LogTLSData* data = getLogTlsData(); memset(data->platformResultDesc, 0, PAL_LOG_MSG_SIZE); -#ifdef __linux__ - if (errno == 0) { + if (e == 0) { return; } +#ifdef __linux__ #if defined(__GLIBC__) - char* ret = strerror_r(errno, data->platformResultDesc, PAL_LOG_MSG_SIZE); + char* ret = strerror_r(e, data->platformResultDesc, PAL_LOG_MSG_SIZE); if (ret != data->platformResultDesc) { snprintf(data->platformResultDesc, PAL_LOG_MSG_SIZE, "%s", ret); } #else - strerror_r(errno, data->platformResultDesc, PAL_LOG_MSG_SIZE); + strerror_r(e, data->platformResultDesc, PAL_LOG_MSG_SIZE); #endif // __GLIBC__ #else - DWORD error = GetLastError(); - if (error == 0) { - return; - } - FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, - error, + e, 0, data->platformResultDesc, PAL_LOG_MSG_SIZE, diff --git a/src/system/pal_system_win32.c b/src/system/pal_system_win32.c index 4f0ef33..bcce0a7 100644 --- a/src/system/pal_system_win32.c +++ b/src/system/pal_system_win32.c @@ -125,7 +125,7 @@ static inline bool isVersionWin32( return osVersion->build >= build; } -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // Public API @@ -142,7 +142,8 @@ PalResult PAL_CALL palGetPlatformInfo(PalPlatformInfo* info) // get windows build, version and combine them if (!getVersionWin32(&info->version)) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -280,7 +281,8 @@ PalResult PAL_CALL palGetCPUInfo( if (!ret) { palFree(allocator, buffer); - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/src/thread/pal_thread_win32.c b/src/thread/pal_thread_win32.c index 1cfc03c..4337c63 100644 --- a/src/thread/pal_thread_win32.c +++ b/src/thread/pal_thread_win32.c @@ -82,7 +82,7 @@ static DWORD WINAPI threadEntryToWin32(LPVOID arg) return (DWORD)(uintptr_t)ret; } -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // Public API @@ -137,7 +137,7 @@ PalResult PAL_CALL palCreateThread( return PAL_RESULT_ACCESS_DENIED; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -334,7 +334,7 @@ PalResult PAL_CALL palSetThreadPriority( return PAL_RESULT_ACCESS_DENIED; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -359,7 +359,7 @@ PalResult PAL_CALL palSetThreadAffinity( return PAL_RESULT_INVALID_ARGUMENT; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -406,7 +406,8 @@ PalResult PAL_CALL palSetThreadName( return PAL_RESULT_ACCESS_DENIED; } else { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -543,7 +544,7 @@ PalResult PAL_CALL palWaitCondVar( if (error == ERROR_TIMEOUT) { return PAL_RESULT_TIMEOUT; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -571,7 +572,7 @@ PalResult PAL_CALL palWaitCondVarTimeout( if (error == ERROR_TIMEOUT) { return PAL_RESULT_TIMEOUT; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/video/pal_video_win32.c b/src/video/pal_video_win32.c index 441a3d4..a95412f 100644 --- a/src/video/pal_video_win32.c +++ b/src/video/pal_video_win32.c @@ -171,7 +171,7 @@ static Keyboard s_Keyboard = {0}; // Internal API // ================================================== -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); LRESULT CALLBACK videoProc( HWND hwnd, @@ -778,7 +778,7 @@ static inline PalResult setMonitorMode( return PAL_RESULT_INVALID_MONITOR; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1117,7 +1117,8 @@ PalResult PAL_CALL palInitVideo( nullptr); if (!s_Video.hiddenWindow) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1131,7 +1132,8 @@ PalResult PAL_CALL palInitVideo( rid.usUsage = 0x02; rid.usUsagePage = 0x01; if (!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE))) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1420,7 +1422,8 @@ PalResult PAL_CALL palGetPrimaryMonitor(PalMonitor** outMonitor) HMONITOR monitor = nullptr; monitor = MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY); if (!monitor) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1448,7 +1451,7 @@ PalResult PAL_CALL palGetMonitorInfo( return PAL_RESULT_INVALID_MONITOR; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1529,7 +1532,7 @@ PalResult PAL_CALL palEnumerateMonitorModes( return PAL_RESULT_INVALID_MONITOR; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1598,7 +1601,7 @@ PalResult PAL_CALL palGetCurrentMonitorMode( return PAL_RESULT_INVALID_MONITOR; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1661,7 +1664,7 @@ PalResult PAL_CALL palSetMonitorOrientation( return PAL_RESULT_INVALID_MONITOR; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1779,7 +1782,8 @@ PalResult PAL_CALL palCreateWindow( MONITOR_DEFAULTTOPRIMARY); if (!monitor) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1828,7 +1832,8 @@ PalResult PAL_CALL palCreateWindow( nullptr); if (!handle) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2040,7 +2045,7 @@ PalResult PAL_CALL palFlashWindow( return PAL_RESULT_INVALID_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2133,7 +2138,7 @@ PalResult PAL_CALL palGetWindowMonitor( return PAL_RESULT_INVALID_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2414,7 +2419,7 @@ PalResult PAL_CALL palSetWindowOpacity( } else { // FIXME: check for child windows - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2499,7 +2504,7 @@ PalResult PAL_CALL palSetWindowStyle( return PAL_RESULT_INVALID_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2555,7 +2560,7 @@ PalResult PAL_CALL palSetWindowPos( return PAL_RESULT_INVALID_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2593,7 +2598,7 @@ PalResult PAL_CALL palSetWindowSize( return PAL_RESULT_INVALID_ARGUMENT; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2619,7 +2624,7 @@ PalResult PAL_CALL palSetFocusWindow(PalWindow* window) return PAL_RESULT_ACCESS_DENIED; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -2671,7 +2676,8 @@ PalResult PAL_CALL palCreateIcon( if (!bitmap) { ReleaseDC(nullptr, hdc); - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } ReleaseDC(nullptr, hdc); @@ -2708,7 +2714,8 @@ PalResult PAL_CALL palCreateIcon( HICON icon = CreateIconIndirect(&iconInfo); if (!icon) { s_Video.deleteObject(bitmap); - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2790,7 +2797,8 @@ PalResult PAL_CALL palCreateCursor( if (!bitmap) { ReleaseDC(nullptr, hdc); - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } ReleaseDC(nullptr, hdc); @@ -2829,7 +2837,8 @@ PalResult PAL_CALL palCreateCursor( HCURSOR cursor = CreateIconIndirect(&iconInfo); if (!cursor) { s_Video.deleteObject(bitmap); - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -2884,7 +2893,8 @@ PalResult PAL_CALL palCreateCursorFrom( } if (!cursor) { - palSetLastPlatformError(); + DWORD error = GetLastError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3013,7 +3023,7 @@ PalResult PAL_CALL palSetWindowCursor( return PAL_RESULT_INVALID_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(error); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/tests/tests_main.c b/tests/tests_main.c index a551b67..5a869d2 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,49 +9,49 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - registerTest("Logger Test", loggerTest); - registerTest("Time Test", timeTest); - registerTest("User Event Test", userEventTest); - registerTest("Event Test", eventTest); + // registerTest("Logger Test", loggerTest); + // registerTest("Time Test", timeTest); + // registerTest("User Event Test", userEventTest); + // registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - registerTest("System Test", systemTest); + // registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - registerTest("Thread Test", threadTest); - registerTest("TLS Test", tlsTest); - registerTest("Mutex Test", mutexTest); - registerTest("Condvar Test", condvarTest); + // registerTest("Thread Test", threadTest); + // registerTest("TLS Test", tlsTest); + // registerTest("Mutex Test", mutexTest); + // registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO registerTest("Video Test", videoTest); - registerTest("Monitor Test", monitorTest); - registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); - registerTest("Icon Test", iconTest); - registerTest("Cursor Test", cursorTest); - registerTest("Input Window Test", inputWindowTest); - registerTest("System Cursor Test", systemCursorTest); - registerTest("Attach Window Test", attachWindowTest); - registerTest("Character Event Test", charEventTest); - registerTest("Native Integration Test", nativeIntegrationTest); - registerTest("Native Instance Test", nativeInstanceTest); - registerTest("Custom Decoration Test", customDecorationTest); + // registerTest("Monitor Test", monitorTest); + // registerTest("Monitor Mode Test", monitorModeTest); + // registerTest("Window Test", windowTest); + // registerTest("Icon Test", iconTest); + // registerTest("Cursor Test", cursorTest); + // registerTest("Input Window Test", inputWindowTest); + // registerTest("System Cursor Test", systemCursorTest); + // registerTest("Attach Window Test", attachWindowTest); + // registerTest("Character Event Test", charEventTest); + // registerTest("Native Integration Test", nativeIntegrationTest); + // registerTest("Native Instance Test", nativeInstanceTest); + // registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - registerTest("Opengl Test", openglTest); - registerTest("Opengl FBConfig Test", openglFBConfigTest); - registerTest("Opengl Context Test", openglContextTest); - registerTest("Opengl Multi Context Test", openglMultiContextTest); + // registerTest("Opengl Test", openglTest); + // registerTest("Opengl FBConfig Test", openglFBConfigTest); + // registerTest("Opengl Context Test", openglContextTest); + // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); From 63e8a73a54d83c90ede2d825f4a7c0105a359a6b Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 16:28:13 +0000 Subject: [PATCH 42/51] update platform error handling linux --- src/opengl/pal_opengl_linux.c | 40 ++++++++++----------- src/system/pal_system_linux.c | 10 +++--- src/thread/pal_thread_linux.c | 10 +++--- src/video/pal_video_linux.c | 66 +++++++++++++++++------------------ tests/tests_main.c | 52 +++++++++++++-------------- 5 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index 78dc1e7..d345e92 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -342,7 +342,7 @@ static void freeContextData(PalGLContext* context) } } -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // Public API @@ -359,7 +359,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } if (!s_GL.platformDisplay) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -375,7 +375,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.handle = dlopen("libEGL.so", RTLD_LAZY); if (!s_GL.handle) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -453,7 +453,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) !s_GL.eglQueryString || !s_GL.eglGetConfigs || !s_GL.eglCreateWindowSurface) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -471,19 +471,19 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } if (!s_GL.eglBindAPI(s_GL.apiType)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } EGLDisplay display = s_GL.eglGetDisplay(s_GL.platformDisplay); EGLDisplay * tmpDisplay = EGL_NO_DISPLAY; if (display == EGL_NO_DISPLAY) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } if (!s_GL.eglInitialize(display, nullptr, nullptr)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -504,12 +504,12 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) // create a default display and use that tmpDisplay = s_GL.eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } if (!s_GL.eglInitialize(tmpDisplay, nullptr, nullptr)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } else { @@ -519,14 +519,14 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) s_GL.eglChooseConfig(tmpDisplay, attribs, &config, 1, &numConfigs); if (!config) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } s_GL.eglGetConfigAttrib(tmpDisplay, config, EGL_RENDERABLE_TYPE, &type); if (!(type & s_GL.apiTypeBit)) { // we must support the required API - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -535,7 +535,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) if (s_GL.apiType == EGL_OPENGL_API) { if (!(type & EGL_OPENGL_ES2_BIT)) { // FIXME: create a dummy window if EGL_OPENGL_ES2_BIT - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -552,7 +552,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) pBufferAttribs); if (surface == EGL_NO_SURFACE) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -583,7 +583,7 @@ PalResult PAL_CALL palInitGL(const PalAllocator* allocator) } if (context == EGL_NO_CONTEXT) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -743,7 +743,7 @@ PalResult PAL_CALL palEnumerateGLFBConfigs( // get the number of configs and filter the ones for opengl desktop EGLint numConfigs = 0; if (!s_GL.eglGetConfigs(s_GL.display, nullptr, 0, &numConfigs)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1029,7 +1029,7 @@ PalResult PAL_CALL palCreateGLContext( // we need to get the EGL config from the user supplied index EGLint numConfigs = 0; if (!s_GL.eglGetConfigs(s_GL.display, nullptr, 0, &numConfigs)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -1145,7 +1145,7 @@ PalResult PAL_CALL palCreateGLContext( return PAL_RESULT_INVALID_GL_VERSION; } else { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1173,7 +1173,7 @@ PalResult PAL_CALL palCreateGLContext( return PAL_RESULT_INVALID_GL_FBCONFIG; } else { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1254,7 +1254,7 @@ PalResult PAL_CALL palMakeContextCurrent( return PAL_RESULT_INVALID_GL_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -1306,7 +1306,7 @@ PalResult PAL_CALL palSwapBuffers( return PAL_RESULT_INVALID_GL_WINDOW; } else { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/system/pal_system_linux.c b/src/system/pal_system_linux.c index ce76354..851419e 100644 --- a/src/system/pal_system_linux.c +++ b/src/system/pal_system_linux.c @@ -67,7 +67,7 @@ static Uint32 parseCache(const char* path) return cacheSize; } -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // Public API @@ -94,7 +94,7 @@ PalResult PAL_CALL palGetPlatformInfo(PalPlatformInfo* info) FILE* file = fopen("/etc/os-release", "r"); if (!file) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -123,7 +123,7 @@ PalResult PAL_CALL palGetPlatformInfo(PalPlatformInfo* info) struct statvfs stats; if (statvfs("/", &stats) != 0) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -147,7 +147,7 @@ PalResult PAL_CALL palGetCPUInfo( FILE* file = fopen("/proc/cpuinfo", "r"); if (!file) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -227,7 +227,7 @@ PalResult PAL_CALL palGetCPUInfo( // get architecture struct utsname arch; if (uname(&arch) != 0) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/src/thread/pal_thread_linux.c b/src/thread/pal_thread_linux.c index b889f3c..2c5b786 100644 --- a/src/thread/pal_thread_linux.c +++ b/src/thread/pal_thread_linux.c @@ -57,7 +57,7 @@ struct PalCondVar { // Internal API // ================================================== -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // Public API @@ -84,7 +84,7 @@ PalResult PAL_CALL palCreateThread( pthread_t thread; if (info->stackSize == 0) { if (pthread_create(&thread, nullptr, info->entry, info->arg) != 0) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -94,7 +94,7 @@ PalResult PAL_CALL palCreateThread( pthread_attr_setstacksize(&attr, info->stackSize); if (pthread_create(&thread, nullptr, info->entry, info->arg) != 0) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } pthread_attr_destroy(&attr); @@ -127,7 +127,7 @@ PalResult PAL_CALL palJoinThread( if (ret == 0) { return PAL_RESULT_SUCCESS; } else { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } @@ -442,7 +442,7 @@ PalResult PAL_CALL palWaitCondVar( } else if (ret == ETIMEDOUT) { return PAL_RESULT_TIMEOUT; } else { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } } diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index b1f1981..14e21ba 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -3005,7 +3005,7 @@ static void createScancodeTable() s_Keyboard.scancodes[0x07E] = PAL_SCANCODE_RSUPER; } -void palSetLastPlatformError(); +void palSetLastPlatformError(Uint32 e); // ================================================== // X11 API @@ -3019,7 +3019,7 @@ static PalResult glxBackend(const int index) // user choose GLX FBConfig backend if (!s_X11.glxHandle) { // Rare - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } // clang-format off @@ -3052,20 +3052,20 @@ static PalResult eglXBackend(int index) { // user choose EGL FBConfig backend if (!s_Egl.handle) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } EGLDisplay display = EGL_NO_DISPLAY; display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_X11.display); if (display == EGL_NO_DISPLAY) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } EGLint numConfigs = 0; if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3474,14 +3474,14 @@ static PalResult xInitVideo() // load X11 library s_X11.handle = dlopen("libX11.so", RTLD_LAZY); if (!s_X11.handle) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } // libXCursor is needed s_X11.libCursor = dlopen("libXcursor.so", RTLD_LAZY); if (!s_X11.libCursor) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3802,7 +3802,7 @@ static PalResult xInitVideo() } if (!s_X11.display) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -3863,7 +3863,7 @@ static PalResult xInitVideo() s_X11.setLocaleModifiers(""); s_X11.im = s_X11.openIM(s_X11.display, nullptr, nullptr, nullptr); if (s_X11.im == None) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4420,7 +4420,7 @@ static PalResult xGetPrimaryMonitor(PalMonitor** outMonitor) return PAL_RESULT_SUCCESS; } - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4715,7 +4715,7 @@ static PalResult xSetMonitorMode( s_X11.freeScreenResources(resources); if (ret != Success) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4810,7 +4810,7 @@ static PalResult xSetMonitorOrientation( s_X11.freeScreenResources(resources); if (ret != Success) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -4852,7 +4852,7 @@ static PalResult xCreateWindow( // clang-format on if (!colormap) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -5000,7 +5000,7 @@ static PalResult xCreateWindow( &attrs); if (window == None) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -5210,7 +5210,7 @@ static PalResult xCreateWindow( nullptr); if (!data->ic) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -5830,7 +5830,7 @@ PalResult xCreateCursor( Cursor cursor = s_X11.cursorImageLoadCursor(s_X11.display, image); if (!cursor) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6152,7 +6152,7 @@ PalResult eglWlBackend(const int index) { // user choose EGL FBConfig backend if (!s_Egl.handle) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6160,13 +6160,13 @@ PalResult eglWlBackend(const int index) display = s_Egl.eglGetDisplay((EGLNativeDisplayType)s_Wl.display); if (display == EGL_NO_DISPLAY) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } EGLint numConfigs = 0; if (!s_Egl.eglGetConfigs(display, nullptr, 0, &numConfigs)) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6519,7 +6519,7 @@ PalResult wlInitVideo() !s_Wl.xkbCommon || !s_Wl.libCursor || !s_Wl.libWaylandEgl) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6685,7 +6685,7 @@ PalResult wlInitVideo() } if (!s_Wl.display) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6698,21 +6698,21 @@ PalResult wlInitVideo() s_Wl.displayRoundtrip(s_Wl.display); if (!s_Wl.compositor || !s_Wl.xdgBase || !s_Wl.shm) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } // create an input context s_Wl.inputContext = s_Wl.xkbContextNew(XKB_CONTEXT_NO_FLAGS); if (!s_Wl.inputContext) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } // get the current theme s_Wl.cursorTheme = s_Wl.cursorThemeLoad(nullptr, 32, s_Wl.shm); if (!s_Wl.cursorTheme) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -6968,20 +6968,20 @@ PalResult wlCreateWindow( // create surface surface = wlCompositorCreateSurface(s_Wl.compositor); if (!surface) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } wlSurfaceAddListener(surface, &surfaceListener, data); xdgSurface = xdgWmBaseGetXdgSurface(s_Wl.xdgBase, surface); if (!xdgSurface) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } xdgToplevel = xdgSurfaceGetToplevel(xdgSurface); if (!xdgSurface) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7053,7 +7053,7 @@ PalResult wlCreateWindow( if (s_Wl.eglFBConfig) { data->eglWindow = s_Wl.eglWindowCreate(surface, data->w, data->h); if (!data->eglWindow) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7062,7 +7062,7 @@ PalResult wlCreateWindow( struct wl_buffer* buffer = nullptr; buffer = createShmBuffer(data->w, data->h, nullptr, false); if (!buffer) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7350,7 +7350,7 @@ PalResult wlCreateCursor( cursor->surface = wlCompositorCreateSurface(s_Wl.compositor); if (!cursor->surface) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7358,7 +7358,7 @@ PalResult wlCreateCursor( createShmBuffer(info->width, info->height, info->pixels, true); if (!cursor->buffer) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7406,7 +7406,7 @@ PalResult wlCreateCursorFrom( struct wl_cursor* wlCursor = nullptr; wlCursor = s_Wl.cursorThemeGetCursor(s_Wl.cursorTheme, cursorType); if (!wlCursor) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } @@ -7418,7 +7418,7 @@ PalResult wlCreateCursorFrom( cursor->surface = wlCompositorCreateSurface(s_Wl.compositor); if (!cursor->surface) { - palSetLastPlatformError(); + palSetLastPlatformError(errno); return PAL_RESULT_PLATFORM_FAILURE; } diff --git a/tests/tests_main.c b/tests/tests_main.c index 5a869d2..a551b67 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,49 +9,49 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - // registerTest("Logger Test", loggerTest); - // registerTest("Time Test", timeTest); - // registerTest("User Event Test", userEventTest); - // registerTest("Event Test", eventTest); + registerTest("Logger Test", loggerTest); + registerTest("Time Test", timeTest); + registerTest("User Event Test", userEventTest); + registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - // registerTest("System Test", systemTest); + registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - // registerTest("Thread Test", threadTest); - // registerTest("TLS Test", tlsTest); - // registerTest("Mutex Test", mutexTest); - // registerTest("Condvar Test", condvarTest); + registerTest("Thread Test", threadTest); + registerTest("TLS Test", tlsTest); + registerTest("Mutex Test", mutexTest); + registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO registerTest("Video Test", videoTest); - // registerTest("Monitor Test", monitorTest); - // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); - // registerTest("Icon Test", iconTest); - // registerTest("Cursor Test", cursorTest); - // registerTest("Input Window Test", inputWindowTest); - // registerTest("System Cursor Test", systemCursorTest); - // registerTest("Attach Window Test", attachWindowTest); - // registerTest("Character Event Test", charEventTest); - // registerTest("Native Integration Test", nativeIntegrationTest); - // registerTest("Native Instance Test", nativeInstanceTest); - // registerTest("Custom Decoration Test", customDecorationTest); + registerTest("Monitor Test", monitorTest); + registerTest("Monitor Mode Test", monitorModeTest); + registerTest("Window Test", windowTest); + registerTest("Icon Test", iconTest); + registerTest("Cursor Test", cursorTest); + registerTest("Input Window Test", inputWindowTest); + registerTest("System Cursor Test", systemCursorTest); + registerTest("Attach Window Test", attachWindowTest); + registerTest("Character Event Test", charEventTest); + registerTest("Native Integration Test", nativeIntegrationTest); + registerTest("Native Instance Test", nativeInstanceTest); + registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - // registerTest("Opengl Test", openglTest); - // registerTest("Opengl FBConfig Test", openglFBConfigTest); - // registerTest("Opengl Context Test", openglContextTest); - // registerTest("Opengl Multi Context Test", openglMultiContextTest); + registerTest("Opengl Test", openglTest); + registerTest("Opengl FBConfig Test", openglFBConfigTest); + registerTest("Opengl Context Test", openglContextTest); + registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); From 573968a9e91eeaa7f87e4397d3666ef362dfe25f Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 20:51:00 +0000 Subject: [PATCH 43/51] surface enter and leave wayland --- src/opengl/pal_opengl_linux.c | 1 + src/system/pal_system_linux.c | 1 + src/thread/pal_thread_linux.c | 1 + src/video/pal_video_linux.c | 22 ++++++++++++-- tests/custom_decoration_test.c | 8 +++--- tests/tests_main.c | 52 +++++++++++++++++----------------- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index d345e92..3654e3a 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -33,6 +33,7 @@ freely, subject to the following restrictions: #include #include #include +#include // ================================================== // Typedefs, enums and structs diff --git a/src/system/pal_system_linux.c b/src/system/pal_system_linux.c index 851419e..1685c38 100644 --- a/src/system/pal_system_linux.c +++ b/src/system/pal_system_linux.c @@ -36,6 +36,7 @@ freely, subject to the following restrictions: #include #include #include +#include // ================================================== // Typedefs, enums and structs diff --git a/src/thread/pal_thread_linux.c b/src/thread/pal_thread_linux.c index 2c5b786..f94dc2f 100644 --- a/src/thread/pal_thread_linux.c +++ b/src/thread/pal_thread_linux.c @@ -33,6 +33,7 @@ freely, subject to the following restrictions: #include #include #include +#include #include "pal/pal_thread.h" diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 14e21ba..8f058ef 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -35,6 +35,7 @@ freely, subject to the following restrictions: #include #include #include +#include // X11 headers #if PAL_HAS_X11 @@ -1428,8 +1429,25 @@ static void surfaceHandleEnter( // this assumes a surface can only span 4 monitors // at the sametime but this might be wrong // FIXME: check if we need more - if (data->monitorCount < MAX_SPAN_MONITORS) { - SpanMonitor* span = &data->monitors[data->monitorCount]; + + // wayland will trigger this event if the window gains focus + // so we check if the output is the same + SpanMonitor* span = nullptr; + if (data->monitorCount > 0) { + for (int i = 0; i < data->monitorCount; i++) { + if (data->monitors[i].monitor == (void*)output) { + // the monitor already exist in our array + // so we just update it + span = &data->monitors[i]; + span->dpi = monitorData->dpi; + break; + } + } + } + + if (span == nullptr) { + // new entry + span = &data->monitors[data->monitorCount]; span->monitor = output; span->dpi = monitorData->dpi; data->monitorCount++; diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index 898e624..bd89268 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -1004,11 +1004,11 @@ bool customDecorationTest() return true; } -#endif // __linux__ - +#else #include "tests.h" - bool customDecorationTest() { return false; -} \ No newline at end of file +} + +#endif // __linux__ \ No newline at end of file diff --git a/tests/tests_main.c b/tests/tests_main.c index a551b67..f99c358 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,49 +9,49 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - registerTest("Logger Test", loggerTest); - registerTest("Time Test", timeTest); - registerTest("User Event Test", userEventTest); - registerTest("Event Test", eventTest); + // registerTest("Logger Test", loggerTest); + // registerTest("Time Test", timeTest); + // registerTest("User Event Test", userEventTest); + // registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - registerTest("System Test", systemTest); + // registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - registerTest("Thread Test", threadTest); - registerTest("TLS Test", tlsTest); - registerTest("Mutex Test", mutexTest); - registerTest("Condvar Test", condvarTest); + // registerTest("Thread Test", threadTest); + // registerTest("TLS Test", tlsTest); + // registerTest("Mutex Test", mutexTest); + // registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO - registerTest("Video Test", videoTest); - registerTest("Monitor Test", monitorTest); - registerTest("Monitor Mode Test", monitorModeTest); - registerTest("Window Test", windowTest); - registerTest("Icon Test", iconTest); - registerTest("Cursor Test", cursorTest); - registerTest("Input Window Test", inputWindowTest); - registerTest("System Cursor Test", systemCursorTest); - registerTest("Attach Window Test", attachWindowTest); - registerTest("Character Event Test", charEventTest); - registerTest("Native Integration Test", nativeIntegrationTest); - registerTest("Native Instance Test", nativeInstanceTest); + // registerTest("Video Test", videoTest); + // registerTest("Monitor Test", monitorTest); + // registerTest("Monitor Mode Test", monitorModeTest); + // registerTest("Window Test", windowTest); + // registerTest("Icon Test", iconTest); + // registerTest("Cursor Test", cursorTest); + // registerTest("Input Window Test", inputWindowTest); + // registerTest("System Cursor Test", systemCursorTest); + // registerTest("Attach Window Test", attachWindowTest); + // registerTest("Character Event Test", charEventTest); + // registerTest("Native Integration Test", nativeIntegrationTest); + // registerTest("Native Instance Test", nativeInstanceTest); registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - registerTest("Opengl Test", openglTest); - registerTest("Opengl FBConfig Test", openglFBConfigTest); - registerTest("Opengl Context Test", openglContextTest); - registerTest("Opengl Multi Context Test", openglMultiContextTest); + // registerTest("Opengl Test", openglTest); + // registerTest("Opengl FBConfig Test", openglFBConfigTest); + // registerTest("Opengl Context Test", openglContextTest); + // registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); From 1bbfcfe42848a6b6a5321aa695581b23d1bcf394 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 21:18:16 +0000 Subject: [PATCH 44/51] code of conduct --- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cb5fc1..b0c4983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,7 +108,7 @@ This is a runtime behavior change. The tests are updated in the repo. - **Pal mouse button event** - The `event.data` now packs both the button and wayland seat serial (If on wayland). Existing code that reads as a single value -without unpacking will see different values. +without unpacking will see different values. This is a runtime behavior change. The **input_window_test.c** has been updated in the repo. - **palJoinThread()** - ABI remains unchanged but now takes the address diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d61a878 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,58 @@ + +# Code of Conduct + +PAL aims to provide a respectful and productive environment for all +contributors. To ensure this, everyone participating in the project — on GitHub +issues, discussions, pull requests, or community channels — is expected to +follow this Code of Conduct. + +--- + +## Expected Behavior + +- Be respectful and professional. +- Assume good intentions. +- Engage in technical discussions with clarity and patience. +- Provide constructive feedback. +- Help others understand platform-specific details when possible. + +--- + +## Unacceptable Behavior + +- Personal attacks, insults, or harassment. +- Aggressive or hostile debate. +- Dismissing someone’s question without explanation. +- Repeated disruptive behavior. +- Sharing private or confidential information. + +--- + +## Reporting Issues + +If you experience or observe unacceptable behavior: + +- Send a private message or email to the project maintainer. () +- Provide a brief summary of what happened. + +Reports will be handled respectfully and confidentially. + +--- + +## Enforcement + +The project maintainer may: + +- Issue warnings +- Temporarily restrict participation +- Permanently ban repeat or severe offenders + +These actions are taken solely to maintain a safe and productive environment. + +--- + +## Conclusion + +PAL is a technical system-level project built on respect, clarity, and +collaboration. +Thank you for helping maintain a healthy community! From ff645d41d80f452eedc019213d1b9c37e137f857 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 21:21:58 +0000 Subject: [PATCH 45/51] security.md --- README.md | 3 --- SECURITY.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 SECURITY.md diff --git a/README.md b/README.md index 3e9b380..213972b 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,6 @@ PAL is a lightweight, low-level, cross-platform abstraction layer in **C**, designed to be **explicit** and as close to the **OS** as possible — similar in philosophy to Vulkan. It gives you precise control without hidden behavior, making it ideal for developers who want performance and predictability. -Originally named as **Platform Abstraction Layer**, -PAL has evolved into **Prime Abstraction Layer** — the **first** and most **direct** layer between your engine or software and the operating system. - PAL is transparent. All queries — window size, position, monitor info, and more — reflect the current platform state. Using PAL is like working directly with the OS: it applies no hidden logic, makes no assumptions, and leaves behavior fully in your control. This approach gives you total control: you handle events, manage resources, and cache state explicitly. PAL provides the building blocks; how you use them — whether for simple applications or advanced frameworks — is entirely up to you. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..70aeefc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +PAL takes security seriously. If you discover a security vulnerability, please report it responsibly so it can be fixed safely. + +--- + +## Reporting a Vulnerability + +- Contact the project maintainer at: +- Include a clear description of the issue. +- Provide steps to reproduce the problem if possible. +- Do **not** publicly disclose the vulnerability until a fix is released. + +--- + +## Supported Versions + +- All actively maintained versions of PAL are supported for security fixes. + +--- + +## Response + +The maintainer will: + +- Acknowledge the report promptly. +- Work to verify and fix the issue. +- Notify the reporter once a fix is available. From 751aa65a911041fa643b54b842d553174e879ed2 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 21:29:14 +0000 Subject: [PATCH 46/51] contributing.md --- CONTRIBUTING.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ SECURITY.md | 1 + 2 files changed, 98 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4c03dfd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,97 @@ + +# Contributing to PAL + +Thank you for your interest in contributing to **PAL**, a low-level, explicit, +cross-platform abstraction layer. Contributions of all kinds are welcome — +bug fixes, platform correctness updates, documentation, or new backend +improvements. + +Please take a moment to read these guidelines before opening an issue or pull request. + +--- + +## 🐛 Reporting Issues + +When filing an issue, include: + +- Platform + version (Windows / Linux / etc.) +- Compiler + version +- Backend involved (`win32`, `x11`, `wayland`, etc.) +- A minimal reproducible example if possible +- Exact error codes or logs produced by PAL + +Clear reports make it much easier to fix the problem. + +--- + +## 💡 Suggesting Enhancements + +Before starting work on any major change: + +1. Open an **Issue** or **Discussion**. +2. Describe: + - The problem you’re solving + - Why it belongs in PAL + - How it aligns with PAL goals (explicit, low-level, predictable) + +This helps keep the project focused and consistent. + +--- + +## 🧩 Pull Requests + +### PR Checklist + +Before submitting a PR: + +- Ensure it compiles on **all** supported platforms. +- Run any relevant backend smoke tests. +- Document any public API changes. +- Update `CHANGELOG.md` if applicable. +- Keep the PR focused — avoid mixing unrelated edits. + +### PR Style + +- Clear commit messages (e.g. `wayland: fix surface_enter behavior`). +- Prefer small, incremental commits over one large “everything” commit. +- Keep platform code isolated (`*_win32.c`, `*_x11.c`, etc.). + +--- + +## 🧪 Coding Style + +PAL uses simple, consistent C: + +- **C99** for C source. +- `lower_camel_case` for functions + variables. (e.g. `palCreateWindow`). +- `PascalCase` for public types (e.g. `PalResult`, `PalWindow`). +- Minimal runtime validation. +- No hidden work (ex: no automatic device enumeration). +- Avoid dynamic allocations unless documented. + +Platform Rules: + +- Follow each platform’s documentation strictly. +- Do not depend on undefined or unofficial behavior. +- Keep backend-specific files separated. + +--- + +## 🧱 Architecture Philosophy + +PAL aims to remain: + +- **Explicit** — no hidden allocations or state. +- **Low-overhead** — almost equivalent to calling the OS directly. +- **Cross-platform** — similar API behavior across OSes. +- **Predictable** — the library never “does work behind your back.” + +Please keep this in mind when contributing. + +--- + +## ✔️ Thank You + +Every contribution — tests, docs, bug reports, or backend fixes — helps PAL +remain a robust low-level foundation for engine developers. +Thank you for being part of the project! \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index 70aeefc..ba2da48 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,3 +1,4 @@ + # Security Policy PAL takes security seriously. If you discover a security vulnerability, please report it responsibly so it can be fixed safely. From 2f35fb232b147a19721b9cf6ad0c4b82bd3259b0 Mon Sep 17 00:00:00 2001 From: nichcode Date: Tue, 18 Nov 2025 21:52:36 +0000 Subject: [PATCH 47/51] add wayland backend --- CHANGELOG.md | 2 +- LICENSE.txt | 2 +- README.md | 2 +- tests/tests_main.c | 52 +++++++++++++++++++++++----------------------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0c4983..c421656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# Changelog +# CHANGELOG ## [1.0.0] - 2025-09-27 - Initial stable release of PAL. diff --git a/LICENSE.txt b/LICENSE.txt index ee6114c..ba74348 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,7 @@ zlib License -Copyright (C) 2025 Nicholas Agbo +Copyright (C) 2025 Nicholas Agbo This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/README.md b/README.md index 213972b..f20e337 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,9 @@ For more detailed examples, see the [tests folder](./tests) tests folder, which ## Supported Platforms - Windows (Vista+) - Linux (X11) +- Linux (Wayland) ## Planned Platforms -- Linux (Wayland) - macOS (Cocoa) - Android - iOS diff --git a/tests/tests_main.c b/tests/tests_main.c index f99c358..a551b67 100644 --- a/tests/tests_main.c +++ b/tests/tests_main.c @@ -9,49 +9,49 @@ int main(int argc, char** argv) palLog(nullptr, "%s: %s", "PAL Version", palGetVersionString()); // core - // registerTest("Logger Test", loggerTest); - // registerTest("Time Test", timeTest); - // registerTest("User Event Test", userEventTest); - // registerTest("Event Test", eventTest); + registerTest("Logger Test", loggerTest); + registerTest("Time Test", timeTest); + registerTest("User Event Test", userEventTest); + registerTest("Event Test", eventTest); #if PAL_HAS_SYSTEM - // registerTest("System Test", systemTest); + registerTest("System Test", systemTest); #endif // PAL_HAS_SYSTEM #if PAL_HAS_THREAD - // registerTest("Thread Test", threadTest); - // registerTest("TLS Test", tlsTest); - // registerTest("Mutex Test", mutexTest); - // registerTest("Condvar Test", condvarTest); + registerTest("Thread Test", threadTest); + registerTest("TLS Test", tlsTest); + registerTest("Mutex Test", mutexTest); + registerTest("Condvar Test", condvarTest); #endif // PAL_HAS_THREAD #if PAL_HAS_VIDEO - // registerTest("Video Test", videoTest); - // registerTest("Monitor Test", monitorTest); - // registerTest("Monitor Mode Test", monitorModeTest); - // registerTest("Window Test", windowTest); - // registerTest("Icon Test", iconTest); - // registerTest("Cursor Test", cursorTest); - // registerTest("Input Window Test", inputWindowTest); - // registerTest("System Cursor Test", systemCursorTest); - // registerTest("Attach Window Test", attachWindowTest); - // registerTest("Character Event Test", charEventTest); - // registerTest("Native Integration Test", nativeIntegrationTest); - // registerTest("Native Instance Test", nativeInstanceTest); + registerTest("Video Test", videoTest); + registerTest("Monitor Test", monitorTest); + registerTest("Monitor Mode Test", monitorModeTest); + registerTest("Window Test", windowTest); + registerTest("Icon Test", iconTest); + registerTest("Cursor Test", cursorTest); + registerTest("Input Window Test", inputWindowTest); + registerTest("System Cursor Test", systemCursorTest); + registerTest("Attach Window Test", attachWindowTest); + registerTest("Character Event Test", charEventTest); + registerTest("Native Integration Test", nativeIntegrationTest); + registerTest("Native Instance Test", nativeInstanceTest); registerTest("Custom Decoration Test", customDecorationTest); #endif // PAL_HAS_VIDEO // This test can run without video system so long as your have a valid // window #if PAL_HAS_OPENGL && PAL_HAS_VIDEO - // registerTest("Opengl Test", openglTest); - // registerTest("Opengl FBConfig Test", openglFBConfigTest); - // registerTest("Opengl Context Test", openglContextTest); - // registerTest("Opengl Multi Context Test", openglMultiContextTest); + registerTest("Opengl Test", openglTest); + registerTest("Opengl FBConfig Test", openglFBConfigTest); + registerTest("Opengl Context Test", openglContextTest); + registerTest("Opengl Multi Context Test", openglMultiContextTest); #endif // PAL_HAS_OPENGL #if PAL_HAS_OPENGL && PAL_HAS_VIDEO && PAL_HAS_THREAD - // registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); + registerTest("Multi Thread OpenGL Test", multiThreadOpenGlTest); #endif // runTests(); From 02f47869711c4c0f07f2914cd26b2e4e4c9597d6 Mon Sep 17 00:00:00 2001 From: nichcode Date: Wed, 19 Nov 2025 10:02:11 +0000 Subject: [PATCH 48/51] update native api test --- tests/native_integration_test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 0fef15a..377070d 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -405,6 +405,9 @@ bool nativeIntegrationTest() getWindowTitle(&windowInfo); palLog(nullptr, "Window title: %s", s_TitleBuffer); + // using native API like wl_proxy_set_user_data, XContext and + // SetWindowLongPtr(GWLP_USERDATA) can be used freely + bool running = true; while (running) { // update the video system to push video events From 1ebb974feedd641dca6adf398a5ea44aa95aba5a Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 20 Nov 2025 14:42:52 +0000 Subject: [PATCH 49/51] update github templates --- .github/CONTRIBUTING.md | 43 ++++++++++ .github/ISSUE_TEMPLATE/bug_report.md | 28 +++++++ .github/ISSUE_TEMPLATE/config.yaml | 1 + .github/ISSUE_TEMPLATE/feature_request.md | 17 ++++ CODE_OF_CONDUCT.md | 13 +-- CONTRIBUTING.md | 97 ----------------------- README.md | 4 + SECURITY.md | 29 ------- 8 files changed, 96 insertions(+), 136 deletions(-) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 CONTRIBUTING.md delete mode 100644 SECURITY.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..5093607 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing to PAL + +Thank you for your interest in contributing to **PAL**. + +Everyone is welcome to contribute to PAL by submitting bug reports, +bug fixes, improving documentation, adding tests examples, telling others about PAL, +giving PAL a star, adding a new backend, etc. + +### Reporting Bugs + +Make sure the bug is not an API usage issue and you have the lastest version of PAL. +If the above is not the case, report the bug on our [GitHub Issue Tracker](https://github.com/nichcode/PAL/issues) using the bug report template. Please make sure you write a good bug report. + +## Requesting Features + +Requesting a feature which does not align with the goals of PAL (explicit, low-level) will likey +not be merge into PAL. This approach keeps project focused and consistent. + +Request features on our [GitHub Issue Tracker](https://github.com/nichcode/PAL/issues) +using the feature report template. Please explain into detail why your feature will work and if +possibly usages of it in use. + +## Coding Convention + +- **C99** for C source. + +- Naming convention: + - `lowerCamelCase` for functions and function parameters. (e.g. `palCreateWindow`, `windowHandle`). + - `PascalCase` for public types (e.g. `PalResult`). + - static internal variables uses 's_' prefix. (eg. `s_InternalData`). + - `snakeCase` for public and internal macros. (e.g. `PAL_DEFAULT_ALIGNMENT`). + + +## Contributing code + +**PAL is released under the Zlib License and every +code contributed to PAL must agree to Zlib licensing terms.** + +Pull request checklist: + +- Ensure it compiles on **all** supported platforms. +- Ensure the PR is focused and changes are relevant to the feature or bug fix. +- Ensure your code is formatted with clang-format using the `clang-format` file in the repo. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..8b3a49e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Create a report to help us improve +labels: bug +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Operating system (please complete the following information)**: +- OS: [e.g. windows] +- Version: [e.g. 11] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 0000000..ec4bb38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..2356405 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project +labels: enhancement +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d61a878..64edc86 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,8 +1,8 @@ # Code of Conduct -PAL aims to provide a respectful and productive environment for all -contributors. To ensure this, everyone participating in the project — on GitHub +PAL aims to provide a respectful, productive and positive environment for all +contributors and maintainers. To ensure this, everyone participating in the project — on GitHub issues, discussions, pull requests, or community channels — is expected to follow this Code of Conduct. @@ -22,7 +22,6 @@ follow this Code of Conduct. - Personal attacks, insults, or harassment. - Aggressive or hostile debate. -- Dismissing someone’s question without explanation. - Repeated disruptive behavior. - Sharing private or confidential information. @@ -45,14 +44,8 @@ The project maintainer may: - Issue warnings - Temporarily restrict participation -- Permanently ban repeat or severe offenders +- Permanently ban offenders These actions are taken solely to maintain a safe and productive environment. --- - -## Conclusion - -PAL is a technical system-level project built on respect, clarity, and -collaboration. -Thank you for helping maintain a healthy community! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 4c03dfd..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,97 +0,0 @@ - -# Contributing to PAL - -Thank you for your interest in contributing to **PAL**, a low-level, explicit, -cross-platform abstraction layer. Contributions of all kinds are welcome — -bug fixes, platform correctness updates, documentation, or new backend -improvements. - -Please take a moment to read these guidelines before opening an issue or pull request. - ---- - -## 🐛 Reporting Issues - -When filing an issue, include: - -- Platform + version (Windows / Linux / etc.) -- Compiler + version -- Backend involved (`win32`, `x11`, `wayland`, etc.) -- A minimal reproducible example if possible -- Exact error codes or logs produced by PAL - -Clear reports make it much easier to fix the problem. - ---- - -## 💡 Suggesting Enhancements - -Before starting work on any major change: - -1. Open an **Issue** or **Discussion**. -2. Describe: - - The problem you’re solving - - Why it belongs in PAL - - How it aligns with PAL goals (explicit, low-level, predictable) - -This helps keep the project focused and consistent. - ---- - -## 🧩 Pull Requests - -### PR Checklist - -Before submitting a PR: - -- Ensure it compiles on **all** supported platforms. -- Run any relevant backend smoke tests. -- Document any public API changes. -- Update `CHANGELOG.md` if applicable. -- Keep the PR focused — avoid mixing unrelated edits. - -### PR Style - -- Clear commit messages (e.g. `wayland: fix surface_enter behavior`). -- Prefer small, incremental commits over one large “everything” commit. -- Keep platform code isolated (`*_win32.c`, `*_x11.c`, etc.). - ---- - -## 🧪 Coding Style - -PAL uses simple, consistent C: - -- **C99** for C source. -- `lower_camel_case` for functions + variables. (e.g. `palCreateWindow`). -- `PascalCase` for public types (e.g. `PalResult`, `PalWindow`). -- Minimal runtime validation. -- No hidden work (ex: no automatic device enumeration). -- Avoid dynamic allocations unless documented. - -Platform Rules: - -- Follow each platform’s documentation strictly. -- Do not depend on undefined or unofficial behavior. -- Keep backend-specific files separated. - ---- - -## 🧱 Architecture Philosophy - -PAL aims to remain: - -- **Explicit** — no hidden allocations or state. -- **Low-overhead** — almost equivalent to calling the OS directly. -- **Cross-platform** — similar API behavior across OSes. -- **Predictable** — the library never “does work behind your back.” - -Please keep this in mind when contributing. - ---- - -## ✔️ Thank You - -Every contribution — tests, docs, bug reports, or backend fixes — helps PAL -remain a robust low-level foundation for engine developers. -Thank you for being part of the project! \ No newline at end of file diff --git a/README.md b/README.md index f20e337..6438f82 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,10 @@ PAL is a lightweight, low-level, cross-platform abstraction layer in **C**, desi PAL is transparent. All queries — window size, position, monitor info, and more — reflect the current platform state. Using PAL is like working directly with the OS: it applies no hidden logic, makes no assumptions, and leaves behavior fully in your control. +The goal is very simple, write low-level cross-platform code without having per platform files +all over the place. Example: `renderer_vulkan`, `renderer_d3d12`, `window_win32`, etc. +PAL makes it possible to safely mix native API with its API in a very straight forward way. This is one of the main reasons why PAL exists. + This approach gives you total control: you handle events, manage resources, and cache state explicitly. PAL provides the building blocks; how you use them — whether for simple applications or advanced frameworks — is entirely up to you. Example – Get Window Size diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index ba2da48..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,29 +0,0 @@ - -# Security Policy - -PAL takes security seriously. If you discover a security vulnerability, please report it responsibly so it can be fixed safely. - ---- - -## Reporting a Vulnerability - -- Contact the project maintainer at: -- Include a clear description of the issue. -- Provide steps to reproduce the problem if possible. -- Do **not** publicly disclose the vulnerability until a fix is released. - ---- - -## Supported Versions - -- All actively maintained versions of PAL are supported for security fixes. - ---- - -## Response - -The maintainer will: - -- Acknowledge the report promptly. -- Work to verify and fix the issue. -- Notify the reporter once a fix is available. From 95ca278627b413f6ec599beb4a26020354c7951f Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 20 Nov 2025 16:34:26 +0000 Subject: [PATCH 50/51] format code --- CHANGELOG.md | 4 +-- CODE_OF_CONDUCT.md | 51 --------------------------------- src/opengl/pal_opengl_linux.c | 2 +- src/system/pal_system_linux.c | 2 +- src/thread/pal_thread_linux.c | 1 - src/video/pal_video_linux.c | 2 +- tests/custom_decoration_test.c | 2 +- tests/native_integration_test.c | 2 +- 8 files changed, 7 insertions(+), 59 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c421656..a8c99ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,7 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr ## [1.3.0] - 2025-11-21 ### Features -- **Video:** Added Wayland-based backend support. +- **Video:** Added Wayland backend support - **Video:** Added **palGetVideoFeaturesEx()** to check old and extended supported features. - **Video:** Added **palGetWindowHandleInfoEx()** to get extended window handles. - **Video:** Added **palGetRawMouseWheelDelta()** to get raw mouse wheel delta. @@ -83,7 +83,7 @@ reflecting its role as the primary explicit foundation for OS and graphics abstr - **Core:** Added **palPackFloat()** to combine two floats into a single Int64 integer. - **Core:** Added **palUnpackFloat()** to retreive two floats from a single Int64 integer. -- **OpenGL:** Added **palGLSetInstance()** o set the native instance or display PAL opengl should use. This must be set before calling **palInitGL()**. +- **OpenGL:** Added **palGLSetInstance()** to set the native instance or display PAL opengl should use. This must be set before calling **palInitGL()**. - **OpenGL:** Added **palGLGetBackend()** to get the opengl backend. - **Event:** Added **PAL_EVENT_WINDOW_DECORATION_MODE** to `PalEventType` enum. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 64edc86..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,51 +0,0 @@ - -# Code of Conduct - -PAL aims to provide a respectful, productive and positive environment for all -contributors and maintainers. To ensure this, everyone participating in the project — on GitHub -issues, discussions, pull requests, or community channels — is expected to -follow this Code of Conduct. - ---- - -## Expected Behavior - -- Be respectful and professional. -- Assume good intentions. -- Engage in technical discussions with clarity and patience. -- Provide constructive feedback. -- Help others understand platform-specific details when possible. - ---- - -## Unacceptable Behavior - -- Personal attacks, insults, or harassment. -- Aggressive or hostile debate. -- Repeated disruptive behavior. -- Sharing private or confidential information. - ---- - -## Reporting Issues - -If you experience or observe unacceptable behavior: - -- Send a private message or email to the project maintainer. () -- Provide a brief summary of what happened. - -Reports will be handled respectfully and confidentially. - ---- - -## Enforcement - -The project maintainer may: - -- Issue warnings -- Temporarily restrict participation -- Permanently ban offenders - -These actions are taken solely to maintain a safe and productive environment. - ---- diff --git a/src/opengl/pal_opengl_linux.c b/src/opengl/pal_opengl_linux.c index 3654e3a..c8b3b6a 100644 --- a/src/opengl/pal_opengl_linux.c +++ b/src/opengl/pal_opengl_linux.c @@ -30,10 +30,10 @@ freely, subject to the following restrictions: #include "pal/pal_opengl.h" #include +#include #include #include #include -#include // ================================================== // Typedefs, enums and structs diff --git a/src/system/pal_system_linux.c b/src/system/pal_system_linux.c index 1685c38..f775c41 100644 --- a/src/system/pal_system_linux.c +++ b/src/system/pal_system_linux.c @@ -29,6 +29,7 @@ freely, subject to the following restrictions: #define _POSIX_C_SOURCE 200112L #include "pal/pal_system.h" +#include #include #include #include @@ -36,7 +37,6 @@ freely, subject to the following restrictions: #include #include #include -#include // ================================================== // Typedefs, enums and structs diff --git a/src/thread/pal_thread_linux.c b/src/thread/pal_thread_linux.c index f94dc2f..2c5b786 100644 --- a/src/thread/pal_thread_linux.c +++ b/src/thread/pal_thread_linux.c @@ -33,7 +33,6 @@ freely, subject to the following restrictions: #include #include #include -#include #include "pal/pal_thread.h" diff --git a/src/video/pal_video_linux.c b/src/video/pal_video_linux.c index 8f058ef..ffe25d3 100644 --- a/src/video/pal_video_linux.c +++ b/src/video/pal_video_linux.c @@ -30,12 +30,12 @@ freely, subject to the following restrictions: #include "pal/pal_video.h" #include +#include #include #include #include #include #include -#include // X11 headers #if PAL_HAS_X11 diff --git a/tests/custom_decoration_test.c b/tests/custom_decoration_test.c index bd89268..d0965cb 100644 --- a/tests/custom_decoration_test.c +++ b/tests/custom_decoration_test.c @@ -580,7 +580,7 @@ void fillRect( { for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { - pixels[(y + j)* stride + (x + i)] = color; + pixels[(y + j) * stride + (x + i)] = color; } } } diff --git a/tests/native_integration_test.c b/tests/native_integration_test.c index 377070d..80eabd7 100644 --- a/tests/native_integration_test.c +++ b/tests/native_integration_test.c @@ -405,7 +405,7 @@ bool nativeIntegrationTest() getWindowTitle(&windowInfo); palLog(nullptr, "Window title: %s", s_TitleBuffer); - // using native API like wl_proxy_set_user_data, XContext and + // using native API like wl_proxy_set_user_data, XContext and // SetWindowLongPtr(GWLP_USERDATA) can be used freely bool running = true; From 55d60335454507edb360e95b332f8125fac66d03 Mon Sep 17 00:00:00 2001 From: nichcode Date: Thu, 20 Nov 2025 16:39:23 +0000 Subject: [PATCH 51/51] format github issues template --- .github/CONTRIBUTING.md | 2 -- .github/ISSUE_TEMPLATE/bug_report.md | 10 +++++----- .github/ISSUE_TEMPLATE/feature_request.md | 8 ++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 5093607..6bb0457 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,7 +1,5 @@ # Contributing to PAL -Thank you for your interest in contributing to **PAL**. - Everyone is welcome to contribute to PAL by submitting bug reports, bug fixes, improving documentation, adding tests examples, telling others about PAL, giving PAL a star, adding a new backend, etc. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8b3a49e..1291251 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,25 +4,25 @@ about: Create a report to help us improve labels: bug --- -**Describe the bug** +**Describe the bug** A clear and concise description of what the bug is. -**To Reproduce** +**To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -**Expected behavior** +**Expected behavior** A clear and concise description of what you expected to happen. -**Screenshots** +**Screenshots** If applicable, add screenshots to help explain your problem. **Operating system (please complete the following information)**: - OS: [e.g. windows] - Version: [e.g. 11] -**Additional context** +**Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 2356405..31b18c9 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,14 +4,14 @@ about: Suggest an idea for this project labels: enhancement --- -**Is your feature request related to a problem? Please describe.** +**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -**Describe the solution you'd like** +**Describe the solution you'd like** A clear and concise description of what you want to happen. -**Describe alternatives you've considered** +**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. -**Additional context** +**Additional context** Add any other context or screenshots about the feature request here.