From e7922d7b4f7c25349fd63a0170403425461cd67b Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:46:16 +0100 Subject: [PATCH 01/31] Add GLFW theming functions Cocoa implementations are broken. Other platforms have no stubs, and therefore can't be compiled for. --- include/GLFW/glfw3.h | 31 ++++++++++++++ src/cocoa_init.m | 14 ++++++ src/cocoa_platform.h | 4 ++ src/cocoa_window.m | 100 +++++++++++++++++++++++++++++++++++++++++++ src/init.c | 14 ++++++ src/internal.h | 3 ++ src/window.c | 18 ++++++++ 7 files changed, 184 insertions(+) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index d4b40dd4..8129d2fa 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -5845,6 +5845,37 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); +typedef struct GLFWtheme +{ + int baseTheme; // light/dark + int flags; + unsigned char color[4]; +} GLFWtheme; + +#define GLFW_BASE_THEME_DEFAULT 0 +#define GLFW_BASE_THEME_LIGHT 1 +#define GLFW_BASE_THEME_DARK 2 + +#define GLFW_THEME_FLAG_HAS_COLOR 1 +#define GLFW_THEME_FLAG_HIGH_CONTRAST 2 +#define GLFW_THEME_FLAG_VIBRANT 4 + +typedef void (* GLFWthemefun)(GLFWtheme* theme); + +/*! @brief Notifies the application when the system default theme changes. + * + */ +GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback); + +/*! @brief Sets the theme for a window. + * + * @param[in] window The window to set the theme for. + * @param[in] theme The theme to set. Pass `NULL` to set it to the system default. + */ +GLFWAPI void glfwSetTheme(GLFWwindow* handle, GLFWtheme* theme); +GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle); +GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme(); + /*! @brief Returns the GLFW time. * * This function returns the current GLFW time, in seconds. Unless the time diff --git a/src/cocoa_init.m b/src/cocoa_init.m index b3831df1..64c99b7c 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -496,8 +496,11 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) const _GLFWplatform cocoa = { GLFW_PLATFORM_COCOA, + // Init _glfwInitCocoa, _glfwTerminateCocoa, + _glfwGetSystemDefaultThemeCocoa, + // Input _glfwGetCursorPosCocoa, _glfwSetCursorPosCocoa, _glfwSetCursorModeCocoa, @@ -516,6 +519,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) _glfwPollJoystickCocoa, _glfwGetMappingNameCocoa, _glfwUpdateGamepadGUIDCocoa, + // Monitor _glfwFreeMonitorCocoa, _glfwGetMonitorPosCocoa, _glfwGetMonitorContentScaleCocoa, @@ -524,6 +528,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) _glfwGetVideoModeCocoa, _glfwGetGammaRampCocoa, _glfwSetGammaRampCocoa, + // Window _glfwCreateWindowCocoa, _glfwDestroyWindowCocoa, _glfwSetWindowTitleCocoa, @@ -557,13 +562,17 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) _glfwSetWindowFloatingCocoa, _glfwSetWindowOpacityCocoa, _glfwSetWindowMousePassthroughCocoa, + _glfwGetThemeCocoa, + _glfwSetThemeCocoa, _glfwPollEventsCocoa, _glfwWaitEventsCocoa, _glfwWaitEventsTimeoutCocoa, _glfwPostEmptyEventCocoa, + // EGL _glfwGetEGLPlatformCocoa, _glfwGetEGLNativeDisplayCocoa, _glfwGetEGLNativeWindowCocoa, + // Vulkan _glfwGetRequiredInstanceExtensionsCocoa, _glfwGetPhysicalDevicePresentationSupportCocoa, _glfwCreateWindowSurfaceCocoa, @@ -693,5 +702,10 @@ void _glfwTerminateCocoa(void) } // autoreleasepool } +GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) +{ + return NULL; +} + #endif // _GLFW_COCOA diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 9f7d191d..3535615d 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -300,3 +300,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextNSGL(_GLFWwindow* window); + +GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void); +void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme); +GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 6f8aa978..8391884a 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1876,6 +1876,106 @@ const char* _glfwGetClipboardStringCocoa(void) } // autoreleasepool } +void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) +{ + if (!theme || theme->baseTheme == GLFW_BASE_THEME_DEFAULT) + { + [window->ns.object setAppearance:nil]; + return; + } + + NSAppearanceName name; + + if (theme->baseTheme == GLFW_BASE_THEME_LIGHT) + { + if ((theme->flags & GLFW_THEME_FLAG_VIBRANT) && (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST)) + { + name = NSAppearanceNameAccessibilityHighContrastVibrantLight; + } + else if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { + name = NSAppearanceNameVibrantLight; + } + else if (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST) + { + name = NSAppearanceNameAccessibilityHighContrastAqua; + } + else + { + name = NSAppearanceNameAqua; + } + } + else + { + if ((theme->flags & GLFW_THEME_FLAG_VIBRANT) && (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST)) + { + name = NSAppearanceNameAccessibilityHighContrastVibrantDark; + } + else if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { + name = NSAppearanceNameVibrantDark; + } + else if (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST) + { + name = NSAppearanceNameAccessibilityHighContrastDarkAqua; + } + else + { + name = NSAppearanceNameDarkAqua; + } + } + + NSAppearance* appearance = [NSAppearance appearanceNamed:name]; + [window->ns.object setAppearance:appearance]; +} + +GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) +{ + GLFWtheme theme; + NSAppearanceName name = [window->ns.object appearance].name; + + if (name == NSAppearanceNameAqua) + { + theme.baseTheme = GLFW_BASE_THEME_LIGHT; + } + else if (name == NSAppearanceNameDarkAqua) + { + theme.baseTheme = GLFW_BASE_THEME_DARK; + } + else if (name == NSAppearanceNameVibrantLight) + { + theme.baseTheme = GLFW_BASE_THEME_LIGHT; + theme.flags |= GLFW_THEME_FLAG_VIBRANT; + } + else if (name == NSAppearanceNameVibrantDark) + { + theme.baseTheme = GLFW_BASE_THEME_DARK; + theme.flags |= GLFW_THEME_FLAG_VIBRANT; + } + if (name == NSAppearanceNameAccessibilityHighContrastAqua) + { + theme.baseTheme = GLFW_BASE_THEME_LIGHT; + theme.flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) + { + theme.baseTheme = GLFW_BASE_THEME_DARK; + theme.flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) + { + theme.baseTheme = GLFW_BASE_THEME_LIGHT; + theme.flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) + { + theme.baseTheme = GLFW_BASE_THEME_DARK; + theme.flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + } + + //return theme; + return NULL; +} + + EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) diff --git a/src/init.c b/src/init.c index d07a492e..890af4aa 100644 --- a/src/init.c +++ b/src/init.c @@ -543,3 +543,17 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) return cbfun; } +GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.platform.getSystemDefaultTheme(); +} + +GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) +{ + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + //return _glfw.platform.setSystemThemeCallback(callback); + return NULL; +} diff --git a/src/internal.h b/src/internal.h index 781c8cdc..5bb135e1 100644 --- a/src/internal.h +++ b/src/internal.h @@ -673,6 +673,7 @@ struct _GLFWplatform // init GLFWbool (*init)(void); void (*terminate)(void); + GLFWtheme* (*getSystemDefaultTheme)(void); // input void (*getCursorPos)(_GLFWwindow*,double*,double*); void (*setCursorPos)(_GLFWwindow*,double,double); @@ -735,6 +736,8 @@ struct _GLFWplatform void (*setWindowFloating)(_GLFWwindow*,GLFWbool); void (*setWindowOpacity)(_GLFWwindow*,float); void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool); + GLFWtheme* (*getTheme)(_GLFWwindow*); + void (*setTheme)(_GLFWwindow*,GLFWtheme*); void (*pollEvents)(void); void (*waitEvents)(void); void (*waitEventsTimeout)(double); diff --git a/src/window.c b/src/window.c index 1c8519ff..e380d3fb 100644 --- a/src/window.c +++ b/src/window.c @@ -1153,3 +1153,21 @@ GLFWAPI void glfwPostEmptyEvent(void) _glfw.platform.postEmptyEvent(); } +GLFWAPI void glfwSetTheme(GLFWwindow* handle, GLFWtheme* theme) +{ + + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfw.platform.setTheme(window, theme); +} + +GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.platform.getTheme(window); +} From da9e6dac247470134275de0f573436c85dc821bf Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:53:15 +0100 Subject: [PATCH 02/31] Add theming test program source --- tests/theming.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 tests/theming.c diff --git a/tests/theming.c b/tests/theming.c new file mode 100644 index 00000000..695876ff --- /dev/null +++ b/tests/theming.c @@ -0,0 +1,164 @@ +//======================================================================== +// Theming test program +// Copyright (c) Camilla Löwy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test the theming features. +// +//======================================================================== + +#define GLAD_GL_IMPLEMENTATION +#include +#define GLFW_INCLUDE_NONE +#include + +#include +#include +#include + +const unsigned char theme_colors[6][4] = +{ + { 0, 0, 0, 255 }, // black + { 255, 0, 0, 255 }, // red + { 0, 255, 0, 255 }, // green + { 0, 0, 255, 255 }, // blue + { 255, 255, 255, 255 }, // white + { 128, 128, 128, 255 } // gray (no theme color) +}; + +static int cur_theme_color = 0; + +static GLFWtheme theme; + +static void set_theme(GLFWwindow* window, int theme_color) +{ + memcpy(theme.color, theme_colors[theme_color], 4); + + if (theme_color == 6) + { + theme.flags &= ~GLFW_THEME_FLAG_HAS_COLOR; + } else + { + theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; + } + + const char* title; + + switch (theme.baseTheme) { + case GLFW_BASE_THEME_DEFAULT: + title = "Default theme"; + break; + case GLFW_BASE_THEME_LIGHT: + title = "Light theme"; + break; + case GLFW_BASE_THEME_DARK: + title = "Dark theme"; + break; + } + + glfwSetWindowTitle(window, title); + glfwSetTheme(window, &theme); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_0: + glfwSetWindowTitle(window, "Default theme (NULL)"); + glfwSetTheme(window, NULL); + break; + case GLFW_KEY_1: + theme.baseTheme = GLFW_BASE_THEME_DEFAULT; + set_theme(window, cur_theme_color); + break; + case GLFW_KEY_2: + theme.baseTheme = GLFW_BASE_THEME_LIGHT; + set_theme(window, cur_theme_color); + break; + case GLFW_KEY_3: + theme.baseTheme = GLFW_BASE_THEME_DARK; + set_theme(window, cur_theme_color); + break; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_SPACE: + cur_theme_color = (cur_theme_color + 1) % 6; + set_theme(window, cur_theme_color); + break; + } +} + +int main(int argc, char** argv) +{ + GLFWwindow* window; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + window = glfwCreateWindow(200, 200, "Window Icon", NULL, NULL); + if (!window) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGL(glfwGetProcAddress); + + glfwSetKeyCallback(window, key_callback); + + theme.baseTheme = GLFW_BASE_THEME_DEFAULT; + theme.flags = 0; + theme.color[0] = 0; + theme.color[1] = 0; + theme.color[2] = 0; + theme.color[3] = 0; + set_theme(window, cur_theme_color); + + while (!glfwWindowShouldClose(window)) + { + glClearColor( + theme_colors[cur_theme_color][0] / 255.0f, + theme_colors[cur_theme_color][1] / 255.0f, + theme_colors[cur_theme_color][2] / 255.0f, + theme_colors[cur_theme_color][3] / 255.0f + ); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + glfwWaitEvents(); + } + + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_SUCCESS); +} From f81a9b32c309721b1e0d4f27646a18c19b3f57c5 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:19:41 +0100 Subject: [PATCH 03/31] Add empty theme support for Null platform Add TODO comments for Null and Cocoa theming functions. --- src/cocoa_init.m | 3 ++- src/cocoa_window.m | 5 ++++- src/init.c | 4 +++- src/null_init.c | 13 +++++++++++++ src/null_platform.h | 3 +++ src/null_window.c | 9 +++++++++ 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 64c99b7c..3d3245f5 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -704,7 +704,8 @@ void _glfwTerminateCocoa(void) GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) { - return NULL; + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement } #endif // _GLFW_COCOA diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 8391884a..76aac5b2 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1883,6 +1883,7 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) [window->ns.object setAppearance:nil]; return; } + // TODO: support color NSAppearanceName name; @@ -1929,6 +1930,8 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) { + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); // TODO: remove + GLFWtheme theme; NSAppearanceName name = [window->ns.object appearance].name; @@ -1972,7 +1975,7 @@ GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) } //return theme; - return NULL; + return NULL; // TODO: implement } diff --git a/src/init.c b/src/init.c index 890af4aa..52c708eb 100644 --- a/src/init.c +++ b/src/init.c @@ -555,5 +555,7 @@ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); //return _glfw.platform.setSystemThemeCallback(callback); - return NULL; + + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement } diff --git a/src/null_init.c b/src/null_init.c index de4b28f3..f42f70b4 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -41,8 +41,11 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) const _GLFWplatform null = { GLFW_PLATFORM_NULL, + // Init _glfwInitNull, _glfwTerminateNull, + _glfwGetSystemDefaultThemeNull, + // Input _glfwGetCursorPosNull, _glfwSetCursorPosNull, _glfwSetCursorModeNull, @@ -61,6 +64,7 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) _glfwPollJoystickNull, _glfwGetMappingNameNull, _glfwUpdateGamepadGUIDNull, + // Monitor _glfwFreeMonitorNull, _glfwGetMonitorPosNull, _glfwGetMonitorContentScaleNull, @@ -69,6 +73,7 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) _glfwGetVideoModeNull, _glfwGetGammaRampNull, _glfwSetGammaRampNull, + // Window _glfwCreateWindowNull, _glfwDestroyWindowNull, _glfwSetWindowTitleNull, @@ -102,13 +107,17 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) _glfwSetWindowFloatingNull, _glfwSetWindowOpacityNull, _glfwSetWindowMousePassthroughNull, + _glfwGetThemeNull, + _glfwSetThemeNull, _glfwPollEventsNull, _glfwWaitEventsNull, _glfwWaitEventsTimeoutNull, _glfwPostEmptyEventNull, + // EGL _glfwGetEGLPlatformNull, _glfwGetEGLNativeDisplayNull, _glfwGetEGLNativeWindowNull, + // Vulkan _glfwGetRequiredInstanceExtensionsNull, _glfwGetPhysicalDevicePresentationSupportNull, _glfwCreateWindowSurfaceNull, @@ -131,3 +140,7 @@ void _glfwTerminateNull(void) _glfwTerminateEGL(); } +GLFWtheme* _glfwGetSystemDefaultThemeNull(void) +{ + return NULL; // TODO: should probably return a colorless GLFWtheme with baseTheme set to GLFW_BASE_THEME_LIGHT +} diff --git a/src/null_platform.h b/src/null_platform.h index b646acb3..44a61cad 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -147,3 +147,6 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window, void _glfwPollMonitorsNull(void); +GLFWtheme* _glfwGetSystemDefaultThemeNull(void); +void _glfwSetThemeNull(_GLFWwindow* window, GLFWtheme* theme); +GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window); diff --git a/src/null_window.c b/src/null_window.c index 5cdf3e23..2804fa59 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -551,6 +551,15 @@ const char* _glfwGetClipboardStringNull(void) return _glfw.null.clipboardString; } +void _glfwSetThemeNull(_GLFWwindow* window, GLFWtheme* theme) +{ +} + +GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window) +{ + return NULL; // TODO: see to-do in _glfwGetSystemDefaultThemeNull +} + EGLenum _glfwGetEGLPlatformNull(EGLint** attribs) { return 0; From 963694988293b98d108baf0e97afe55e5365b941 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:24:19 +0100 Subject: [PATCH 04/31] Add theming test program to CMake --- tests/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f81cfeb9..3125c1b5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,6 +40,7 @@ add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD_GL}) add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD_GL}) add_executable(triangle-vulkan WIN32 triangle-vulkan.c ${GLAD_VULKAN}) add_executable(window WIN32 MACOSX_BUNDLE window.c ${GLAD_GL}) +add_executable(theming WIN32 MACOSX_BUNDLE theming.c ${GLAD_GL}) target_link_libraries(empty Threads::Threads) target_link_libraries(threads Threads::Threads) @@ -49,7 +50,7 @@ if (RT_LIBRARY) endif() set(GUI_ONLY_BINARIES empty gamma icon inputlag joysticks tearing threads - timeout title triangle-vulkan window) + timeout title triangle-vulkan window theming) set(CONSOLE_BINARIES allocator clipboard events msaa glfwinfo iconify monitors reopen cursor) @@ -77,6 +78,7 @@ if (APPLE) set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout") set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title") set_target_properties(window PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Window") + set_target_properties(theming PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Theming") set_target_properties(${GUI_ONLY_BINARIES} PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION} From ae437d3eaa185501f41bd7455f1a07fc6d5b7817 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 00:46:37 +0100 Subject: [PATCH 05/31] Fix _glfwGetThemeCocoa --- src/cocoa_window.m | 49 +++++++++++++++++++++++++++++----------------- src/internal.h | 2 ++ tests/theming.c | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 76aac5b2..1551b2b4 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1932,53 +1932,66 @@ GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); // TODO: remove - GLFWtheme theme; + // TODO: must use KVO to observe NSApplication NSAppearance for theme callback. + + GLFWtheme* theme = &window->theme; NSAppearanceName name = [window->ns.object appearance].name; + theme->baseTheme = 0; + theme->flags = 0; + if (name == NSAppearanceNameAqua) { - theme.baseTheme = GLFW_BASE_THEME_LIGHT; + theme->baseTheme = GLFW_BASE_THEME_LIGHT; } else if (name == NSAppearanceNameDarkAqua) { - theme.baseTheme = GLFW_BASE_THEME_DARK; + theme->baseTheme = GLFW_BASE_THEME_DARK; } else if (name == NSAppearanceNameVibrantLight) { - theme.baseTheme = GLFW_BASE_THEME_LIGHT; - theme.flags |= GLFW_THEME_FLAG_VIBRANT; + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; } else if (name == NSAppearanceNameVibrantDark) { - theme.baseTheme = GLFW_BASE_THEME_DARK; - theme.flags |= GLFW_THEME_FLAG_VIBRANT; + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; } if (name == NSAppearanceNameAccessibilityHighContrastAqua) { - theme.baseTheme = GLFW_BASE_THEME_LIGHT; - theme.flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; } else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) { - theme.baseTheme = GLFW_BASE_THEME_DARK; - theme.flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; } else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) { - theme.baseTheme = GLFW_BASE_THEME_LIGHT; - theme.flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; } else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) { - theme.baseTheme = GLFW_BASE_THEME_DARK; - theme.flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; } - //return theme; - return NULL; // TODO: implement + // TODO: this is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. + // TODO: must use KVO to observe the controlAccentColor for the theme callback. + NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + + theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme->color[0] = color.redComponent * 255; + theme->color[1] = color.greenComponent * 255; + theme->color[2] = color.blueComponent * 255; + theme->color[3] = color.alphaComponent * 255; + + return theme; } - EGLenum _glfwGetEGLPlatformCocoa(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) diff --git a/src/internal.h b/src/internal.h index 5bb135e1..1024e193 100644 --- a/src/internal.h +++ b/src/internal.h @@ -570,6 +570,8 @@ struct _GLFWwindow GLFWcharmodsfun charmods; GLFWdropfun drop; } callbacks; + + GLFWtheme theme; // This is defined in platform.h GLFW_PLATFORM_WINDOW_STATE diff --git a/tests/theming.c b/tests/theming.c index 695876ff..c74425c8 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -50,6 +50,33 @@ static int cur_theme_color = 0; static GLFWtheme theme; +static void print_theme(GLFWtheme* theme, const char* title) +{ + int n = 0; + + printf("%s: {\n", title); + printf(" Base: %s\n", theme->baseTheme == GLFW_BASE_THEME_LIGHT ? "light" : "dark"); + printf(" Flags: ["); + if (theme->flags & GLFW_THEME_FLAG_HAS_COLOR) + { + printf(n++ > 0 ? ", %s" : "%s", "HAS_COLOR"); + } + if (theme->flags & GLFW_THEME_FLAG_VIBRANT) + { + printf(n++ > 0 ? ", %s" : "%s", "VIBRANT"); + } + if (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST) + { + printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST"); + } + printf("]\n"); + if (theme->flags & GLFW_THEME_FLAG_HAS_COLOR) + { + printf(" Color: [%i, %i, %i, %i]\n", theme->color[0], theme->color[1], theme->color[2], theme->color[3]); + } + printf("}\n"); +} + static void set_theme(GLFWwindow* window, int theme_color) { memcpy(theme.color, theme_colors[theme_color], 4); @@ -110,9 +137,17 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, cur_theme_color = (cur_theme_color + 1) % 6; set_theme(window, cur_theme_color); break; + case GLFW_KEY_P: + print_theme(glfwGetTheme(window), "Window theme"); + break; } } +static void theme_callback(GLFWtheme* theme) +{ + print_theme(theme, "System theme changed to"); +} + int main(int argc, char** argv) { GLFWwindow* window; @@ -144,6 +179,8 @@ int main(int argc, char** argv) theme.color[2] = 0; theme.color[3] = 0; set_theme(window, cur_theme_color); + + glfwSetSystemThemeCallback(theme_callback); while (!glfwWindowShouldClose(window)) { From b091cc0c803c2c5006bc5b3a825a25698ff2fe2a Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 02:22:57 +0100 Subject: [PATCH 06/31] Fix glfwSetSystemThemeCallback and its Cocoa implementation --- src/cocoa_init.m | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/cocoa_platform.h | 4 ++ src/cocoa_window.m | 46 +-------------------- src/init.c | 9 ++-- src/input.c | 7 ++++ src/internal.h | 3 ++ 6 files changed, 118 insertions(+), 48 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 3d3245f5..46d9925b 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -175,6 +175,50 @@ static void createMenuBar(void) [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; } +void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) +{ + NSAppearanceName name = appearance.name; + + if (name == NSAppearanceNameAqua) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + } + else if (name == NSAppearanceNameDarkAqua) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + } + else if (name == NSAppearanceNameVibrantLight) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; + } + else if (name == NSAppearanceNameVibrantDark) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; + } + if (name == NSAppearanceNameAccessibilityHighContrastAqua) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + } + else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + } +} + // Create key code translation tables // static void createKeyTables(void) @@ -393,6 +437,41 @@ static GLFWbool initializeTIS(void) - (void)doNothing:(id)object { } +/* +- (void)themeChanged:(NSNotification*)notification +{ + _glfwInputSystemTheme(NULL); +} + +- (void)accentColorChanged +{ + _glfwInputSystemTheme(NULL); +}*/ + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + // This class is never subclassed, so it's safe to ignore the context parameter + + GLFWtheme theme = { 0, 0 }; + nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); + + NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + + theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme.color[0] = color.redComponent * 255; + theme.color[1] = color.greenComponent * 255; + theme.color[2] = color.blueComponent * 255; + theme.color[3] = color.alphaComponent * 255; + + _glfwInputSystemTheme(&theme); + + /*if ([keyPath isEqualToString:@"controlAccentColor"]) { + [self accentColorChanged]; + }*/ +} @end // GLFWHelper @@ -641,6 +720,24 @@ int _glfwInitCocoa(void) return GLFW_FALSE; _glfwPollMonitorsCocoa(); + + /* + [[NSNotificationCenter defaultCenter] + addObserver:_glfw.ns.helper + selector:@selector(themeChanged:) + name:@"AppleInterfaceThemeChangedNotification" + object:nil]; + + [NSColor addObserver:_glfw.ns.helper + forKeyPath:@"controlAccentColor" + options:0 + context:nil]; + */ + + [NSApp addObserver:_glfw.ns.helper + forKeyPath:@"effectiveAppearance" + options:0 + context:nil]; if (![[NSRunningApplication currentApplication] isFinishedLaunching]) [NSApp run]; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 3535615d..d22ca54b 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -39,8 +39,10 @@ #import #else typedef void* id; +typedef void NSAppearance; #endif + // NOTE: Many Cocoa enum values have been renamed and we need to build across // SDK versions where one is unavailable or deprecated. // We use the newer names in code and replace them with the older names if @@ -291,6 +293,8 @@ void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor); float _glfwTransformYCocoa(float y); +void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme); + void* _glfwLoadLocalVulkanLoaderCocoa(void); GLFWbool _glfwInitNSGL(void); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 1551b2b4..b1416201 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1930,58 +1930,16 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); // TODO: remove - - // TODO: must use KVO to observe NSApplication NSAppearance for theme callback. - GLFWtheme* theme = &window->theme; - NSAppearanceName name = [window->ns.object appearance].name; theme->baseTheme = 0; theme->flags = 0; - if (name == NSAppearanceNameAqua) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - } - else if (name == NSAppearanceNameDarkAqua) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - } - else if (name == NSAppearanceNameVibrantLight) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; - } - else if (name == NSAppearanceNameVibrantDark) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; - } - if (name == NSAppearanceNameAccessibilityHighContrastAqua) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; - } + nsAppearanceToGLFWTheme([window->ns.object appearance], theme); // TODO: this is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. - // TODO: must use KVO to observe the controlAccentColor for the theme callback. NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; theme->color[0] = color.redComponent * 255; diff --git a/src/init.c b/src/init.c index 52c708eb..41107ee8 100644 --- a/src/init.c +++ b/src/init.c @@ -554,8 +554,9 @@ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - //return _glfw.platform.setSystemThemeCallback(callback); - - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + //return _glfw.platform.setSystemThemeCallback(callback + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP(GLFWthemefun, _glfw.callbacks.theme, callback); + return callback; } diff --git a/src/input.c b/src/input.c index 36128e10..dc2ab137 100644 --- a/src/input.c +++ b/src/input.c @@ -477,6 +477,13 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) js->hats[hat] = value; } +void _glfwInputSystemTheme(GLFWtheme* theme) +{ + assert(theme != NULL); + + if (_glfw.callbacks.theme) + _glfw.callbacks.theme(theme); +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// diff --git a/src/internal.h b/src/internal.h index 1024e193..6dbe5d01 100644 --- a/src/internal.h +++ b/src/internal.h @@ -868,6 +868,7 @@ struct _GLFWlibrary struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; + GLFWthemefun theme; } callbacks; // These are defined in platform.h @@ -934,6 +935,8 @@ void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); +void _glfwInputSystemTheme(GLFWtheme* theme); + void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); From 5ee6c4526d81e8047d5067218c992331f3c79467 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 02:30:27 +0100 Subject: [PATCH 07/31] Guard Cocoa system appearance observation in ifdefs for MacOS 10.14+ NSAppearanceName constants have various OS constraints, and so require more thorough checks. --- src/cocoa_init.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 46d9925b..059eb10f 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -448,6 +448,7 @@ static GLFWbool initializeTIS(void) _glfwInputSystemTheme(NULL); }*/ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change @@ -472,6 +473,7 @@ static GLFWbool initializeTIS(void) [self accentColorChanged]; }*/ } +#endif @end // GLFWHelper @@ -734,10 +736,12 @@ int _glfwInitCocoa(void) context:nil]; */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 [NSApp addObserver:_glfw.ns.helper forKeyPath:@"effectiveAppearance" options:0 context:nil]; +#endif if (![[NSRunningApplication currentApplication] isFinishedLaunching]) [NSApp run]; From 1259135f1a424523d366053d8efb3e018cdb759e Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 04:06:14 +0100 Subject: [PATCH 08/31] Fix NSAppearance conversion to GLFWtheme No longer attempts to set high contrast modes, as Cocoa does not allow this. --- src/cocoa_init.m | 42 +++++++++++++++++++++++++++++++++--------- src/cocoa_platform.h | 3 +++ src/cocoa_window.m | 23 +++++------------------ 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 059eb10f..9b52b9b4 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -35,6 +35,19 @@ // Needed for _NSGetProgname #include + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 +NSAppearanceName const NSAppearanceNameVibrantLight = @"NSAppearanceNameVibrantLight"; +NSAppearanceName const NSAppearanceNameVibrantDark = @"NSAppearanceNameVibrantDark"; +#endif +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 +NSAppearanceName const NSAppearanceNameDarkAqua = @"NSAppearanceNameDarkAqua"; +NSAppearanceName const NSAppearanceNameAccessibilityHighContrastAqua = @"NSAppearanceNameAccessibilityAqua"; +NSAppearanceName const NSAppearanceNameAccessibilityHighContrastDarkAqua = @"NSAppearanceNameAccessibilityDarkAqua"; +NSAppearanceName const NSAppearanceNameAccessibilityHighContrastVibrantLight = @"NSAppearanceNameAccessibilityVibrantLight"; +NSAppearanceName const NSAppearanceNameAccessibilityHighContrastVibrantDark = @"NSAppearanceNameAccessibilityVibrantDark"; +#endif + // Change to our application bundle's resources directory, if present // static void changeToResourcesDirectory(void) @@ -177,42 +190,51 @@ static void createMenuBar(void) void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) { - NSAppearanceName name = appearance.name; + NSAppearanceName name = [appearance bestMatchFromAppearancesWithNames:@[ + NSAppearanceNameAqua, + NSAppearanceNameDarkAqua, + NSAppearanceNameVibrantLight, + NSAppearanceNameVibrantDark, + NSAppearanceNameAccessibilityHighContrastAqua, + NSAppearanceNameAccessibilityHighContrastDarkAqua, + NSAppearanceNameAccessibilityHighContrastVibrantLight, + NSAppearanceNameAccessibilityHighContrastVibrantDark + ]]; - if (name == NSAppearanceNameAqua) + if ([name isEqualToString:NSAppearanceNameAqua]) { theme->baseTheme = GLFW_BASE_THEME_LIGHT; } - else if (name == NSAppearanceNameDarkAqua) + else if ([name isEqualToString:NSAppearanceNameDarkAqua]) { theme->baseTheme = GLFW_BASE_THEME_DARK; } - else if (name == NSAppearanceNameVibrantLight) + else if ([name isEqualToString:NSAppearanceNameVibrantLight]) { theme->baseTheme = GLFW_BASE_THEME_LIGHT; theme->flags |= GLFW_THEME_FLAG_VIBRANT; } - else if (name == NSAppearanceNameVibrantDark) + else if ([name isEqualToString:NSAppearanceNameVibrantDark]) { theme->baseTheme = GLFW_BASE_THEME_DARK; theme->flags |= GLFW_THEME_FLAG_VIBRANT; } - if (name == NSAppearanceNameAccessibilityHighContrastAqua) + if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]) { theme->baseTheme = GLFW_BASE_THEME_LIGHT; theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; } - else if (name == NSAppearanceNameAccessibilityHighContrastDarkAqua) + else if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]) { theme->baseTheme = GLFW_BASE_THEME_DARK; theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; } - else if (name == NSAppearanceNameAccessibilityHighContrastVibrantLight) + else if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight]) { theme->baseTheme = GLFW_BASE_THEME_LIGHT; theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; } - else if (name == NSAppearanceNameAccessibilityHighContrastVibrantDark) + else if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]) { theme->baseTheme = GLFW_BASE_THEME_DARK; theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; @@ -456,6 +478,8 @@ static GLFWbool initializeTIS(void) { // This class is never subclassed, so it's safe to ignore the context parameter + // TODO: FIXME: this method is invoked twice when the high contrast setting is edited in the preferences. + GLFWtheme theme = { 0, 0 }; nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index d22ca54b..9246b4b8 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -42,6 +42,9 @@ typedef void* id; typedef void NSAppearance; #endif +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 +typedef NSString *NSAppearanceName; +#endif // NOTE: Many Cocoa enum values have been renamed and we need to build across // SDK versions where one is unavailable or deprecated. diff --git a/src/cocoa_window.m b/src/cocoa_window.m index b1416201..e4965d89 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1884,22 +1884,17 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) return; } // TODO: support color + // TODO: fix vibrancy + // As per the Cocoa documentation, passing the high contrast names to + // appearanceNamed: will result in nil, so these can not be used. NSAppearanceName name; if (theme->baseTheme == GLFW_BASE_THEME_LIGHT) { - if ((theme->flags & GLFW_THEME_FLAG_VIBRANT) && (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST)) - { - name = NSAppearanceNameAccessibilityHighContrastVibrantLight; - } - else if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { + if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { name = NSAppearanceNameVibrantLight; } - else if (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST) - { - name = NSAppearanceNameAccessibilityHighContrastAqua; - } else { name = NSAppearanceNameAqua; @@ -1907,17 +1902,9 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) } else { - if ((theme->flags & GLFW_THEME_FLAG_VIBRANT) && (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST)) - { - name = NSAppearanceNameAccessibilityHighContrastVibrantDark; - } - else if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { + if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { name = NSAppearanceNameVibrantDark; } - else if (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST) - { - name = NSAppearanceNameAccessibilityHighContrastDarkAqua; - } else { name = NSAppearanceNameDarkAqua; From 2db9a368f7992faaae670eaac24125a58b97d36e Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 04:07:43 +0100 Subject: [PATCH 09/31] Add credit --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 47301dae..015e4ce3 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -98,6 +98,7 @@ video tutorials. - IntellectualKitty - Aaron Jacobs - JannikGM + - Andreas O. Jansen - Erik S. V. Jansson - jjYBdx4IL - Toni Jovanoski From fe1eddc2ad8a8dc38d2701b8b1c41eb30203e0d7 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 06:38:27 +0100 Subject: [PATCH 10/31] Conditionally execute theming based on OS version --- src/cocoa_init.m | 48 ++++++++++++++++++++++++++++------------------ src/cocoa_window.m | 44 ++++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 9b52b9b4..213e41b1 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -190,16 +190,23 @@ static void createMenuBar(void) void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) { - NSAppearanceName name = [appearance bestMatchFromAppearancesWithNames:@[ - NSAppearanceNameAqua, - NSAppearanceNameDarkAqua, - NSAppearanceNameVibrantLight, - NSAppearanceNameVibrantDark, - NSAppearanceNameAccessibilityHighContrastAqua, - NSAppearanceNameAccessibilityHighContrastDarkAqua, - NSAppearanceNameAccessibilityHighContrastVibrantLight, - NSAppearanceNameAccessibilityHighContrastVibrantDark - ]]; + NSAppearanceName name; + + if (@available(macOS 10.14, *)) + { + name = [appearance bestMatchFromAppearancesWithNames:@[ + NSAppearanceNameAqua, + NSAppearanceNameDarkAqua, + NSAppearanceNameVibrantLight, + NSAppearanceNameVibrantDark, + NSAppearanceNameAccessibilityHighContrastAqua, + NSAppearanceNameAccessibilityHighContrastDarkAqua, + NSAppearanceNameAccessibilityHighContrastVibrantLight, + NSAppearanceNameAccessibilityHighContrastVibrantDark + ]]; + } else { + name = appearance.name; + } if ([name isEqualToString:NSAppearanceNameAqua]) { @@ -480,16 +487,19 @@ static GLFWbool initializeTIS(void) // TODO: FIXME: this method is invoked twice when the high contrast setting is edited in the preferences. - GLFWtheme theme = { 0, 0 }; - nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); + GLFWtheme theme = { GLFW_BASE_THEME_LIGHT, 0 }; - NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; - - theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; - theme.color[0] = color.redComponent * 255; - theme.color[1] = color.greenComponent * 255; - theme.color[2] = color.blueComponent * 255; - theme.color[3] = color.alphaComponent * 255; + if (@available(macOS 10.14, *)) { + nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); + + NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + + theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme.color[0] = color.redComponent * 255; + theme.color[1] = color.greenComponent * 255; + theme.color[2] = color.blueComponent * 255; + theme.color[3] = color.alphaComponent * 255; + } _glfwInputSystemTheme(&theme); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index e4965d89..16bd5eb3 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1883,6 +1883,12 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) [window->ns.object setAppearance:nil]; return; } + + if (@available(macOS 10.10, *)) {} else + { + return; + } + // TODO: support color // TODO: fix vibrancy @@ -1892,7 +1898,8 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) if (theme->baseTheme == GLFW_BASE_THEME_LIGHT) { - if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { + if (theme->flags & GLFW_THEME_FLAG_VIBRANT) + { name = NSAppearanceNameVibrantLight; } else @@ -1902,12 +1909,16 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) } else { - if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { + if (theme->flags & GLFW_THEME_FLAG_VIBRANT) + { name = NSAppearanceNameVibrantDark; } - else + else if (@available(macOS 10.14, *)) { name = NSAppearanceNameDarkAqua; + } else + { + name = NSAppearanceNameAqua; } } @@ -1919,20 +1930,25 @@ GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) { GLFWtheme* theme = &window->theme; - theme->baseTheme = 0; + theme->baseTheme = GLFW_BASE_THEME_LIGHT; theme->flags = 0; - nsAppearanceToGLFWTheme([window->ns.object appearance], theme); + if (@available(macOS 10.09, *)) + { + nsAppearanceToGLFWTheme([window->ns.object appearance], theme); + } - // TODO: this is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. - NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; - // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. - - theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; - theme->color[0] = color.redComponent * 255; - theme->color[1] = color.greenComponent * 255; - theme->color[2] = color.blueComponent * 255; - theme->color[3] = color.alphaComponent * 255; + if (@available(macOS 10.14, *)) { + // TODO: this is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. + NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. + + theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme->color[0] = color.redComponent * 255; + theme->color[1] = color.greenComponent * 255; + theme->color[2] = color.blueComponent * 255; + theme->color[3] = color.alphaComponent * 255; + } return theme; } From bbd5bcb7fdc0a20115ce1648d73a18a25bd7cc5f Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:31:31 +0100 Subject: [PATCH 11/31] Attempt at fixing compilation errors for MacOS --- src/cocoa_init.m | 4 ++-- src/cocoa_platform.h | 1 + src/cocoa_window.m | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 213e41b1..8c60bb31 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -36,11 +36,11 @@ #include -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101000 NSAppearanceName const NSAppearanceNameVibrantLight = @"NSAppearanceNameVibrantLight"; NSAppearanceName const NSAppearanceNameVibrantDark = @"NSAppearanceNameVibrantDark"; #endif -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 NSAppearanceName const NSAppearanceNameDarkAqua = @"NSAppearanceNameDarkAqua"; NSAppearanceName const NSAppearanceNameAccessibilityHighContrastAqua = @"NSAppearanceNameAccessibilityAqua"; NSAppearanceName const NSAppearanceNameAccessibilityHighContrastDarkAqua = @"NSAppearanceNameAccessibilityDarkAqua"; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 9246b4b8..84a0ace0 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -40,6 +40,7 @@ #else typedef void* id; typedef void NSAppearance; +typedef id NSString; #endif #if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 16bd5eb3..4e32731a 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1935,7 +1935,7 @@ GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) if (@available(macOS 10.09, *)) { - nsAppearanceToGLFWTheme([window->ns.object appearance], theme); + nsAppearanceToGLFWTheme([(NSWindow*)window->ns.object appearance], theme); } if (@available(macOS 10.14, *)) { From b5363d87d2d01055bf5cbedfefce9eab0bbb1f73 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:50:42 +0100 Subject: [PATCH 12/31] Fix remaining build errors for Cocoa. --- src/cocoa_init.m | 94 ++++++++++++++++++++++++---------------------- src/cocoa_window.m | 4 +- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 8c60bb31..f9726f84 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -35,19 +35,6 @@ // Needed for _NSGetProgname #include - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101000 -NSAppearanceName const NSAppearanceNameVibrantLight = @"NSAppearanceNameVibrantLight"; -NSAppearanceName const NSAppearanceNameVibrantDark = @"NSAppearanceNameVibrantDark"; -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 -NSAppearanceName const NSAppearanceNameDarkAqua = @"NSAppearanceNameDarkAqua"; -NSAppearanceName const NSAppearanceNameAccessibilityHighContrastAqua = @"NSAppearanceNameAccessibilityAqua"; -NSAppearanceName const NSAppearanceNameAccessibilityHighContrastDarkAqua = @"NSAppearanceNameAccessibilityDarkAqua"; -NSAppearanceName const NSAppearanceNameAccessibilityHighContrastVibrantLight = @"NSAppearanceNameAccessibilityVibrantLight"; -NSAppearanceName const NSAppearanceNameAccessibilityHighContrastVibrantDark = @"NSAppearanceNameAccessibilityVibrantDark"; -#endif - // Change to our application bundle's resources directory, if present // static void changeToResourcesDirectory(void) @@ -211,41 +198,58 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) if ([name isEqualToString:NSAppearanceNameAqua]) { theme->baseTheme = GLFW_BASE_THEME_LIGHT; + return; } - else if ([name isEqualToString:NSAppearanceNameDarkAqua]) + + if (@available(macOS 10.10, *)) { + if ([name isEqualToString:NSAppearanceNameVibrantLight]) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; + return; + } + if ([name isEqualToString:NSAppearanceNameVibrantDark]) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT; + return; + } + } + + if (@available(macOS 10.14, *)) { - theme->baseTheme = GLFW_BASE_THEME_DARK; - } - else if ([name isEqualToString:NSAppearanceNameVibrantLight]) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; - } - else if ([name isEqualToString:NSAppearanceNameVibrantDark]) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; - } - if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight]) - { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; - } - else if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]) - { - theme->baseTheme = GLFW_BASE_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + if ([name isEqualToString:NSAppearanceNameDarkAqua]) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + return; + } + if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + return; + } + if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + return; + } + if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight]) + { + theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + return; + } + if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]) + { + theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + return; + } } + + theme->baseTheme = GLFW_BASE_THEME_LIGHT; } // Create key code translation tables diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 4e32731a..6ba54501 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1880,7 +1880,7 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) { if (!theme || theme->baseTheme == GLFW_BASE_THEME_DEFAULT) { - [window->ns.object setAppearance:nil]; + [(NSWindow*)window->ns.object setAppearance:nil]; return; } @@ -1923,7 +1923,7 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) } NSAppearance* appearance = [NSAppearance appearanceNamed:name]; - [window->ns.object setAppearance:appearance]; + [(NSWindow*)window->ns.object setAppearance:appearance]; } GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) From 266f67c6db46302a64fdf53973d6fac36097b0e0 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 10:16:55 +0100 Subject: [PATCH 13/31] Add stubs for all platforms --- src/cocoa_init.m | 1 + src/internal.h | 11 ++++++----- src/win32_init.c | 16 ++++++++++++++++ src/win32_platform.h | 3 +++ src/win32_window.c | 11 +++++++++++ src/wl_init.c | 16 ++++++++++++++++ src/wl_platform.h | 3 +++ src/wl_window.c | 11 +++++++++++ src/x11_init.c | 16 ++++++++++++++++ src/x11_platform.h | 3 +++ src/x11_window.c | 11 +++++++++++ 11 files changed, 97 insertions(+), 5 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index f9726f84..9570d4c2 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -683,6 +683,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform) _glfwSetWindowMousePassthroughCocoa, _glfwGetThemeCocoa, _glfwSetThemeCocoa, + // Events _glfwPollEventsCocoa, _glfwWaitEventsCocoa, _glfwWaitEventsTimeoutCocoa, diff --git a/src/internal.h b/src/internal.h index 6dbe5d01..5d3ec9a6 100644 --- a/src/internal.h +++ b/src/internal.h @@ -672,11 +672,11 @@ struct _GLFWmutex struct _GLFWplatform { int platformID; - // init + // Init GLFWbool (*init)(void); void (*terminate)(void); GLFWtheme* (*getSystemDefaultTheme)(void); - // input + // Input void (*getCursorPos)(_GLFWwindow*,double*,double*); void (*setCursorPos)(_GLFWwindow*,double,double); void (*setCursorMode)(_GLFWwindow*,int); @@ -695,7 +695,7 @@ struct _GLFWplatform GLFWbool (*pollJoystick)(_GLFWjoystick*,int); const char* (*getMappingName)(void); void (*updateGamepadGUID)(char*); - // monitor + // Monitor void (*freeMonitor)(_GLFWmonitor*); void (*getMonitorPos)(_GLFWmonitor*,int*,int*); void (*getMonitorContentScale)(_GLFWmonitor*,float*,float*); @@ -704,7 +704,7 @@ struct _GLFWplatform void (*getVideoMode)(_GLFWmonitor*,GLFWvidmode*); GLFWbool (*getGammaRamp)(_GLFWmonitor*,GLFWgammaramp*); void (*setGammaRamp)(_GLFWmonitor*,const GLFWgammaramp*); - // window + // Window GLFWbool (*createWindow)(_GLFWwindow*,const _GLFWwndconfig*,const _GLFWctxconfig*,const _GLFWfbconfig*); void (*destroyWindow)(_GLFWwindow*); void (*setWindowTitle)(_GLFWwindow*,const char*); @@ -740,6 +740,7 @@ struct _GLFWplatform void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool); GLFWtheme* (*getTheme)(_GLFWwindow*); void (*setTheme)(_GLFWwindow*,GLFWtheme*); + // Events void (*pollEvents)(void); void (*waitEvents)(void); void (*waitEventsTimeout)(double); @@ -748,7 +749,7 @@ struct _GLFWplatform EGLenum (*getEGLPlatform)(EGLint**); EGLNativeDisplayType (*getEGLNativeDisplay)(void); EGLNativeWindowType (*getEGLNativeWindow)(_GLFWwindow*); - // vulkan + // Vulkan void (*getRequiredInstanceExtensions)(char**); GLFWbool (*getPhysicalDevicePresentationSupport)(VkInstance,VkPhysicalDevice,uint32_t); VkResult (*createWindowSurface)(VkInstance,_GLFWwindow*,const VkAllocationCallbacks*,VkSurfaceKHR*); diff --git a/src/win32_init.c b/src/win32_init.c index 64393e77..629ef33f 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -606,8 +606,11 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) const _GLFWplatform win32 = { GLFW_PLATFORM_WIN32, + // Init _glfwInitWin32, _glfwTerminateWin32, + _glfwGetSystemDefaultThemeWin32 + // Input _glfwGetCursorPosWin32, _glfwSetCursorPosWin32, _glfwSetCursorModeWin32, @@ -626,6 +629,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) _glfwPollJoystickWin32, _glfwGetMappingNameWin32, _glfwUpdateGamepadGUIDWin32, + // Monitor _glfwFreeMonitorWin32, _glfwGetMonitorPosWin32, _glfwGetMonitorContentScaleWin32, @@ -634,6 +638,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) _glfwGetVideoModeWin32, _glfwGetGammaRampWin32, _glfwSetGammaRampWin32, + // Window _glfwCreateWindowWin32, _glfwDestroyWindowWin32, _glfwSetWindowTitleWin32, @@ -667,13 +672,18 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) _glfwSetWindowFloatingWin32, _glfwSetWindowOpacityWin32, _glfwSetWindowMousePassthroughWin32, + _glfwGetThemeWin32, + _glfwSetThemeWin32, + // Events _glfwPollEventsWin32, _glfwWaitEventsWin32, _glfwWaitEventsTimeoutWin32, _glfwPostEmptyEventWin32, + // EGL _glfwGetEGLPlatformWin32, _glfwGetEGLNativeDisplayWin32, _glfwGetEGLNativeWindowWin32, + // Vulkan _glfwGetRequiredInstanceExtensionsWin32, _glfwGetPhysicalDevicePresentationSupportWin32, _glfwCreateWindowSurfaceWin32, @@ -727,5 +737,11 @@ void _glfwTerminateWin32(void) freeLibraries(); } +GLFWtheme* _glfwGetSystemDefaultThemeWin32(void) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement +} + #endif // _GLFW_WIN32 diff --git a/src/win32_platform.h b/src/win32_platform.h index 82b34bb9..ca46cb16 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -622,3 +622,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +GLFWtheme* _glfwGetSystemDefaultThemeWin32(void); +void _glfwSetThemeWin32(_GLFWwindow* window, GLFWtheme* theme); +GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window); diff --git a/src/win32_window.c b/src/win32_window.c index 676640bf..53a11a14 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2373,6 +2373,17 @@ const char* _glfwGetClipboardStringWin32(void) return _glfw.win32.clipboardString; } +void _glfwSetThemeWin32(_GLFWwindow* window, GLFWtheme* theme) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); +} + +GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement +} + EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) diff --git a/src/wl_init.c b/src/wl_init.c index 7a9157a4..49e785cc 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -379,8 +379,11 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) const _GLFWplatform wayland = { GLFW_PLATFORM_WAYLAND, + // Init _glfwInitWayland, _glfwTerminateWayland, + _glfwGetSystemDefaultThemeWayland + // Input _glfwGetCursorPosWayland, _glfwSetCursorPosWayland, _glfwSetCursorModeWayland, @@ -407,6 +410,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) _glfwGetMappingNameNull, _glfwUpdateGamepadGUIDNull, #endif + // Monitor _glfwFreeMonitorWayland, _glfwGetMonitorPosWayland, _glfwGetMonitorContentScaleWayland, @@ -415,6 +419,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) _glfwGetVideoModeWayland, _glfwGetGammaRampWayland, _glfwSetGammaRampWayland, + // Window _glfwCreateWindowWayland, _glfwDestroyWindowWayland, _glfwSetWindowTitleWayland, @@ -448,13 +453,18 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) _glfwSetWindowFloatingWayland, _glfwSetWindowOpacityWayland, _glfwSetWindowMousePassthroughWayland, + _glfwGetThemeWayland, + _glfwSetThemeWayland, + // Events _glfwPollEventsWayland, _glfwWaitEventsWayland, _glfwWaitEventsTimeoutWayland, _glfwPostEmptyEventWayland, + // EGL _glfwGetEGLPlatformWayland, _glfwGetEGLNativeDisplayWayland, _glfwGetEGLNativeWindowWayland, + // Vulkan _glfwGetRequiredInstanceExtensionsWayland, _glfwGetPhysicalDevicePresentationSupportWayland, _glfwCreateWindowSurfaceWayland, @@ -793,5 +803,11 @@ void _glfwTerminateWayland(void) _glfw_free(_glfw.wl.clipboardString); } +GLFWtheme* _glfwGetSystemDefaultThemeWayland(void) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement +} + #endif // _GLFW_WAYLAND diff --git a/src/wl_platform.h b/src/wl_platform.h index 238e1ed4..af357cac 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -519,3 +519,6 @@ void _glfwUpdateContentScaleWayland(_GLFWwindow* window); void _glfwAddSeatListenerWayland(struct wl_seat* seat); void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); +GLFWtheme* _glfwGetSystemDefaultThemeWayland(void); +void _glfwSetThemeWayland(_GLFWwindow* window, GLFWtheme* theme); +GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window); diff --git a/src/wl_window.c b/src/wl_window.c index a227c16f..48a60b52 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -2775,6 +2775,17 @@ const char* _glfwGetClipboardStringWayland(void) return _glfw.wl.clipboardString; } +void _glfwSetThemeWayland(_GLFWwindow* window, GLFWtheme* theme) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); +} + +GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement +} + EGLenum _glfwGetEGLPlatformWayland(EGLint** attribs) { if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_wayland) diff --git a/src/x11_init.c b/src/x11_init.c index a0100f2f..2806eadb 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1169,8 +1169,11 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) const _GLFWplatform x11 = { GLFW_PLATFORM_X11, + // Init _glfwInitX11, _glfwTerminateX11, + _glfwGetSystemDefaultThemeX11 + // Input _glfwGetCursorPosX11, _glfwSetCursorPosX11, _glfwSetCursorModeX11, @@ -1197,6 +1200,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) _glfwGetMappingNameNull, _glfwUpdateGamepadGUIDNull, #endif + // Monitor _glfwFreeMonitorX11, _glfwGetMonitorPosX11, _glfwGetMonitorContentScaleX11, @@ -1205,6 +1209,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) _glfwGetVideoModeX11, _glfwGetGammaRampX11, _glfwSetGammaRampX11, + // Window _glfwCreateWindowX11, _glfwDestroyWindowX11, _glfwSetWindowTitleX11, @@ -1238,13 +1243,18 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) _glfwSetWindowFloatingX11, _glfwSetWindowOpacityX11, _glfwSetWindowMousePassthroughX11, + _glfwGetThemeX11, + _glfwSetThemeX11, + // Events _glfwPollEventsX11, _glfwWaitEventsX11, _glfwWaitEventsTimeoutX11, _glfwPostEmptyEventX11, + // EGL _glfwGetEGLPlatformX11, _glfwGetEGLNativeDisplayX11, _glfwGetEGLNativeWindowX11, + // Vulkan _glfwGetRequiredInstanceExtensionsX11, _glfwGetPhysicalDevicePresentationSupportX11, _glfwCreateWindowSurfaceX11, @@ -1654,5 +1664,11 @@ void _glfwTerminateX11(void) } } +GLFWtheme* _glfwGetSystemDefaultThemeX11(void) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement +} + #endif // _GLFW_X11 diff --git a/src/x11_platform.h b/src/x11_platform.h index cdea3957..e0214699 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -1002,3 +1002,6 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); +GLFWtheme* _glfwGetSystemDefaultThemeX11(void); +void _glfwSetThemeX11(_GLFWwindow* window, GLFWtheme* theme); +GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window); diff --git a/src/x11_window.c b/src/x11_window.c index 7da9b965..f3adc490 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3082,6 +3082,17 @@ const char* _glfwGetClipboardStringX11(void) return getSelectionString(_glfw.x11.CLIPBOARD); } +void _glfwSetThemeX11(_GLFWwindow* window, GLFWtheme* theme) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); +} + +GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window) +{ + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + return NULL; // TODO: implement +} + EGLenum _glfwGetEGLPlatformX11(EGLint** attribs) { if (_glfw.egl.ANGLE_platform_angle) From 17ab6209df6a9543e768f1dba20b8e2e8c8fdef8 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 26 Jan 2023 10:22:40 +0100 Subject: [PATCH 14/31] Fix duplicate syntax error in win32_init, wl_init, x11_init --- src/win32_init.c | 2 +- src/wl_init.c | 2 +- src/x11_init.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 629ef33f..2ee73b23 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -609,7 +609,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform) // Init _glfwInitWin32, _glfwTerminateWin32, - _glfwGetSystemDefaultThemeWin32 + _glfwGetSystemDefaultThemeWin32, // Input _glfwGetCursorPosWin32, _glfwSetCursorPosWin32, diff --git a/src/wl_init.c b/src/wl_init.c index 49e785cc..b28e0e15 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -382,7 +382,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform) // Init _glfwInitWayland, _glfwTerminateWayland, - _glfwGetSystemDefaultThemeWayland + _glfwGetSystemDefaultThemeWayland, // Input _glfwGetCursorPosWayland, _glfwSetCursorPosWayland, diff --git a/src/x11_init.c b/src/x11_init.c index 2806eadb..21645b94 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1172,7 +1172,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform) // Init _glfwInitX11, _glfwTerminateX11, - _glfwGetSystemDefaultThemeX11 + _glfwGetSystemDefaultThemeX11, // Input _glfwGetCursorPosX11, _glfwSetCursorPosX11, From 3e7ce3ac6359f065a56bc8ded4c1700a71234919 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Sun, 29 Jan 2023 21:06:42 +0100 Subject: [PATCH 15/31] Convert GLFWtheme to an opaque object. Add creation, copying and destruction functions for theming objects. Add setters and getters for theme objects. --- include/GLFW/glfw3.h | 35 +++++++++++++----- src/CMakeLists.txt | 4 +- src/cocoa_init.m | 27 ++++++++------ src/cocoa_platform.h | 8 ++-- src/cocoa_window.m | 12 +++--- src/init.c | 6 ++- src/input.c | 4 +- src/internal.h | 10 ++--- src/null_init.c | 4 +- src/null_platform.h | 6 +-- src/null_window.c | 4 +- src/theme.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ src/win32_init.c | 2 +- src/win32_platform.h | 6 +-- src/win32_window.c | 4 +- src/window.c | 4 +- src/wl_init.c | 2 +- src/wl_platform.h | 6 +-- src/wl_window.c | 4 +- src/x11_init.c | 2 +- src/x11_platform.h | 6 +-- src/x11_window.c | 4 +- tests/theming.c | 71 +++++++++++++++++++---------------- 23 files changed, 219 insertions(+), 100 deletions(-) create mode 100644 src/theme.c diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 8129d2fa..ca8e1e87 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -5845,16 +5845,31 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); -typedef struct GLFWtheme -{ - int baseTheme; // light/dark - int flags; - unsigned char color[4]; -} GLFWtheme; +typedef struct GLFWtheme GLFWtheme; -#define GLFW_BASE_THEME_DEFAULT 0 -#define GLFW_BASE_THEME_LIGHT 1 -#define GLFW_BASE_THEME_DARK 2 +typedef struct _GLFWtheme +{ + int variation; // light/dark + int flags; + unsigned char color[4]; // TODO: change to 128 bit (4 floats) to support wider gamuts. +} _GLFWtheme; + +GLFWAPI GLFWtheme* glfwCreateTheme(void); +GLFWAPI void glfwDestroyTheme(GLFWtheme* theme); +GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target); + +GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme); +GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value); + +GLFWAPI int glfwThemeGetFlags(const GLFWtheme* theme); +GLFWAPI void glfwThemeSetFlags(GLFWtheme* theme, int value); + +GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha); +GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha); + +#define GLFW_THEME_DARK -1 +#define GLFW_THEME_DEFAULT 0 +#define GLFW_THEME_LIGHT 1 #define GLFW_THEME_FLAG_HAS_COLOR 1 #define GLFW_THEME_FLAG_HIGH_CONTRAST 2 @@ -5872,7 +5887,7 @@ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback); * @param[in] window The window to set the theme for. * @param[in] theme The theme to set. Pass `NULL` to set it to the system default. */ -GLFWAPI void glfwSetTheme(GLFWwindow* handle, GLFWtheme* theme); +GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme); GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle); GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 01f191c9..09427835 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,7 @@ add_library(glfw ${GLFW_LIBRARY_TYPE} "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" internal.h platform.h mappings.h - context.c init.c input.c monitor.c platform.c vulkan.c window.c + context.c init.c input.c monitor.c platform.c theme.c vulkan.c window.c egl_context.c osmesa_context.c null_platform.h null_joystick.h null_init.c null_monitor.c null_window.c null_joystick.c) @@ -248,7 +248,7 @@ endif() # Make GCC warn about declarations that VS 2010 and 2012 won't accept for all # source files that VS will build (Clang ignores this because we set -std=c99) if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - set_source_files_properties(context.c init.c input.c monitor.c platform.c vulkan.c + set_source_files_properties(context.c init.c input.c monitor.c platform.c theme.c vulkan.c window.c null_init.c null_joystick.c null_monitor.c null_window.c win32_init.c win32_joystick.c win32_module.c win32_monitor.c win32_time.c win32_thread.c win32_window.c diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 9570d4c2..5c8b0e82 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -175,7 +175,7 @@ static void createMenuBar(void) [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; } -void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) +void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) { NSAppearanceName name; @@ -197,20 +197,20 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) if ([name isEqualToString:NSAppearanceNameAqua]) { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->variation = GLFW_THEME_LIGHT; return; } if (@available(macOS 10.10, *)) { if ([name isEqualToString:NSAppearanceNameVibrantLight]) { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->variation = GLFW_THEME_LIGHT; theme->flags |= GLFW_THEME_FLAG_VIBRANT; return; } if ([name isEqualToString:NSAppearanceNameVibrantDark]) { - theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->variation = GLFW_THEME_DARK; theme->flags |= GLFW_THEME_FLAG_VIBRANT; return; } @@ -220,36 +220,36 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme) { if ([name isEqualToString:NSAppearanceNameDarkAqua]) { - theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->variation = GLFW_THEME_DARK; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]) { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->variation = GLFW_THEME_LIGHT; theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]) { - theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->variation = GLFW_THEME_DARK; theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight]) { - theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->variation = GLFW_THEME_LIGHT; theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]) { - theme->baseTheme = GLFW_BASE_THEME_DARK; + theme->variation = GLFW_THEME_DARK; theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; return; } } - theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->variation = GLFW_THEME_LIGHT; } // Create key code translation tables @@ -491,9 +491,12 @@ static GLFWbool initializeTIS(void) // TODO: FIXME: this method is invoked twice when the high contrast setting is edited in the preferences. - GLFWtheme theme = { GLFW_BASE_THEME_LIGHT, 0 }; + _GLFWtheme theme = { GLFW_THEME_LIGHT, 0 }; if (@available(macOS 10.14, *)) { + // effectiveAppearance is actually not the system appearance, but the application appearance. + // As long as NSApplication.appearance is never set, using the effective appearance is fine + // to get and observe the system appearance. nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; @@ -842,7 +845,7 @@ void _glfwTerminateCocoa(void) } // autoreleasepool } -GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) +_GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 84a0ace0..6e6f9054 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -297,7 +297,7 @@ void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor); float _glfwTransformYCocoa(float y); -void nsAppearanceToGLFWTheme(NSAppearance* appearance, GLFWtheme* theme); +void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme); void* _glfwLoadLocalVulkanLoaderCocoa(void); @@ -309,6 +309,6 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, void _glfwDestroyContextNSGL(_GLFWwindow* window); -GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void); -void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme); -GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window); +_GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void); +void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 6ba54501..b99a9e85 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1876,9 +1876,9 @@ const char* _glfwGetClipboardStringCocoa(void) } // autoreleasepool } -void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) +void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme) { - if (!theme || theme->baseTheme == GLFW_BASE_THEME_DEFAULT) + if (!theme || theme->variation == GLFW_THEME_DEFAULT) { [(NSWindow*)window->ns.object setAppearance:nil]; return; @@ -1896,7 +1896,7 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) // appearanceNamed: will result in nil, so these can not be used. NSAppearanceName name; - if (theme->baseTheme == GLFW_BASE_THEME_LIGHT) + if (theme->variation == GLFW_THEME_LIGHT) { if (theme->flags & GLFW_THEME_FLAG_VIBRANT) { @@ -1926,11 +1926,11 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, GLFWtheme* theme) [(NSWindow*)window->ns.object setAppearance:appearance]; } -GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) { - GLFWtheme* theme = &window->theme; + _GLFWtheme* theme = &window->theme; - theme->baseTheme = GLFW_BASE_THEME_LIGHT; + theme->variation = GLFW_THEME_LIGHT; theme->flags = 0; if (@available(macOS 10.09, *)) diff --git a/src/init.c b/src/init.c index 41107ee8..69d391c1 100644 --- a/src/init.c +++ b/src/init.c @@ -546,7 +546,11 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfw.platform.getSystemDefaultTheme(); + + _GLFWtheme* theme = _glfw.platform.getSystemDefaultTheme(); + assert(theme->variation != GLFW_THEME_DEFAULT); + + return (GLFWtheme*) theme; } GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) diff --git a/src/input.c b/src/input.c index dc2ab137..03fc41cb 100644 --- a/src/input.c +++ b/src/input.c @@ -477,12 +477,12 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) js->hats[hat] = value; } -void _glfwInputSystemTheme(GLFWtheme* theme) +void _glfwInputSystemTheme(_GLFWtheme* theme) { assert(theme != NULL); if (_glfw.callbacks.theme) - _glfw.callbacks.theme(theme); + _glfw.callbacks.theme((GLFWtheme*) theme); } ////////////////////////////////////////////////////////////////////////// diff --git a/src/internal.h b/src/internal.h index 5d3ec9a6..9795fc6b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -571,7 +571,7 @@ struct _GLFWwindow GLFWdropfun drop; } callbacks; - GLFWtheme theme; + _GLFWtheme theme; // TODO: This data is mutable by clients, so remove it. // This is defined in platform.h GLFW_PLATFORM_WINDOW_STATE @@ -675,7 +675,7 @@ struct _GLFWplatform // Init GLFWbool (*init)(void); void (*terminate)(void); - GLFWtheme* (*getSystemDefaultTheme)(void); + _GLFWtheme* (*getSystemDefaultTheme)(void); // Input void (*getCursorPos)(_GLFWwindow*,double*,double*); void (*setCursorPos)(_GLFWwindow*,double,double); @@ -738,8 +738,8 @@ struct _GLFWplatform void (*setWindowFloating)(_GLFWwindow*,GLFWbool); void (*setWindowOpacity)(_GLFWwindow*,float); void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool); - GLFWtheme* (*getTheme)(_GLFWwindow*); - void (*setTheme)(_GLFWwindow*,GLFWtheme*); + _GLFWtheme* (*getTheme)(_GLFWwindow*); + void (*setTheme)(_GLFWwindow*,_GLFWtheme*); // Events void (*pollEvents)(void); void (*waitEvents)(void); @@ -936,7 +936,7 @@ void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); -void _glfwInputSystemTheme(GLFWtheme* theme); +void _glfwInputSystemTheme(_GLFWtheme* theme); void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); diff --git a/src/null_init.c b/src/null_init.c index f42f70b4..15738873 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -140,7 +140,7 @@ void _glfwTerminateNull(void) _glfwTerminateEGL(); } -GLFWtheme* _glfwGetSystemDefaultThemeNull(void) +_GLFWtheme* _glfwGetSystemDefaultThemeNull(void) { - return NULL; // TODO: should probably return a colorless GLFWtheme with baseTheme set to GLFW_BASE_THEME_LIGHT + return NULL; // TODO: should probably return a colorless GLFWtheme with baseTheme set to GLFW_THEME_LIGHT } diff --git a/src/null_platform.h b/src/null_platform.h index 44a61cad..b5abe2b0 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -147,6 +147,6 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window, void _glfwPollMonitorsNull(void); -GLFWtheme* _glfwGetSystemDefaultThemeNull(void); -void _glfwSetThemeNull(_GLFWwindow* window, GLFWtheme* theme); -GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window); +_GLFWtheme* _glfwGetSystemDefaultThemeNull(void); +void _glfwSetThemeNull(_GLFWwindow* window, _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window); diff --git a/src/null_window.c b/src/null_window.c index 2804fa59..c0d3264a 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -551,11 +551,11 @@ const char* _glfwGetClipboardStringNull(void) return _glfw.null.clipboardString; } -void _glfwSetThemeNull(_GLFWwindow* window, GLFWtheme* theme) +void _glfwSetThemeNull(_GLFWwindow* window, _GLFWtheme* theme) { } -GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window) { return NULL; // TODO: see to-do in _glfwGetSystemDefaultThemeNull } diff --git a/src/theme.c b/src/theme.c new file mode 100644 index 00000000..bcb119a0 --- /dev/null +++ b/src/theme.c @@ -0,0 +1,88 @@ +// +// theme.c +// glfw +// +// Created by Andreas Ormevik Jansen on 28/01/2023. +// + +#include "internal.h" + +#include +#include +#include +#include +#include + + +GLFWAPI GLFWtheme* glfwCreateTheme(void) +{ + _GLFWtheme* theme = _glfw_calloc(1, sizeof(_GLFWtheme)); + + theme->variation = GLFW_THEME_DEFAULT; + theme->flags = 0; + + return (GLFWtheme*) theme; +} + +GLFWAPI void glfwDestroyTheme(GLFWtheme* theme) +{ + _glfw_free((_GLFWtheme*) theme); +} + +GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target) +{ + memcpy(target, source, sizeof(_GLFWtheme)); +} + +GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme) +{ + assert(theme != NULL); + return ((_GLFWtheme*) theme)->variation; +} + +GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value) +{ + assert(theme != NULL); + assert(value == GLFW_THEME_DARK || value == GLFW_THEME_DEFAULT || value == GLFW_THEME_LIGHT); + + ((_GLFWtheme*) theme)->variation = value; +} + +GLFWAPI int glfwThemeGetFlags(const GLFWtheme* theme) +{ + assert(theme != NULL); + return ((_GLFWtheme*) theme)->flags; +} + +GLFWAPI void glfwThemeSetFlags(GLFWtheme* theme, int value) +{ + assert(theme != NULL); + assert((value & ~(GLFW_THEME_FLAG_HAS_COLOR | GLFW_THEME_FLAG_HIGH_CONTRAST | GLFW_THEME_FLAG_VIBRANT)) == 0); + + ((_GLFWtheme*) theme)->flags = value; +} + +GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha) +{ + assert(theme != NULL); + assert(red != NULL && green != NULL && blue != NULL && alpha != NULL); + + const _GLFWtheme* iTheme = ((_GLFWtheme*) theme); + + *red = iTheme->color[0] / 255.0f; + *green = iTheme->color[1] / 255.0f; + *blue = iTheme->color[2] / 255.0f; + *alpha = iTheme->color[3] / 255.0f; +} + +GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha) +{ + assert(theme != NULL); + + _GLFWtheme* iTheme = ((_GLFWtheme*) theme); + + iTheme->color[0] = red * 255.0f; + iTheme->color[1] = green * 255.0f; + iTheme->color[2] = blue * 255.0f; + iTheme->color[3] = alpha * 255.0f; +} diff --git a/src/win32_init.c b/src/win32_init.c index 2ee73b23..3bba1f65 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -737,7 +737,7 @@ void _glfwTerminateWin32(void) freeLibraries(); } -GLFWtheme* _glfwGetSystemDefaultThemeWin32(void) +_GLFWtheme* _glfwGetSystemDefaultThemeWin32(void) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/win32_platform.h b/src/win32_platform.h index ca46cb16..fd7624f0 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -622,6 +622,6 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); -GLFWtheme* _glfwGetSystemDefaultThemeWin32(void); -void _glfwSetThemeWin32(_GLFWwindow* window, GLFWtheme* theme); -GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window); +_GLFWtheme* _glfwGetSystemDefaultThemeWin32(void); +void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window); diff --git a/src/win32_window.c b/src/win32_window.c index 53a11a14..0a73f36b 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2373,12 +2373,12 @@ const char* _glfwGetClipboardStringWin32(void) return _glfw.win32.clipboardString; } -void _glfwSetThemeWin32(_GLFWwindow* window, GLFWtheme* theme) +void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); } -GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/window.c b/src/window.c index e380d3fb..12bd4a30 100644 --- a/src/window.c +++ b/src/window.c @@ -1160,7 +1160,7 @@ GLFWAPI void glfwSetTheme(GLFWwindow* handle, GLFWtheme* theme) assert(window != NULL); _GLFW_REQUIRE_INIT(); - _glfw.platform.setTheme(window, theme); + _glfw.platform.setTheme(window, (_GLFWtheme*) theme); } GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle) @@ -1169,5 +1169,5 @@ GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle) assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return _glfw.platform.getTheme(window); + return (GLFWtheme*) _glfw.platform.getTheme(window); } diff --git a/src/wl_init.c b/src/wl_init.c index b28e0e15..0efa0007 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -803,7 +803,7 @@ void _glfwTerminateWayland(void) _glfw_free(_glfw.wl.clipboardString); } -GLFWtheme* _glfwGetSystemDefaultThemeWayland(void) +_GLFWtheme* _glfwGetSystemDefaultThemeWayland(void) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/wl_platform.h b/src/wl_platform.h index af357cac..bf19f16a 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -519,6 +519,6 @@ void _glfwUpdateContentScaleWayland(_GLFWwindow* window); void _glfwAddSeatListenerWayland(struct wl_seat* seat); void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); -GLFWtheme* _glfwGetSystemDefaultThemeWayland(void); -void _glfwSetThemeWayland(_GLFWwindow* window, GLFWtheme* theme); -GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window); +_GLFWtheme* _glfwGetSystemDefaultThemeWayland(void); +void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window); diff --git a/src/wl_window.c b/src/wl_window.c index 48a60b52..ed490155 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -2775,12 +2775,12 @@ const char* _glfwGetClipboardStringWayland(void) return _glfw.wl.clipboardString; } -void _glfwSetThemeWayland(_GLFWwindow* window, GLFWtheme* theme) +void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); } -GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/x11_init.c b/src/x11_init.c index 21645b94..582624b9 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -1664,7 +1664,7 @@ void _glfwTerminateX11(void) } } -GLFWtheme* _glfwGetSystemDefaultThemeX11(void) +_GLFWtheme* _glfwGetSystemDefaultThemeX11(void) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/x11_platform.h b/src/x11_platform.h index e0214699..5cff4c7a 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -1002,6 +1002,6 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); -GLFWtheme* _glfwGetSystemDefaultThemeX11(void); -void _glfwSetThemeX11(_GLFWwindow* window, GLFWtheme* theme); -GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window); +_GLFWtheme* _glfwGetSystemDefaultThemeX11(void); +void _glfwSetThemeX11(_GLFWwindow* window, _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window); diff --git a/src/x11_window.c b/src/x11_window.c index f3adc490..675f5014 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3082,12 +3082,12 @@ const char* _glfwGetClipboardStringX11(void) return getSelectionString(_glfw.x11.CLIPBOARD); } -void _glfwSetThemeX11(_GLFWwindow* window, GLFWtheme* theme) +void _glfwSetThemeX11(_GLFWwindow* window, _GLFWtheme* theme) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); } -GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/tests/theming.c b/tests/theming.c index c74425c8..21bab47b 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -36,75 +36,84 @@ #include #include -const unsigned char theme_colors[6][4] = +const float theme_colors[6][4] = { - { 0, 0, 0, 255 }, // black - { 255, 0, 0, 255 }, // red - { 0, 255, 0, 255 }, // green - { 0, 0, 255, 255 }, // blue - { 255, 255, 255, 255 }, // white - { 128, 128, 128, 255 } // gray (no theme color) + { 0, 0, 0, 1.0 }, // black + { 1.0, 0, 0, 1.0 }, // red + { 0, 1.0, 0, 1.0 }, // green + { 0, 0, 1.0, 1.0 }, // blue + { 1.0, 1.0, 1.0, 1.0 }, // white + { 0.5, 0.5, 0.5, 1.0 } // gray (no theme color) }; static int cur_theme_color = 0; -static GLFWtheme theme; +static GLFWtheme* theme; static void print_theme(GLFWtheme* theme, const char* title) { + const int flags = glfwThemeGetFlags(theme); int n = 0; printf("%s: {\n", title); - printf(" Base: %s\n", theme->baseTheme == GLFW_BASE_THEME_LIGHT ? "light" : "dark"); + printf(" Base: %s\n", glfwThemeGetVariation(theme) == GLFW_THEME_LIGHT ? "light" : "dark"); printf(" Flags: ["); - if (theme->flags & GLFW_THEME_FLAG_HAS_COLOR) + if (flags & GLFW_THEME_FLAG_HAS_COLOR) { printf(n++ > 0 ? ", %s" : "%s", "HAS_COLOR"); } - if (theme->flags & GLFW_THEME_FLAG_VIBRANT) + if (flags & GLFW_THEME_FLAG_VIBRANT) { printf(n++ > 0 ? ", %s" : "%s", "VIBRANT"); } - if (theme->flags & GLFW_THEME_FLAG_HIGH_CONTRAST) + if (flags & GLFW_THEME_FLAG_HIGH_CONTRAST) { printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST"); } printf("]\n"); - if (theme->flags & GLFW_THEME_FLAG_HAS_COLOR) + if (flags & GLFW_THEME_FLAG_HAS_COLOR) { - printf(" Color: [%i, %i, %i, %i]\n", theme->color[0], theme->color[1], theme->color[2], theme->color[3]); + float r, g, b, a; + glfwThemeGetColor(theme, &r, &g, &b, &a); + printf(" Color: [%f, %f, %f, %f]\n", r, g, b, a); } printf("}\n"); } static void set_theme(GLFWwindow* window, int theme_color) { - memcpy(theme.color, theme_colors[theme_color], 4); + glfwThemeSetColor( + theme, + theme_colors[theme_color][0], + theme_colors[theme_color][1], + theme_colors[theme_color][2], + theme_colors[theme_color][3] + ); if (theme_color == 6) { - theme.flags &= ~GLFW_THEME_FLAG_HAS_COLOR; + glfwThemeSetFlags(theme, glfwThemeGetFlags(theme) & ~GLFW_THEME_FLAG_HAS_COLOR); } else { - theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; + glfwThemeSetFlags(theme, glfwThemeGetFlags(theme) | GLFW_THEME_FLAG_HAS_COLOR); } const char* title; - switch (theme.baseTheme) { - case GLFW_BASE_THEME_DEFAULT: + switch (glfwThemeGetVariation(theme)) { + case GLFW_THEME_DEFAULT: title = "Default theme"; break; - case GLFW_BASE_THEME_LIGHT: + case GLFW_THEME_LIGHT: title = "Light theme"; break; - case GLFW_BASE_THEME_DARK: + case GLFW_THEME_DARK: title = "Dark theme"; break; } glfwSetWindowTitle(window, title); - glfwSetTheme(window, &theme); + glfwSetTheme(window, theme); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) @@ -119,15 +128,15 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, glfwSetTheme(window, NULL); break; case GLFW_KEY_1: - theme.baseTheme = GLFW_BASE_THEME_DEFAULT; + glfwThemeSetVariation(theme, GLFW_THEME_DEFAULT); set_theme(window, cur_theme_color); break; case GLFW_KEY_2: - theme.baseTheme = GLFW_BASE_THEME_LIGHT; + glfwThemeSetVariation(theme, GLFW_THEME_LIGHT); set_theme(window, cur_theme_color); break; case GLFW_KEY_3: - theme.baseTheme = GLFW_BASE_THEME_DARK; + glfwThemeSetVariation(theme, GLFW_THEME_DARK); set_theme(window, cur_theme_color); break; case GLFW_KEY_ESCAPE: @@ -172,12 +181,11 @@ int main(int argc, char** argv) glfwSetKeyCallback(window, key_callback); - theme.baseTheme = GLFW_BASE_THEME_DEFAULT; - theme.flags = 0; - theme.color[0] = 0; - theme.color[1] = 0; - theme.color[2] = 0; - theme.color[3] = 0; + theme = glfwCreateTheme(); + + glfwThemeSetVariation(theme, GLFW_THEME_DEFAULT); + glfwThemeSetFlags(theme, 0); + glfwThemeSetColor(theme, 0, 0, 0, 0); set_theme(window, cur_theme_color); glfwSetSystemThemeCallback(theme_callback); @@ -195,6 +203,7 @@ int main(int argc, char** argv) glfwWaitEvents(); } + glfwDestroyTheme(theme); glfwDestroyWindow(window); glfwTerminate(); exit(EXIT_SUCCESS); From bcd5cf6f8e9fb211c85a0a3290effe28e691f531 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Sun, 29 Jan 2023 21:18:48 +0100 Subject: [PATCH 16/31] Fix glfwSetTheme conflicting declaration --- src/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/window.c b/src/window.c index 12bd4a30..dda7993e 100644 --- a/src/window.c +++ b/src/window.c @@ -1153,7 +1153,7 @@ GLFWAPI void glfwPostEmptyEvent(void) _glfw.platform.postEmptyEvent(); } -GLFWAPI void glfwSetTheme(GLFWwindow* handle, GLFWtheme* theme) +GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme) { _GLFWwindow* window = (_GLFWwindow*) handle; From a63905b2f67c9edfb7bd8c8eefc4e247fe3cec5d Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Sun, 29 Jan 2023 22:57:30 +0100 Subject: [PATCH 17/31] Fix clear color in theming test --- tests/theming.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/theming.c b/tests/theming.c index 21bab47b..6c311950 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -193,10 +193,10 @@ int main(int argc, char** argv) while (!glfwWindowShouldClose(window)) { glClearColor( - theme_colors[cur_theme_color][0] / 255.0f, - theme_colors[cur_theme_color][1] / 255.0f, - theme_colors[cur_theme_color][2] / 255.0f, - theme_colors[cur_theme_color][3] / 255.0f + theme_colors[cur_theme_color][0], + theme_colors[cur_theme_color][1], + theme_colors[cur_theme_color][2], + theme_colors[cur_theme_color][3] ); glClear(GL_COLOR_BUFFER_BIT); glfwSwapBuffers(window); From b5758af15579f0d20fa4235f85fcb2971010d38f Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Mon, 30 Jan 2023 00:02:44 +0100 Subject: [PATCH 18/31] Add limited inline documentation. Change _GLFWtheme's color components to floats. --- include/GLFW/glfw3.h | 186 +++++++++++++++++++++++++++++++++++++++---- src/cocoa_init.m | 8 +- src/cocoa_window.m | 8 +- src/init.c | 2 +- src/internal.h | 18 ++++- src/theme.c | 43 +++++++--- 6 files changed, 228 insertions(+), 37 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index ca8e1e87..75ba656e 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1394,6 +1394,18 @@ typedef struct GLFWwindow GLFWwindow; */ typedef struct GLFWcursor GLFWcursor; +/*! @brief Opaque theme object. + * + * Opaque theme object. + * + * @see @ref theme_object + * + * @since Added in version 3.4. + * + * @ingroup theme + */ +typedef struct GLFWtheme GLFWtheme; + /*! @brief The function pointer type for memory allocation callbacks. * * This is the function pointer type for memory allocation callbacks. A memory @@ -1964,6 +1976,28 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); */ typedef void (* GLFWjoystickfun)(int jid, int event); +/*! @brief The function pointer type for system theme callbacks. + * + * This is the function pointer type for system theme callbacks. A system + * theme callback function has the following signature: + * @code + * void function_name(GLFWtheme* theme) + * @endcode + * + * @param[in] theme The new system theme. + * + * @pointer_lifetime The theme is valid until the callback + * function returns. + * + * @sa @ref theme + * @sa @ref glfwSetSystemThemeCallback + * + * @since Added in version 3.4. + * + * @ingroup theme + */ +typedef void (* GLFWthemefun)(GLFWtheme* theme); + /*! @brief Video mode type. * * This describes a single video mode. @@ -5845,15 +5879,6 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); -typedef struct GLFWtheme GLFWtheme; - -typedef struct _GLFWtheme -{ - int variation; // light/dark - int flags; - unsigned char color[4]; // TODO: change to 128 bit (4 floats) to support wider gamuts. -} _GLFWtheme; - GLFWAPI GLFWtheme* glfwCreateTheme(void); GLFWAPI void glfwDestroyTheme(GLFWtheme* theme); GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target); @@ -5867,28 +5892,161 @@ GLFWAPI void glfwThemeSetFlags(GLFWtheme* theme, int value); GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha); GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha); +/*! @brief Theme variation type. + * + * Specifies that a theme is of a dark variation. + * + * @ingroup theme + */ #define GLFW_THEME_DARK -1 + +/*! @brief Theme variation type. + * + * Specifies that a theme has no specific variation. + * This is an invalid value for themes returned by GLFW, but this + * variation can be set to make GLFW use the default system theme when + * applying a user-specified theme. + * + * @ingroup theme + */ #define GLFW_THEME_DEFAULT 0 + +/*! @brief Theme variation type. + * + * Specifies that a theme is of a light variation. + * + * @ingroup theme + */ #define GLFW_THEME_LIGHT 1 + +/*! @brief Theme flag. + * + * Specifies that a theme provides a color. If this flag is set on a theme + * returned by GLFW, this theme contains a color. If this flag is set on a + * theme supplied to GLFW, GLFW applies this theme if the platform supports it. + * If the flag is unset, the theme's color is ignored. + * + * @ingroup theme + */ #define GLFW_THEME_FLAG_HAS_COLOR 1 + +/*! @brief Theme flag. + * + * Specifies that a theme uses a high contrast mode. + * + * @ingroup theme + */ #define GLFW_THEME_FLAG_HIGH_CONTRAST 2 + +/*! @brief Theme flag. + * + * Specifies that a theme is vibrant. + * + * @ingroup theme + */ #define GLFW_THEME_FLAG_VIBRANT 4 -typedef void (* GLFWthemefun)(GLFWtheme* theme); - -/*! @brief Notifies the application when the system default theme changes. +/*! @brief Sets the system theme callback. * + * This function sets the system theme callback, or removes the + * currently set callback. This is called when the system theme changes, + * either because the user edited it, or the system automatically changed it. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name(GLFWtheme* theme) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWthemefun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref theme_event + * + * @since Added in version 3.4. + * + * @ingroup theme */ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback); /*! @brief Sets the theme for a window. * - * @param[in] window The window to set the theme for. - * @param[in] theme The theme to set. Pass `NULL` to set it to the system default. + * @param[in] window The [window](@ref window) to set the theme for. + * @param[in] theme The [theme](@ref theme) to set. Pass `NULL` to set it to the system default. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, + * and @ref GLFW_FEATURE_UNIMPLEMENTED. + * + * @pointer_lifetime The returned theme is allocated and freed by GLFW. You + * should not free it yourself. It is valid until this function or @ref glfwGetTheme + * is called again for the specified window, or the window is destroyed. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref theme + * @sa @ref glfwGetTheme + * + * @since Added in version 3.4. + * + * @ingroup theme */ GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme); + +/*! @brief Returns the currently active system theme. + * + * @param[in] window The [window](@ref window) to retrieve the current theme for. + * + * @return A mutable [theme](@ref theme) object, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, + * and @ref GLFW_FEATURE_UNIMPLEMENTED. + * + * @pointer_lifetime The returned theme is allocated and freed by GLFW. You + * should not free it yourself. It is valid until this function or @ref glfwSetTheme + * is called again for the specified window, or the window is destroyed. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref theme + * @sa @ref glfwSetTheme + * + * @since Added in version 3.4. + * + * @ingroup theme + */ GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle); + +/*! @brief Returns the currently active system theme. + * + * @return A mutable [theme](@ref theme) object, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, + * and @ref GLFW_FEATURE_UNIMPLEMENTED. + * + * @pointer_lifetime The returned theme is allocated and freed by GLFW. You + * should not free it yourself. It is valid until this function is called + * again, or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref theme + * @sa @ref glfwSetSystemThemeCallback + * @sa @ref glfwSetTheme + * + * @since Added in version 3.4. + * + * @ingroup theme + */ GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme(); /*! @brief Returns the GLFW time. diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 5c8b0e82..d075a0e1 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -502,10 +502,10 @@ static GLFWbool initializeTIS(void) NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; - theme.color[0] = color.redComponent * 255; - theme.color[1] = color.greenComponent * 255; - theme.color[2] = color.blueComponent * 255; - theme.color[3] = color.alphaComponent * 255; + theme.color[0] = color.redComponent; + theme.color[1] = color.greenComponent; + theme.color[2] = color.blueComponent; + theme.color[3] = color.alphaComponent; } _glfwInputSystemTheme(&theme); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index b99a9e85..acb23f94 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1944,10 +1944,10 @@ _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; - theme->color[0] = color.redComponent * 255; - theme->color[1] = color.greenComponent * 255; - theme->color[2] = color.blueComponent * 255; - theme->color[3] = color.alphaComponent * 255; + theme->color[0] = color.redComponent; + theme->color[1] = color.greenComponent; + theme->color[2] = color.blueComponent; + theme->color[3] = color.alphaComponent; } return theme; diff --git a/src/init.c b/src/init.c index 69d391c1..6cd16a76 100644 --- a/src/init.c +++ b/src/init.c @@ -548,7 +548,7 @@ GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFWtheme* theme = _glfw.platform.getSystemDefaultTheme(); - assert(theme->variation != GLFW_THEME_DEFAULT); + assert(theme->variation != GLFW_THEME_DEFAULT); return (GLFWtheme*) theme; } diff --git a/src/internal.h b/src/internal.h index 9795fc6b..50f4674d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -72,6 +72,7 @@ typedef struct _GLFWplatform _GLFWplatform; typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; +typedef struct _GLFWtheme _GLFWtheme; typedef struct _GLFWmapelement _GLFWmapelement; typedef struct _GLFWmapping _GLFWmapping; typedef struct _GLFWjoystick _GLFWjoystick; @@ -515,6 +516,20 @@ struct _GLFWcontext GLFW_PLATFORM_CONTEXT_STATE }; +// Theme structure +// +struct _GLFWtheme +{ + // The light/dark variation. If set to DEFAULT, use the system theme variation. + int variation; + + int flags; + + // When applying a theme, ignore this color if the HAS_COLOR + // flag is unset, and use the system theme color instead. + float color[4]; +}; + // Window and context structure // struct _GLFWwindow @@ -571,7 +586,8 @@ struct _GLFWwindow GLFWdropfun drop; } callbacks; - _GLFWtheme theme; // TODO: This data is mutable by clients, so remove it. + // Clients can mutate this theme data at any time + _GLFWtheme theme; // This is defined in platform.h GLFW_PLATFORM_WINDOW_STATE diff --git a/src/theme.c b/src/theme.c index bcb119a0..b27d6bb3 100644 --- a/src/theme.c +++ b/src/theme.c @@ -1,9 +1,26 @@ +//======================================================================== +// GLFW 3.4 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2023 Andreas O. Jansen // -// theme.c -// glfw +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. // -// Created by Andreas Ormevik Jansen on 28/01/2023. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: // +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. #include "internal.h" @@ -67,22 +84,22 @@ GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, assert(theme != NULL); assert(red != NULL && green != NULL && blue != NULL && alpha != NULL); - const _GLFWtheme* iTheme = ((_GLFWtheme*) theme); + const float* color = ((_GLFWtheme*) theme)->color; - *red = iTheme->color[0] / 255.0f; - *green = iTheme->color[1] / 255.0f; - *blue = iTheme->color[2] / 255.0f; - *alpha = iTheme->color[3] / 255.0f; + *red = color[0]; + *green = color[1]; + *blue = color[2]; + *alpha = color[3]; } GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha) { assert(theme != NULL); - _GLFWtheme* iTheme = ((_GLFWtheme*) theme); + float* color = ((_GLFWtheme*) theme)->color; - iTheme->color[0] = red * 255.0f; - iTheme->color[1] = green * 255.0f; - iTheme->color[2] = blue * 255.0f; - iTheme->color[3] = alpha * 255.0f; + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = alpha; } From d87fc99503594203596d24cabca4fcdfb2d32c3e Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Mon, 30 Jan 2023 01:12:12 +0100 Subject: [PATCH 19/31] Change GLFWtheme's public API flags to attributes --- include/GLFW/glfw3.h | 16 ++++++++-------- src/cocoa_init.m | 14 +++++++------- src/cocoa_window.m | 6 +++--- src/theme.c | 22 +++++++++++++++++----- tests/theming.c | 20 +++++++------------- 5 files changed, 42 insertions(+), 36 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 75ba656e..61f0a5f8 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -5886,8 +5886,8 @@ GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target); GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme); GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value); -GLFWAPI int glfwThemeGetFlags(const GLFWtheme* theme); -GLFWAPI void glfwThemeSetFlags(GLFWtheme* theme, int value); +GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute); +GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value); GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha); GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha); @@ -5920,7 +5920,7 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float b #define GLFW_THEME_LIGHT 1 -/*! @brief Theme flag. +/*! @brief Theme attribute. * * Specifies that a theme provides a color. If this flag is set on a theme * returned by GLFW, this theme contains a color. If this flag is set on a @@ -5929,23 +5929,23 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float b * * @ingroup theme */ -#define GLFW_THEME_FLAG_HAS_COLOR 1 +#define GLFW_THEME_ATTRIBUTE_HAS_COLOR 1 -/*! @brief Theme flag. +/*! @brief Theme attribute. * * Specifies that a theme uses a high contrast mode. * * @ingroup theme */ -#define GLFW_THEME_FLAG_HIGH_CONTRAST 2 +#define GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST 2 -/*! @brief Theme flag. +/*! @brief Theme attribute. * * Specifies that a theme is vibrant. * * @ingroup theme */ -#define GLFW_THEME_FLAG_VIBRANT 4 +#define GLFW_THEME_ATTRIBUTE_VIBRANT 4 /*! @brief Sets the system theme callback. * diff --git a/src/cocoa_init.m b/src/cocoa_init.m index d075a0e1..45708892 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -205,13 +205,13 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) if ([name isEqualToString:NSAppearanceNameVibrantLight]) { theme->variation = GLFW_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; + theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT; return; } if ([name isEqualToString:NSAppearanceNameVibrantDark]) { theme->variation = GLFW_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT; + theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT; return; } } @@ -226,25 +226,25 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastAqua]) { theme->variation = GLFW_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastDarkAqua]) { theme->variation = GLFW_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight]) { theme->variation = GLFW_THEME_LIGHT; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT | GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; return; } if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]) { theme->variation = GLFW_THEME_DARK; - theme->flags |= GLFW_THEME_FLAG_VIBRANT | GLFW_THEME_FLAG_HIGH_CONTRAST; + theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT | GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; return; } } @@ -501,7 +501,7 @@ static GLFWbool initializeTIS(void) NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; - theme.flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme.flags |= GLFW_THEME_ATTRIBUTE_HAS_COLOR; theme.color[0] = color.redComponent; theme.color[1] = color.greenComponent; theme.color[2] = color.blueComponent; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index acb23f94..bb5c465a 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1898,7 +1898,7 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme) if (theme->variation == GLFW_THEME_LIGHT) { - if (theme->flags & GLFW_THEME_FLAG_VIBRANT) + if (theme->flags & GLFW_THEME_ATTRIBUTE_VIBRANT) { name = NSAppearanceNameVibrantLight; } @@ -1909,7 +1909,7 @@ void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme) } else { - if (theme->flags & GLFW_THEME_FLAG_VIBRANT) + if (theme->flags & GLFW_THEME_ATTRIBUTE_VIBRANT) { name = NSAppearanceNameVibrantDark; } @@ -1943,7 +1943,7 @@ _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. - theme->flags |= GLFW_THEME_FLAG_HAS_COLOR; + theme->flags |= GLFW_THEME_ATTRIBUTE_HAS_COLOR; theme->color[0] = color.redComponent; theme->color[1] = color.greenComponent; theme->color[2] = color.blueComponent; diff --git a/src/theme.c b/src/theme.c index b27d6bb3..356124d9 100644 --- a/src/theme.c +++ b/src/theme.c @@ -65,18 +65,30 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value) ((_GLFWtheme*) theme)->variation = value; } -GLFWAPI int glfwThemeGetFlags(const GLFWtheme* theme) +GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute) { assert(theme != NULL); - return ((_GLFWtheme*) theme)->flags; + assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HAS_COLOR | + GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_VIBRANT)) == 0); + + return ((_GLFWtheme*) theme)->flags & attribute ? GLFW_TRUE : GLFW_FALSE; } -GLFWAPI void glfwThemeSetFlags(GLFWtheme* theme, int value) +GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value) { assert(theme != NULL); - assert((value & ~(GLFW_THEME_FLAG_HAS_COLOR | GLFW_THEME_FLAG_HIGH_CONTRAST | GLFW_THEME_FLAG_VIBRANT)) == 0); + assert(value == GLFW_TRUE || value == GLFW_FALSE); + assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HAS_COLOR | + GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_VIBRANT)) == 0); - ((_GLFWtheme*) theme)->flags = value; + _GLFWtheme* _theme = (_GLFWtheme*) theme; + + if (value == GLFW_TRUE) + _theme->flags |= attribute; + else + _theme->flags &= ~attribute; } GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha) diff --git a/tests/theming.c b/tests/theming.c index 6c311950..8d22e0b4 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -52,26 +52,25 @@ static GLFWtheme* theme; static void print_theme(GLFWtheme* theme, const char* title) { - const int flags = glfwThemeGetFlags(theme); int n = 0; printf("%s: {\n", title); printf(" Base: %s\n", glfwThemeGetVariation(theme) == GLFW_THEME_LIGHT ? "light" : "dark"); printf(" Flags: ["); - if (flags & GLFW_THEME_FLAG_HAS_COLOR) + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR)) { printf(n++ > 0 ? ", %s" : "%s", "HAS_COLOR"); } - if (flags & GLFW_THEME_FLAG_VIBRANT) + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_VIBRANT)) { printf(n++ > 0 ? ", %s" : "%s", "VIBRANT"); } - if (flags & GLFW_THEME_FLAG_HIGH_CONTRAST) + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST)) { printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST"); } printf("]\n"); - if (flags & GLFW_THEME_FLAG_HAS_COLOR) + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR)) { float r, g, b, a; glfwThemeGetColor(theme, &r, &g, &b, &a); @@ -91,12 +90,9 @@ static void set_theme(GLFWwindow* window, int theme_color) ); if (theme_color == 6) - { - glfwThemeSetFlags(theme, glfwThemeGetFlags(theme) & ~GLFW_THEME_FLAG_HAS_COLOR); - } else - { - glfwThemeSetFlags(theme, glfwThemeGetFlags(theme) | GLFW_THEME_FLAG_HAS_COLOR); - } + glfwThemeSetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR, GLFW_FALSE); + else + glfwThemeSetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR, GLFW_TRUE); const char* title; @@ -183,8 +179,6 @@ int main(int argc, char** argv) theme = glfwCreateTheme(); - glfwThemeSetVariation(theme, GLFW_THEME_DEFAULT); - glfwThemeSetFlags(theme, 0); glfwThemeSetColor(theme, 0, 0, 0, 0); set_theme(window, cur_theme_color); From afae2b0dfa6ca59fa8e24b86edaff0cf4fc57253 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Mon, 30 Jan 2023 03:09:39 +0100 Subject: [PATCH 20/31] Implement _glfwGetSystemDefaultThemeCocoa. Fix bug in _glfwGetThemeCocoa always reporting a light theme for system default themes. --- src/cocoa_init.m | 59 +++++++++++++++++++++++++++------------------- src/cocoa_window.m | 7 +++++- src/internal.h | 3 +++ tests/theming.c | 4 ++-- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/cocoa_init.m b/src/cocoa_init.m index 45708892..e92dcefe 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -252,6 +252,28 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) theme->variation = GLFW_THEME_LIGHT; } +static void getSystemTheme(_GLFWtheme* theme) +{ + theme->variation = GLFW_THEME_LIGHT; + theme->flags = 0; + + if (@available(macOS 10.14, *)) + { + // effectiveAppearance is actually not the system appearance, but the application appearance. + // As long as NSApplication.appearance is never set, using the effective appearance is fine + // to get and observe the system appearance. + nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, theme); + + NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; + + theme->flags |= GLFW_THEME_ATTRIBUTE_HAS_COLOR; + theme->color[0] = color.redComponent; + theme->color[1] = color.greenComponent; + theme->color[2] = color.blueComponent; + theme->color[3] = color.alphaComponent; + } +} + // Create key code translation tables // static void createKeyTables(void) @@ -491,22 +513,8 @@ static GLFWbool initializeTIS(void) // TODO: FIXME: this method is invoked twice when the high contrast setting is edited in the preferences. - _GLFWtheme theme = { GLFW_THEME_LIGHT, 0 }; - - if (@available(macOS 10.14, *)) { - // effectiveAppearance is actually not the system appearance, but the application appearance. - // As long as NSApplication.appearance is never set, using the effective appearance is fine - // to get and observe the system appearance. - nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, &theme); - - NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; - - theme.flags |= GLFW_THEME_ATTRIBUTE_HAS_COLOR; - theme.color[0] = color.redComponent; - theme.color[1] = color.greenComponent; - theme.color[2] = color.blueComponent; - theme.color[3] = color.alphaComponent; - } + _GLFWtheme theme; + getSystemTheme(&theme); _glfwInputSystemTheme(&theme); @@ -778,12 +786,13 @@ int _glfwInitCocoa(void) context:nil]; */ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 - [NSApp addObserver:_glfw.ns.helper - forKeyPath:@"effectiveAppearance" - options:0 - context:nil]; -#endif + if (@available(macOS 10.14, *)) + { + [NSApp addObserver:_glfw.ns.helper + forKeyPath:@"effectiveAppearance" + options:0 + context:nil]; + } if (![[NSRunningApplication currentApplication] isFinishedLaunching]) [NSApp run]; @@ -847,8 +856,10 @@ void _glfwTerminateCocoa(void) _GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + _GLFWtheme* theme = &_glfw.theme; + getSystemTheme(theme); + + return theme; } #endif // _GLFW_COCOA diff --git a/src/cocoa_window.m b/src/cocoa_window.m index bb5c465a..a8fa387a 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1935,7 +1935,12 @@ _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) if (@available(macOS 10.09, *)) { - nsAppearanceToGLFWTheme([(NSWindow*)window->ns.object appearance], theme); + NSAppearance* appearance = [(NSWindow*)window->ns.object appearance]; + + if (appearance == NULL) + appearance = [NSApp effectiveAppearance]; + + nsAppearanceToGLFWTheme(appearance, theme); } if (@available(macOS 10.14, *)) { diff --git a/src/internal.h b/src/internal.h index 50f4674d..d6d76851 100644 --- a/src/internal.h +++ b/src/internal.h @@ -887,6 +887,9 @@ struct _GLFWlibrary GLFWjoystickfun joystick; GLFWthemefun theme; } callbacks; + + // Clients can mutate this theme data at any time + _GLFWtheme theme; // These are defined in platform.h GLFW_PLATFORM_LIBRARY_WINDOW_STATE diff --git a/tests/theming.c b/tests/theming.c index 8d22e0b4..41bfdf35 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -162,6 +162,8 @@ int main(int argc, char** argv) fprintf(stderr, "Failed to initialize GLFW\n"); exit(EXIT_FAILURE); } + + print_theme(glfwGetSystemDefaultTheme(), "System theme"); window = glfwCreateWindow(200, 200, "Window Icon", NULL, NULL); if (!window) @@ -178,8 +180,6 @@ int main(int argc, char** argv) glfwSetKeyCallback(window, key_callback); theme = glfwCreateTheme(); - - glfwThemeSetColor(theme, 0, 0, 0, 0); set_theme(window, cur_theme_color); glfwSetSystemThemeCallback(theme_callback); From 5805c51eab1378e750ecc0fb3cefab4d6497ffb7 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Wed, 1 Feb 2023 04:44:22 +0100 Subject: [PATCH 21/31] Add color specifier to GLFWtheme. Add color specifier parameter to glfwThemeGetColor and glfwThemeSetColor. Add GLFW_THEME_COLOR_MAIN. Remove GLFW_THEME_ATTRIBUTE_HAS_COLOR. --- include/GLFW/glfw3.h | 36 ++++++++++++++++++++---------------- src/cocoa_init.m | 2 +- src/cocoa_window.m | 2 +- src/theme.c | 18 ++++++++++-------- tests/theming.c | 19 ++++++++++--------- 5 files changed, 42 insertions(+), 35 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 61f0a5f8..12ea8d3c 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -5889,8 +5889,13 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value); GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute); GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value); -GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha); -GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha); +GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, + int specifier, + float* red, float* green, float* blue, float* alpha); + +GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, + int specifier, + float red, float green, float blue, float alpha); /*! @brief Theme variation type. * @@ -5919,25 +5924,13 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float b */ #define GLFW_THEME_LIGHT 1 - -/*! @brief Theme attribute. - * - * Specifies that a theme provides a color. If this flag is set on a theme - * returned by GLFW, this theme contains a color. If this flag is set on a - * theme supplied to GLFW, GLFW applies this theme if the platform supports it. - * If the flag is unset, the theme's color is ignored. - * - * @ingroup theme - */ -#define GLFW_THEME_ATTRIBUTE_HAS_COLOR 1 - /*! @brief Theme attribute. * * Specifies that a theme uses a high contrast mode. * * @ingroup theme */ -#define GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST 2 +#define GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST 1 /*! @brief Theme attribute. * @@ -5945,7 +5938,18 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float b * * @ingroup theme */ -#define GLFW_THEME_ATTRIBUTE_VIBRANT 4 +#define GLFW_THEME_ATTRIBUTE_VIBRANT 2 + +/*! @brief Theme color attribute. + * + * This is both an attribute and color specifier. As an attribute, it specifies + * if this color is present in the theme or not. If this attribute is set on a theme + * returned by GLFW, that theme contains this color. If this attribute is set on a + * theme supplied to GLFW, GLFW applies this color if the platform supports it. + * + * @ingroup theme + */ +#define GLFW_THEME_COLOR_MAIN 4 /*! @brief Sets the system theme callback. * diff --git a/src/cocoa_init.m b/src/cocoa_init.m index e92dcefe..a0d9ed08 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -266,7 +266,7 @@ static void getSystemTheme(_GLFWtheme* theme) NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; - theme->flags |= GLFW_THEME_ATTRIBUTE_HAS_COLOR; + theme->flags |= GLFW_THEME_COLOR_MAIN; theme->color[0] = color.redComponent; theme->color[1] = color.greenComponent; theme->color[2] = color.blueComponent; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index a8fa387a..4ee5e648 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1948,7 +1948,7 @@ _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. - theme->flags |= GLFW_THEME_ATTRIBUTE_HAS_COLOR; + theme->flags |= GLFW_THEME_COLOR_MAIN; theme->color[0] = color.redComponent; theme->color[1] = color.greenComponent; theme->color[2] = color.blueComponent; diff --git a/src/theme.c b/src/theme.c index 356124d9..1aa4be12 100644 --- a/src/theme.c +++ b/src/theme.c @@ -68,9 +68,9 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value) GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute) { assert(theme != NULL); - assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HAS_COLOR | - GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | - GLFW_THEME_ATTRIBUTE_VIBRANT)) == 0); + assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_VIBRANT | + GLFW_THEME_COLOR_MAIN)) == 0); return ((_GLFWtheme*) theme)->flags & attribute ? GLFW_TRUE : GLFW_FALSE; } @@ -79,9 +79,9 @@ GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value) { assert(theme != NULL); assert(value == GLFW_TRUE || value == GLFW_FALSE); - assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HAS_COLOR | - GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | - GLFW_THEME_ATTRIBUTE_VIBRANT)) == 0); + assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_VIBRANT | + GLFW_THEME_COLOR_MAIN)) == 0); _GLFWtheme* _theme = (_GLFWtheme*) theme; @@ -91,9 +91,10 @@ GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value) _theme->flags &= ~attribute; } -GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, float* blue, float* alpha) +GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red, float* green, float* blue, float* alpha) { assert(theme != NULL); + assert(specifier == GLFW_THEME_COLOR_MAIN); assert(red != NULL && green != NULL && blue != NULL && alpha != NULL); const float* color = ((_GLFWtheme*) theme)->color; @@ -104,9 +105,10 @@ GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, float* red, float* green, *alpha = color[3]; } -GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, float red, float green, float blue, float alpha) +GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, int specifier, float red, float green, float blue, float alpha) { assert(theme != NULL); + assert(specifier == GLFW_THEME_COLOR_MAIN); float* color = ((_GLFWtheme*) theme)->color; diff --git a/tests/theming.c b/tests/theming.c index 41bfdf35..323dc03d 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -55,11 +55,11 @@ static void print_theme(GLFWtheme* theme, const char* title) int n = 0; printf("%s: {\n", title); - printf(" Base: %s\n", glfwThemeGetVariation(theme) == GLFW_THEME_LIGHT ? "light" : "dark"); - printf(" Flags: ["); - if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR)) + printf(" Variation: %s\n", glfwThemeGetVariation(theme) == GLFW_THEME_LIGHT ? "light" : "dark"); + printf(" Attributes: ["); + if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN)) { - printf(n++ > 0 ? ", %s" : "%s", "HAS_COLOR"); + printf(n++ > 0 ? ", %s" : "%s", "COLOR_MAIN"); } if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_VIBRANT)) { @@ -70,11 +70,11 @@ static void print_theme(GLFWtheme* theme, const char* title) printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST"); } printf("]\n"); - if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR)) + if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN)) { float r, g, b, a; - glfwThemeGetColor(theme, &r, &g, &b, &a); - printf(" Color: [%f, %f, %f, %f]\n", r, g, b, a); + glfwThemeGetColor(theme, GLFW_THEME_COLOR_MAIN, &r, &g, &b, &a); + printf(" Main color: [%f, %f, %f, %f]\n", r, g, b, a); } printf("}\n"); } @@ -83,6 +83,7 @@ static void set_theme(GLFWwindow* window, int theme_color) { glfwThemeSetColor( theme, + GLFW_THEME_COLOR_MAIN, theme_colors[theme_color][0], theme_colors[theme_color][1], theme_colors[theme_color][2], @@ -90,9 +91,9 @@ static void set_theme(GLFWwindow* window, int theme_color) ); if (theme_color == 6) - glfwThemeSetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR, GLFW_FALSE); + glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_FALSE); else - glfwThemeSetAttribute(theme, GLFW_THEME_ATTRIBUTE_HAS_COLOR, GLFW_TRUE); + glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_TRUE); const char* title; From 9889f26b0f4675e042767ad23b19425ddeffc36b Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Sat, 4 Feb 2023 17:17:05 +0100 Subject: [PATCH 22/31] Use C89 var declaration in glfwGetSystemDefaultTheme, init.c --- src/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/init.c b/src/init.c index 6cd16a76..20e9bd19 100644 --- a/src/init.c +++ b/src/init.c @@ -545,9 +545,10 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() { + _GLFWtheme* theme; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - _GLFWtheme* theme = _glfw.platform.getSystemDefaultTheme(); + theme = _glfw.platform.getSystemDefaultTheme(); assert(theme->variation != GLFW_THEME_DEFAULT); return (GLFWtheme*) theme; From 43ea8967a36af464e22a6a0178ad29b63d5bbf18 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sat, 11 Feb 2023 10:56:43 +0800 Subject: [PATCH 23/31] Add win32 theming implementation --- src/win32_init.c | 29 +++++++++++++++++++++--- src/win32_platform.h | 21 +++++++++++++++++- src/win32_window.c | 52 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index 3bba1f65..e110af85 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -151,6 +151,8 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); + _glfw.win32.dwmapi.SetWindowAttribute = (PFN_DwmSetWindowAttribute) + _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmSetWindowAttribute"); } _glfw.win32.shcore.instance = _glfwPlatformLoadModule("shcore.dll"); @@ -169,6 +171,18 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } + _glfw.win32.uxtheme.instance = _glfwPlatformLoadModule("uxtheme.dll"); + if (_glfw.win32.uxtheme.instance) + { + _glfw.win32.uxtheme.ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(132)); + _glfw.win32.uxtheme.GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(95)); + _glfw.win32.uxtheme.GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(96)); + _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)_glfwPlatformGetModuleSymbol(_glfw.win32.uxtheme.instance, MAKEINTRESOURCEA(98)); + + _glfw.win32.uxtheme.uxThemeAvailable = _glfw.win32.uxtheme.ShouldAppsUseDarkMode && _glfw.win32.uxtheme.GetImmersiveColorFromColorSetEx && _glfw.win32.uxtheme.GetImmersiveColorTypeFromName && _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference; + _glfw.win32.uxtheme.darkTitleAvailable = _glfwIsWindows10BuildOrGreaterWin32(22000); + } + return GLFW_TRUE; } @@ -739,9 +753,18 @@ void _glfwTerminateWin32(void) _GLFWtheme* _glfwGetSystemDefaultThemeWin32(void) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + _GLFWtheme* theme = &_glfw.theme; + + theme->variation = GLFW_THEME_LIGHT; + theme->flags = 0; + + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + if (_glfw.win32.uxtheme.ShouldAppsUseDarkMode() & 0x1) { + theme->variation = GLFW_THEME_DARK; + } + } + + return theme; } #endif // _GLFW_WIN32 - diff --git a/src/win32_platform.h b/src/win32_platform.h index fd7624f0..b2851273 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -299,12 +299,15 @@ typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT); // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); -typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); +typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); +typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND,DWORD,LPCVOID,DWORD); + #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow #define DwmGetColorizationColor _glfw.win32.dwmapi.GetColorizationColor +#define DwmSetWindowAttribute _glfw.win32.dwmapi.SetWindowAttribute // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); @@ -366,6 +369,11 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl; #define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl; +typedef BOOL (WINAPI * ShouldAppsUseDarkModePtr)(); +typedef DWORD(WINAPI * GetImmersiveColorFromColorSetExPtr)(UINT,UINT,BOOL,UINT); +typedef int (WINAPI * GetImmersiveColorTypeFromNamePtr)(const WCHAR*); +typedef int (WINAPI * GetImmersiveUserColorSetPreferencePtr)(BOOL,BOOL); + // WGL-specific per-context data // @@ -487,6 +495,7 @@ typedef struct _GLFWlibraryWin32 PFN_DwmFlush Flush; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; PFN_DwmGetColorizationColor GetColorizationColor; + PFN_DwmSetWindowAttribute SetWindowAttribute; } dwmapi; struct { @@ -499,6 +508,16 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; + + struct { + HINSTANCE instance; + GLFWbool uxThemeAvailable; + GLFWbool darkTitleAvailable; + ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode; + GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx; + GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName; + GetImmersiveUserColorSetPreferencePtr GetImmersiveUserColorSetPreference; + } uxtheme; } _GLFWlibraryWin32; // Win32-specific per-monitor data diff --git a/src/win32_window.c b/src/win32_window.c index 0a73f36b..819dbc90 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -37,6 +37,21 @@ #include #include +// Ref: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +static void applySystemTheme(HWND handle) { + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + GLFWbool value = _glfw.win32.uxtheme.ShouldAppsUseDarkMode() & 0x1; + DwmSetWindowAttribute(handle, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &value, + sizeof(value)); + } +} + // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -1146,6 +1161,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l return 0; } + case WM_THEMECHANGED: + case WM_SETTINGCHANGE: { + if (window->theme.variation == GLFW_THEME_DEFAULT) { + applySystemTheme(window->win32.handle); + } + } break; + case WM_GETDPISCALEDSIZE: { if (window->win32.scaleToMonitor) @@ -1436,6 +1458,10 @@ static int createNativeWindow(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); + if (window->theme.variation == GLFW_THEME_DEFAULT) { + applySystemTheme(window->win32.handle); + } + return GLFW_TRUE; } @@ -2375,13 +2401,32 @@ const char* _glfwGetClipboardStringWin32(void) void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + if (!theme || theme->variation == GLFW_THEME_DEFAULT) + { + applySystemTheme(window->win32.handle); + return; + } + + GLFWbool darkMode = theme->variation == GLFW_THEME_DARK; + + DwmSetWindowAttribute(window->win32.handle, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &darkMode, + sizeof(darkMode)); } _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + _GLFWtheme* theme = &window->theme; + + theme->variation = GLFW_THEME_LIGHT; + theme->flags = 0; + + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + theme->variation = GLFW_THEME_DARK; + } + + return theme; } EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs) @@ -2512,4 +2557,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) } #endif // _GLFW_WIN32 - From adc9f9d67641aad4403c7d427a31d5dc72967ee5 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sun, 19 Feb 2023 22:23:35 +0800 Subject: [PATCH 24/31] Free uxtheme properly --- src/win32_init.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/win32_init.c b/src/win32_init.c index e110af85..2a91bd83 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -207,6 +207,9 @@ static void freeLibraries(void) if (_glfw.win32.ntdll.instance) _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); + + if (_glfw.win32.uxtheme.instance) + _glfwPlatformFreeModule(_glfw.win32.uxtheme.instance); } // Create key code translation tables From c6b0894c577a49b0f9f958cb76f034fda2a4e33f Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Sun, 19 Feb 2023 22:29:53 +0800 Subject: [PATCH 25/31] Code format --- src/win32_platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/win32_platform.h b/src/win32_platform.h index b2851273..f60c70e4 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -370,7 +370,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)( #define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl; typedef BOOL (WINAPI * ShouldAppsUseDarkModePtr)(); -typedef DWORD(WINAPI * GetImmersiveColorFromColorSetExPtr)(UINT,UINT,BOOL,UINT); +typedef DWORD (WINAPI * GetImmersiveColorFromColorSetExPtr)(UINT,UINT,BOOL,UINT); typedef int (WINAPI * GetImmersiveColorTypeFromNamePtr)(const WCHAR*); typedef int (WINAPI * GetImmersiveUserColorSetPreferencePtr)(BOOL,BOOL); From 14d3920f6a7b199e751cd1bad57499d8e3a96473 Mon Sep 17 00:00:00 2001 From: floppyhammer Date: Wed, 22 Feb 2023 14:31:48 +0800 Subject: [PATCH 26/31] Add a function to get the system accent color --- src/win32_window.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 819dbc90..fcc822c3 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -42,8 +42,12 @@ #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 #endif -static void applySystemTheme(HWND handle) { - if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { +// Apply the system default theme +// +static void applySystemTheme(HWND handle) +{ + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) + { GLFWbool value = _glfw.win32.uxtheme.ShouldAppsUseDarkMode() & 0x1; DwmSetWindowAttribute(handle, DWMWA_USE_IMMERSIVE_DARK_MODE, @@ -52,6 +56,27 @@ static void applySystemTheme(HWND handle) { } } +static void getAccentColor(float color[4]) +{ + if (!_glfw.win32.uxtheme.uxThemeAvailable) + { + return; + } + + UINT dwImmersiveColorType = _glfw.win32.uxtheme.GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"); + UINT dwImmersiveColorSet = _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference(FALSE, FALSE); + + UINT rgba = _glfw.win32.uxtheme.GetImmersiveColorFromColorSetEx(dwImmersiveColorSet, + dwImmersiveColorType, + FALSE, + 0); + + color[0] = (0xFF & rgba); + color[1] = ((0xFF00 & rgba) >> 8) ; + color[2] = ((0xFF0000 & rgba) >> 16); + color[3] = ((0xFF000000 & rgba) >> 24); +} + // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -1458,9 +1483,8 @@ static int createNativeWindow(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); - if (window->theme.variation == GLFW_THEME_DEFAULT) { - applySystemTheme(window->win32.handle); - } + // Use the system default when creating a window + applySystemTheme(window->win32.handle); return GLFW_TRUE; } @@ -2422,10 +2446,14 @@ _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) theme->variation = GLFW_THEME_LIGHT; theme->flags = 0; - if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) { + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) + { theme->variation = GLFW_THEME_DARK; } + memset(theme->color, 0, sizeof(float) * 4); + getAccentColor(theme->color); + return theme; } From 8ba39bca81ee62a076f43d535772d2f04066646c Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 23 Feb 2023 19:30:12 +0100 Subject: [PATCH 27/31] Add credit --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 015e4ce3..6c0cd91b 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -33,6 +33,7 @@ video tutorials. - David Carlier - Arturo Castro - Chi-kwan Chan + - Haoyun Chen - TheChocolateOre - Ali Chraghi - Joseph Chua From decb018a31d4bf15baa25541a3655cbdab391f7c Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Thu, 23 Feb 2023 22:48:21 +0100 Subject: [PATCH 28/31] Update theming API --- include/GLFW/glfw3.h | 39 ++++++++++-- src/cocoa_init.m | 70 ++++++++++++++-------- src/cocoa_platform.h | 8 +-- src/cocoa_window.m | 137 ++++++++++++++++++++++++++----------------- src/init.c | 4 +- src/internal.h | 15 +++-- src/null_init.c | 7 ++- src/null_platform.h | 4 +- src/null_window.c | 9 ++- src/theme.c | 56 +++++++++++++----- src/win32_platform.h | 4 +- src/win32_window.c | 51 ++++++++++------ src/window.c | 6 +- src/wl_platform.h | 4 +- src/wl_window.c | 2 +- src/x11_platform.h | 4 +- src/x11_window.c | 4 +- tests/theming.c | 70 +++++++++++++++++----- 18 files changed, 335 insertions(+), 159 deletions(-) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 12ea8d3c..137bdc8a 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -5879,9 +5879,12 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); +// TODO: consider requiring GLFW to be initialized for these GLFWTheme functions to work. That way, implementations can add extra data to the themes, and process them in a platform-specific way. + GLFWAPI GLFWtheme* glfwCreateTheme(void); GLFWAPI void glfwDestroyTheme(GLFWtheme* theme); GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target); +GLFWAPI int glfwThemeEqual(const GLFWtheme* first, const GLFWtheme* second); GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme); GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value); @@ -5889,10 +5892,12 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value); GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute); GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value); +// If the return value of glfwGetAttribute(specifier) is GLFW_FALSE, the return values of this function are undefined. GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red, float* green, float* blue, float* alpha); +// Must execute glfwThemeSetAttribute(specifier, GLFW_TRUE) for this to have an effect. GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, int specifier, float red, float green, float blue, float alpha); @@ -5934,11 +5939,19 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, /*! @brief Theme attribute. * - * Specifies that a theme is vibrant. + * Specifies that a theme requests reduced transparency. * * @ingroup theme */ -#define GLFW_THEME_ATTRIBUTE_VIBRANT 2 +#define GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY 2 + +/*! @brief Theme attribute. + * + * Specifies that a theme requests reduced motion. + * + * @ingroup theme + */ +#define GLFW_THEME_ATTRIBUTE_REDUCE_MOTION 4 /*! @brief Theme color attribute. * @@ -5949,7 +5962,7 @@ GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, * * @ingroup theme */ -#define GLFW_THEME_COLOR_MAIN 4 +#define GLFW_THEME_COLOR_MAIN 8 /*! @brief Sets the system theme callback. * @@ -6005,8 +6018,24 @@ GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback); GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme); /*! @brief Returns the currently active system theme. + * + * Executing this yields no changes to a window's theme settings: + * @code + * glfwSetTheme(window, glfwGetTheme(window, GLFW_FALSE)) + * @endcode + * + * Executing this sets a window's theme to the current system theme, and disables + * automatic changes to the window's theme for when the system changes its theme. + * @code + * glfwSetTheme(window, glfwGetTheme(window, GLFW_TRUE)) + * @endcode + * * * @param[in] window The [window](@ref window) to retrieve the current theme for. + * @param[in] inlineDefaults Specifies whether or not GLFW should replace unspecified + * theme attributes with the currently active system ones. If `GLFW_TRUE`, the returned + * theme describes the active style of the window. If `GLFW_FALSE`, the returned theme + * describes the window's theme settings. * * @return A mutable [theme](@ref theme) object, or `NULL` if an * [error](@ref error_handling) occurred. @@ -6027,7 +6056,7 @@ GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme); * * @ingroup theme */ -GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle); +GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle, int inlineDefaults); /*! @brief Returns the currently active system theme. * @@ -6051,7 +6080,7 @@ GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle); * * @ingroup theme */ -GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme(); +GLFWAPI const GLFWtheme* glfwGetSystemDefaultTheme(); /*! @brief Returns the GLFW time. * diff --git a/src/cocoa_init.m b/src/cocoa_init.m index a0d9ed08..a80d6a44 100644 --- a/src/cocoa_init.m +++ b/src/cocoa_init.m @@ -175,7 +175,7 @@ static void createMenuBar(void) [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; } -void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) +void _glfwNSAppearanceToTheme(NSAppearance* appearance, _GLFWtheme* theme) { NSAppearanceName name; @@ -188,10 +188,9 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) NSAppearanceNameVibrantDark, NSAppearanceNameAccessibilityHighContrastAqua, NSAppearanceNameAccessibilityHighContrastDarkAqua, - NSAppearanceNameAccessibilityHighContrastVibrantLight, - NSAppearanceNameAccessibilityHighContrastVibrantDark ]]; - } else { + } else + { name = appearance.name; } @@ -205,13 +204,11 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) if ([name isEqualToString:NSAppearanceNameVibrantLight]) { theme->variation = GLFW_THEME_LIGHT; - theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT; return; } if ([name isEqualToString:NSAppearanceNameVibrantDark]) { theme->variation = GLFW_THEME_DARK; - theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT; return; } } @@ -235,24 +232,12 @@ void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme) theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; return; } - if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantLight]) - { - theme->variation = GLFW_THEME_LIGHT; - theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT | GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; - return; - } - if ([name isEqualToString:NSAppearanceNameAccessibilityHighContrastVibrantDark]) - { - theme->variation = GLFW_THEME_DARK; - theme->flags |= GLFW_THEME_ATTRIBUTE_VIBRANT | GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; - return; - } } theme->variation = GLFW_THEME_LIGHT; } -static void getSystemTheme(_GLFWtheme* theme) +void _glfwGetSystemThemeCocoa(_GLFWtheme* theme) { theme->variation = GLFW_THEME_LIGHT; theme->flags = 0; @@ -262,7 +247,7 @@ static void getSystemTheme(_GLFWtheme* theme) // effectiveAppearance is actually not the system appearance, but the application appearance. // As long as NSApplication.appearance is never set, using the effective appearance is fine // to get and observe the system appearance. - nsAppearanceToGLFWTheme(NSApp.effectiveAppearance, theme); + _glfwNSAppearanceToTheme(NSApp.effectiveAppearance, theme); NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; @@ -272,6 +257,35 @@ static void getSystemTheme(_GLFWtheme* theme) theme->color[2] = color.blueComponent; theme->color[3] = color.alphaComponent; } + + // TODO: return the standard blue accent color if running in 10.13 or earlier. + + // TODO: optimize by reading multiple values at once. + + const CFStringRef applicationID = CFSTR("com.apple.universalaccess"); + + Boolean keyIsValid = false; + Boolean highContrast = CFPreferencesGetAppBooleanValue(CFSTR("increaseContrast"), + applicationID, + &keyIsValid); + + if (keyIsValid && highContrast) + theme->flags |= GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST; + + keyIsValid = false; + Boolean reduceTransparency = CFPreferencesGetAppBooleanValue(CFSTR("reduceTransparency"), + applicationID, + &keyIsValid); + if (keyIsValid && reduceTransparency) + theme->flags |= GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY; + + keyIsValid = false; + Boolean reduceMotion = CFPreferencesGetAppBooleanValue(CFSTR("reduceMotion"), + applicationID, + &keyIsValid); + + if (keyIsValid && reduceMotion) + theme->flags |= GLFW_THEME_ATTRIBUTE_REDUCE_MOTION; } // Create key code translation tables @@ -511,11 +525,16 @@ static GLFWbool initializeTIS(void) { // This class is never subclassed, so it's safe to ignore the context parameter - // TODO: FIXME: this method is invoked twice when the high contrast setting is edited in the preferences. - _GLFWtheme theme; - getSystemTheme(&theme); + _glfwGetSystemThemeCocoa(&theme); + // The observer for the effective appearance is invoked more often than the appearance name itself changes. + // For instance, it's invoked when various accesibility settings are changed in the system preferences. + // Not all of these properties are included in the GLFW themes, so those updates must be filtered out. + if (glfwThemeEqual((GLFWtheme*) &theme, (GLFWtheme*) &_glfw.theme)) + return; + + memcpy(&_glfw.theme, &theme, sizeof(_GLFWtheme)); _glfwInputSystemTheme(&theme); /*if ([keyPath isEqualToString:@"controlAccentColor"]) { @@ -785,7 +804,10 @@ int _glfwInitCocoa(void) options:0 context:nil]; */ + + _glfwInitDefaultTheme(&_glfw.theme); + // TODO: add observer for properties other than effectiveAppearance, for observing prior to 10.14. I assume the accesibility options were available then. if (@available(macOS 10.14, *)) { [NSApp addObserver:_glfw.ns.helper @@ -857,7 +879,7 @@ void _glfwTerminateCocoa(void) _GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void) { _GLFWtheme* theme = &_glfw.theme; - getSystemTheme(theme); + _glfwGetSystemThemeCocoa(theme); return theme; } diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 6e6f9054..18a4ea02 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -297,7 +297,8 @@ void _glfwRestoreVideoModeCocoa(_GLFWmonitor* monitor); float _glfwTransformYCocoa(float y); -void nsAppearanceToGLFWTheme(NSAppearance* appearance, _GLFWtheme* theme); +void _glfwNSAppearanceToTheme(NSAppearance* appearance, _GLFWtheme* theme); +void _glfwGetSystemThemeCocoa(_GLFWtheme* theme); void* _glfwLoadLocalVulkanLoaderCocoa(void); @@ -308,7 +309,6 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextNSGL(_GLFWwindow* window); - _GLFWtheme* _glfwGetSystemDefaultThemeCocoa(void); -void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme); -_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window); +void _glfwSetThemeCocoa(_GLFWwindow* window, const _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window, int inlineDefaults); diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 4ee5e648..b3ec38b6 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1876,59 +1876,90 @@ const char* _glfwGetClipboardStringCocoa(void) } // autoreleasepool } -void _glfwSetThemeCocoa(_GLFWwindow* window, _GLFWtheme* theme) +// TODO: move +static void replaceTheme(_GLFWwindow* window, _GLFWtheme* currentTheme, const _GLFWtheme* newTheme) { - if (!theme || theme->variation == GLFW_THEME_DEFAULT) + const int dissimilarFlags = currentTheme->flags ^ newTheme->flags; + + if (currentTheme->variation != newTheme->variation + || (dissimilarFlags & GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY)) { - [(NSWindow*)window->ns.object setAppearance:nil]; - return; - } - - if (@available(macOS 10.10, *)) {} else - { - return; - } - - // TODO: support color - // TODO: fix vibrancy - - // As per the Cocoa documentation, passing the high contrast names to - // appearanceNamed: will result in nil, so these can not be used. - NSAppearanceName name; - - if (theme->variation == GLFW_THEME_LIGHT) - { - if (theme->flags & GLFW_THEME_ATTRIBUTE_VIBRANT) + if (newTheme->variation == GLFW_THEME_DEFAULT) { - name = NSAppearanceNameVibrantLight; + [(NSWindow*)window->ns.object setAppearance:nil]; } else { - name = NSAppearanceNameAqua; + if (@available(macOS 10.10, *)) + { + // TODO: support color + // TODO: fix vibrancy + + // As per the Cocoa documentation, passing the high contrast names to + // appearanceNamed: will result in nil, so these can not be used. + NSAppearanceName name; + + if (newTheme->variation == GLFW_THEME_LIGHT) + { + if (newTheme->flags & GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY) + name = NSAppearanceNameVibrantLight; + else + name = NSAppearanceNameAqua; + } + else + { + if (newTheme->flags & GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY) + { + name = NSAppearanceNameVibrantDark; + } + else if (@available(macOS 10.14, *)) + { + name = NSAppearanceNameDarkAqua; + } + else + { + name = NSAppearanceNameAqua; + } + } + + NSAppearance* appearance = [NSAppearance appearanceNamed:name]; + [(NSWindow*)window->ns.object setAppearance:appearance]; + } } } - else - { - if (theme->flags & GLFW_THEME_ATTRIBUTE_VIBRANT) - { - name = NSAppearanceNameVibrantDark; - } - else if (@available(macOS 10.14, *)) - { - name = NSAppearanceNameDarkAqua; - } else - { - name = NSAppearanceNameAqua; - } - } - - NSAppearance* appearance = [NSAppearance appearanceNamed:name]; - [(NSWindow*)window->ns.object setAppearance:appearance]; } -_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) +void _glfwSetThemeCocoa(_GLFWwindow* window, const _GLFWtheme* theme) { - _GLFWtheme* theme = &window->theme; + _GLFWtheme* currentTheme = &window->theme.internal; + _GLFWtheme newTheme; + + if (!theme) + _glfwInitDefaultTheme(&newTheme); + else + memcpy(&newTheme, theme, sizeof(_GLFWtheme)); + + replaceTheme(window, currentTheme, &newTheme); + + memcpy(currentTheme, &newTheme, sizeof(_GLFWtheme)); + + // Not available for setting in Cocoa. + currentTheme->flags &= ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY | + GLFW_THEME_ATTRIBUTE_REDUCE_MOTION| + GLFW_THEME_COLOR_MAIN); + + // TODO: NSColor controlAccentColor is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. +} + +_GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window, int inlineDefaults) +{ + _GLFWtheme* theme = &window->theme.external; + memcpy(theme, &window->theme.internal, sizeof(_GLFWtheme)); + + // FIXME: fix not overriding specified properties. + if (!inlineDefaults) + return theme; theme->variation = GLFW_THEME_LIGHT; theme->flags = 0; @@ -1940,21 +1971,21 @@ _GLFWtheme* _glfwGetThemeCocoa(_GLFWwindow* window) if (appearance == NULL) appearance = [NSApp effectiveAppearance]; - nsAppearanceToGLFWTheme(appearance, theme); + _glfwNSAppearanceToTheme(appearance, theme); } - if (@available(macOS 10.14, *)) { - // TODO: this is not settable. Is there any reason in overriding a similar value? Does it apply to menu item highlights? If yes, then it must be overridden. - NSColor* color = [[NSColor controlAccentColor] colorUsingColorSpace:NSColorSpace.genericRGBColorSpace]; - // TODO: Cannot use the accent color directly, for window themes, because the accent color is never overridden. - - theme->flags |= GLFW_THEME_COLOR_MAIN; - theme->color[0] = color.redComponent; - theme->color[1] = color.greenComponent; - theme->color[2] = color.blueComponent; - theme->color[3] = color.alphaComponent; + _GLFWtheme systemTheme; + + _glfwGetSystemThemeCocoa(&systemTheme); + + if ((theme->flags & GLFW_THEME_COLOR_MAIN) == 0) + { + assert(systemTheme.flags & GLFW_THEME_COLOR_MAIN); + memcpy(&theme->color, &systemTheme.color, sizeof(float) * 4); } + theme->flags |= systemTheme.flags; + return theme; } diff --git a/src/init.c b/src/init.c index 20e9bd19..4bed25ac 100644 --- a/src/init.c +++ b/src/init.c @@ -543,7 +543,7 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) return cbfun; } -GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() +GLFWAPI const GLFWtheme* glfwGetSystemDefaultTheme() { _GLFWtheme* theme; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); @@ -551,7 +551,7 @@ GLFWAPI GLFWtheme* glfwGetSystemDefaultTheme() theme = _glfw.platform.getSystemDefaultTheme(); assert(theme->variation != GLFW_THEME_DEFAULT); - return (GLFWtheme*) theme; + return (const GLFWtheme*) theme; } GLFWAPI GLFWthemefun glfwSetSystemThemeCallback(GLFWthemefun callback) diff --git a/src/internal.h b/src/internal.h index d6d76851..92eff2eb 100644 --- a/src/internal.h +++ b/src/internal.h @@ -586,8 +586,13 @@ struct _GLFWwindow GLFWdropfun drop; } callbacks; - // Clients can mutate this theme data at any time - _GLFWtheme theme; + struct { + // Clients can mutate this theme data at any time + _GLFWtheme external; + + // The window's user-specified theme settings. + _GLFWtheme internal; // TODO: init to defaults on window creation. TODO: set in glfwSetTheme + } theme; // This is defined in platform.h GLFW_PLATFORM_WINDOW_STATE @@ -754,8 +759,8 @@ struct _GLFWplatform void (*setWindowFloating)(_GLFWwindow*,GLFWbool); void (*setWindowOpacity)(_GLFWwindow*,float); void (*setWindowMousePassthrough)(_GLFWwindow*,GLFWbool); - _GLFWtheme* (*getTheme)(_GLFWwindow*); - void (*setTheme)(_GLFWwindow*,_GLFWtheme*); + _GLFWtheme* (*getTheme)(_GLFWwindow*,int); + void (*setTheme)(_GLFWwindow*,const _GLFWtheme*); // Events void (*pollEvents)(void); void (*waitEvents)(void); @@ -888,7 +893,6 @@ struct _GLFWlibrary GLFWthemefun theme; } callbacks; - // Clients can mutate this theme data at any time _GLFWtheme theme; // These are defined in platform.h @@ -1035,3 +1039,4 @@ void* _glfw_calloc(size_t count, size_t size); void* _glfw_realloc(void* pointer, size_t size); void _glfw_free(void* pointer); +void _glfwInitDefaultTheme(_GLFWtheme* theme); diff --git a/src/null_init.c b/src/null_init.c index 15738873..f453e1b5 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -142,5 +142,10 @@ void _glfwTerminateNull(void) _GLFWtheme* _glfwGetSystemDefaultThemeNull(void) { - return NULL; // TODO: should probably return a colorless GLFWtheme with baseTheme set to GLFW_THEME_LIGHT + _glfwInitDefaultTheme(&_glfw.theme); + + // DEFAULT is not a valid return value. + _glfw.theme.variation = GLFW_THEME_LIGHT; + + return &_glfw.theme; } diff --git a/src/null_platform.h b/src/null_platform.h index b5abe2b0..41cc1b35 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -148,5 +148,5 @@ VkResult _glfwCreateWindowSurfaceNull(VkInstance instance, _GLFWwindow* window, void _glfwPollMonitorsNull(void); _GLFWtheme* _glfwGetSystemDefaultThemeNull(void); -void _glfwSetThemeNull(_GLFWwindow* window, _GLFWtheme* theme); -_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window); +void _glfwSetThemeNull(_GLFWwindow* window, const _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window, int inlineDefaults); diff --git a/src/null_window.c b/src/null_window.c index c0d3264a..1fc861b5 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -551,13 +551,16 @@ const char* _glfwGetClipboardStringNull(void) return _glfw.null.clipboardString; } -void _glfwSetThemeNull(_GLFWwindow* window, _GLFWtheme* theme) +void _glfwSetThemeNull(_GLFWwindow* window, const _GLFWtheme* theme) { + memcpy(&window->theme.internal, theme, sizeof(_GLFWtheme)); } -_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeNull(_GLFWwindow* window, int inlineDefaults) { - return NULL; // TODO: see to-do in _glfwGetSystemDefaultThemeNull + memcpy(&window->theme.external, &window->theme.internal, sizeof(_GLFWtheme)); + + return &window->theme.external; } EGLenum _glfwGetEGLPlatformNull(EGLint** attribs) diff --git a/src/theme.c b/src/theme.c index 1aa4be12..d790838d 100644 --- a/src/theme.c +++ b/src/theme.c @@ -31,12 +31,25 @@ #include +static GLFWbool attributesAreValid(int attributes) +{ + return (attributes & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY | + GLFW_THEME_ATTRIBUTE_REDUCE_MOTION | + GLFW_THEME_COLOR_MAIN)) == 0; +} + +void _glfwInitDefaultTheme(_GLFWtheme* theme) +{ + theme->variation = GLFW_THEME_DEFAULT; + theme->flags = 0; +} + + GLFWAPI GLFWtheme* glfwCreateTheme(void) { _GLFWtheme* theme = _glfw_calloc(1, sizeof(_GLFWtheme)); - - theme->variation = GLFW_THEME_DEFAULT; - theme->flags = 0; + _glfwInitDefaultTheme(theme); return (GLFWtheme*) theme; } @@ -51,6 +64,23 @@ GLFWAPI void glfwCopyTheme(const GLFWtheme* source, GLFWtheme* target) memcpy(target, source, sizeof(_GLFWtheme)); } +GLFWAPI int glfwThemeEqual(const GLFWtheme* first, const GLFWtheme* second) +{ + _GLFWtheme* _first = (_GLFWtheme*) first; + _GLFWtheme* _second = (_GLFWtheme*) second; + + if (_first->variation != _second->variation) + return GLFW_FALSE; + + if (_first->flags != _second->flags) + return GLFW_FALSE; + + if (_first->flags & GLFW_THEME_COLOR_MAIN) + return memcmp(&_first->color, &_second->color, sizeof(float) * 4); + + return GLFW_TRUE; +} + GLFWAPI int glfwThemeGetVariation(const GLFWtheme* theme) { assert(theme != NULL); @@ -68,22 +98,18 @@ GLFWAPI void glfwThemeSetVariation(GLFWtheme* theme, int value) GLFWAPI int glfwThemeGetAttribute(const GLFWtheme* theme, int attribute) { assert(theme != NULL); - assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | - GLFW_THEME_ATTRIBUTE_VIBRANT | - GLFW_THEME_COLOR_MAIN)) == 0); + assert(attributesAreValid(attribute)); return ((_GLFWtheme*) theme)->flags & attribute ? GLFW_TRUE : GLFW_FALSE; } GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value) { + _GLFWtheme* _theme = (_GLFWtheme*) theme; + assert(theme != NULL); assert(value == GLFW_TRUE || value == GLFW_FALSE); - assert((attribute & ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | - GLFW_THEME_ATTRIBUTE_VIBRANT | - GLFW_THEME_COLOR_MAIN)) == 0); - - _GLFWtheme* _theme = (_GLFWtheme*) theme; + assert(attributesAreValid(attribute)); if (value == GLFW_TRUE) _theme->flags |= attribute; @@ -93,12 +119,12 @@ GLFWAPI void glfwThemeSetAttribute(GLFWtheme* theme, int attribute, int value) GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red, float* green, float* blue, float* alpha) { + const float* color = ((_GLFWtheme*) theme)->color; + assert(theme != NULL); assert(specifier == GLFW_THEME_COLOR_MAIN); assert(red != NULL && green != NULL && blue != NULL && alpha != NULL); - const float* color = ((_GLFWtheme*) theme)->color; - *red = color[0]; *green = color[1]; *blue = color[2]; @@ -107,11 +133,11 @@ GLFWAPI void glfwThemeGetColor(const GLFWtheme* theme, int specifier, float* red GLFWAPI void glfwThemeSetColor(GLFWtheme* theme, int specifier, float red, float green, float blue, float alpha) { + float* color = ((_GLFWtheme*) theme)->color; + assert(theme != NULL); assert(specifier == GLFW_THEME_COLOR_MAIN); - float* color = ((_GLFWtheme*) theme)->color; - color[0] = red; color[1] = green; color[2] = blue; diff --git a/src/win32_platform.h b/src/win32_platform.h index f60c70e4..b5f048e2 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -642,5 +642,5 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWfbconfig* fbconfig); _GLFWtheme* _glfwGetSystemDefaultThemeWin32(void); -void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme); -_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window); +void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults); diff --git a/src/win32_window.c b/src/win32_window.c index fcc822c3..b1c8de10 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -56,12 +56,11 @@ static void applySystemTheme(HWND handle) } } -static void getAccentColor(float color[4]) + +static int getAccentColor(float color[4]) { if (!_glfw.win32.uxtheme.uxThemeAvailable) - { - return; - } + return GLFW_FALSE; UINT dwImmersiveColorType = _glfw.win32.uxtheme.GetImmersiveColorTypeFromName(L"ImmersiveSystemAccent"); UINT dwImmersiveColorSet = _glfw.win32.uxtheme.GetImmersiveUserColorSetPreference(FALSE, FALSE); @@ -71,10 +70,12 @@ static void getAccentColor(float color[4]) FALSE, 0); - color[0] = (0xFF & rgba); - color[1] = ((0xFF00 & rgba) >> 8) ; - color[2] = ((0xFF0000 & rgba) >> 16); - color[3] = ((0xFF000000 & rgba) >> 24); + color[0] = (double) (0xFF & rgba); + color[1] = (double) ((0xFF00 & rgba) >> 8); + color[2] = (double) ((0xFF0000 & rgba) >> 16); + color[3] = (double) ((0xFF000000 & rgba) >> 24); + + return GLFW_TRUE; } // Returns the window style for the specified window @@ -2423,7 +2424,7 @@ const char* _glfwGetClipboardStringWin32(void) return _glfw.win32.clipboardString; } -void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) +void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme) { if (!theme || theme->variation == GLFW_THEME_DEFAULT) { @@ -2437,22 +2438,34 @@ void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) DWMWA_USE_IMMERSIVE_DARK_MODE, &darkMode, sizeof(darkMode)); + + // TODO: set accent color } -_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults) { - _GLFWtheme* theme = &window->theme; + _GLFWtheme* theme = &window->theme.external; + memcpy(theme, &window->theme.internal, sizeof(_GLFWtheme)); + + // FIXME: fix not overriding specified properties. + if (!inlineDefaults) + return theme; - theme->variation = GLFW_THEME_LIGHT; - theme->flags = 0; - - if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) + if (theme->variation == GLFW_THEME_DEFAULT) { - theme->variation = GLFW_THEME_DARK; - } + theme->variation = GLFW_THEME_LIGHT; - memset(theme->color, 0, sizeof(float) * 4); - getAccentColor(theme->color); + if (_glfw.win32.uxtheme.uxThemeAvailable && _glfw.win32.uxtheme.darkTitleAvailable) + theme->variation = GLFW_THEME_DARK; + } + + if ((theme->flags & GLFW_THEME_COLOR_MAIN) == 0) + { + if (getAccentColor(&theme->color)) + theme->flags |= GLFW_THEME_COLOR_MAIN; + else + memset(&theme->color, 0, sizeof(float) * 4); + } return theme; } diff --git a/src/window.c b/src/window.c index dda7993e..174fc15f 100644 --- a/src/window.c +++ b/src/window.c @@ -244,6 +244,8 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, window->maxheight = GLFW_DONT_CARE; window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; + + _glfwInitDefaultTheme(&window->theme.internal); if (!_glfw.platform.createWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { @@ -1163,11 +1165,11 @@ GLFWAPI void glfwSetTheme(GLFWwindow* handle, const GLFWtheme* theme) _glfw.platform.setTheme(window, (_GLFWtheme*) theme); } -GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle) +GLFWAPI GLFWtheme* glfwGetTheme(GLFWwindow* handle, int inlineDefaults) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); - return (GLFWtheme*) _glfw.platform.getTheme(window); + return (GLFWtheme*) _glfw.platform.getTheme(window, inlineDefaults); } diff --git a/src/wl_platform.h b/src/wl_platform.h index bf19f16a..ad04b22e 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -520,5 +520,5 @@ void _glfwAddSeatListenerWayland(struct wl_seat* seat); void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device); _GLFWtheme* _glfwGetSystemDefaultThemeWayland(void); -void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme); -_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window); +void _glfwSetThemeWayland(_GLFWwindow* window, const _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window, int inlineDefaults); diff --git a/src/wl_window.c b/src/wl_window.c index ed490155..2b05af60 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -2780,7 +2780,7 @@ void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme) _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); } -_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window, int inlineDefaults) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/src/x11_platform.h b/src/x11_platform.h index 5cff4c7a..2a5aec12 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -1003,5 +1003,5 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, Visual** visual, int* depth); _GLFWtheme* _glfwGetSystemDefaultThemeX11(void); -void _glfwSetThemeX11(_GLFWwindow* window, _GLFWtheme* theme); -_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window); +void _glfwSetThemeX11(_GLFWwindow* window, const _GLFWtheme* theme); +_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window, int inlineDefaults); diff --git a/src/x11_window.c b/src/x11_window.c index 675f5014..9ee0f2ee 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3082,12 +3082,12 @@ const char* _glfwGetClipboardStringX11(void) return getSelectionString(_glfw.x11.CLIPBOARD); } -void _glfwSetThemeX11(_GLFWwindow* window, _GLFWtheme* theme) +void _glfwSetThemeX11(_GLFWwindow* window, const _GLFWtheme* theme) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); } -_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window) +_GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window, int inlineDefaults) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); return NULL; // TODO: implement diff --git a/tests/theming.c b/tests/theming.c index 323dc03d..2e588761 100644 --- a/tests/theming.c +++ b/tests/theming.c @@ -50,26 +50,29 @@ static int cur_theme_color = 0; static GLFWtheme* theme; -static void print_theme(GLFWtheme* theme, const char* title) +static void print_theme(const GLFWtheme* theme, const char* title) { int n = 0; + int variation = glfwThemeGetVariation(theme); printf("%s: {\n", title); - printf(" Variation: %s\n", glfwThemeGetVariation(theme) == GLFW_THEME_LIGHT ? "light" : "dark"); + printf(" Variation: %s\n", variation == GLFW_THEME_DEFAULT ? "default" : variation == GLFW_THEME_LIGHT ? "light" : "dark"); printf(" Attributes: ["); + if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN)) - { printf(n++ > 0 ? ", %s" : "%s", "COLOR_MAIN"); - } - if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_VIBRANT)) - { - printf(n++ > 0 ? ", %s" : "%s", "VIBRANT"); - } + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST)) - { printf(n++ > 0 ? ", %s" : "%s", "HIGH_CONTRAST"); - } + + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY)) + printf(n++ > 0 ? ", %s" : "%s", "REDUCE_TRANSPARENCY"); + + if (glfwThemeGetAttribute(theme, GLFW_THEME_ATTRIBUTE_REDUCE_MOTION)) + printf(n++ > 0 ? ", %s" : "%s", "REDUCE_MOTION"); + printf("]\n"); + if (glfwThemeGetAttribute(theme, GLFW_THEME_COLOR_MAIN)) { float r, g, b, a; @@ -90,7 +93,7 @@ static void set_theme(GLFWwindow* window, int theme_color) theme_colors[theme_color][3] ); - if (theme_color == 6) + if (theme_color == 5) glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_FALSE); else glfwThemeSetAttribute(theme, GLFW_THEME_COLOR_MAIN, GLFW_TRUE); @@ -113,6 +116,11 @@ static void set_theme(GLFWwindow* window, int theme_color) glfwSetTheme(window, theme); } +static void flip_attribute(int attribute) +{ + glfwThemeSetAttribute(theme, attribute, !glfwThemeGetAttribute(theme, attribute)); +} + static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (action != GLFW_PRESS) @@ -120,31 +128,59 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, switch (key) { + // No theme specified case GLFW_KEY_0: glfwSetWindowTitle(window, "Default theme (NULL)"); glfwSetTheme(window, NULL); break; + // Theme with default variation case GLFW_KEY_1: glfwThemeSetVariation(theme, GLFW_THEME_DEFAULT); set_theme(window, cur_theme_color); break; + // Theme with light variation case GLFW_KEY_2: glfwThemeSetVariation(theme, GLFW_THEME_LIGHT); set_theme(window, cur_theme_color); break; + // Theme with dark variation case GLFW_KEY_3: glfwThemeSetVariation(theme, GLFW_THEME_DARK); set_theme(window, cur_theme_color); break; - case GLFW_KEY_ESCAPE: - glfwSetWindowShouldClose(window, GLFW_TRUE); - break; + // Traverse colors case GLFW_KEY_SPACE: cur_theme_color = (cur_theme_color + 1) % 6; set_theme(window, cur_theme_color); break; + // Inspect + case GLFW_KEY_I: + print_theme(theme, "Constructed theme"); + break; + // Print case GLFW_KEY_P: - print_theme(glfwGetTheme(window), "Window theme"); + print_theme(glfwGetTheme(window, GLFW_FALSE), "Specified window theme"); + print_theme(glfwGetTheme(window, GLFW_TRUE), "Active window theme"); + printf("\n"); + break; + // High contrast + case GLFW_KEY_H: + flip_attribute(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST); + set_theme(window, cur_theme_color); + break; + // Transparency + case GLFW_KEY_T: + flip_attribute(GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY); + set_theme(window, cur_theme_color); + break; + // Motion + case GLFW_KEY_M: + flip_attribute(GLFW_THEME_ATTRIBUTE_REDUCE_MOTION); + set_theme(window, cur_theme_color); + break; + // Exit + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); break; } } @@ -174,6 +210,10 @@ int main(int argc, char** argv) fprintf(stderr, "Failed to open GLFW window\n"); exit(EXIT_FAILURE); } + + print_theme(glfwGetTheme(window, GLFW_FALSE), "Initial specified window theme"); + print_theme(glfwGetTheme(window, GLFW_TRUE), "Initial active window theme"); + printf("\n"); glfwMakeContextCurrent(window); gladLoadGL(glfwGetProcAddress); From ac979c6d7fde5d32ae039b26cc59b9cd42481ba5 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Fri, 24 Feb 2023 00:19:12 +0100 Subject: [PATCH 29/31] Fix win32 theming implementation --- src/win32_window.c | 48 ++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index b1c8de10..1a8c168b 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -70,10 +70,10 @@ static int getAccentColor(float color[4]) FALSE, 0); - color[0] = (double) (0xFF & rgba); - color[1] = (double) ((0xFF00 & rgba) >> 8); - color[2] = (double) ((0xFF0000 & rgba) >> 16); - color[3] = (double) ((0xFF000000 & rgba) >> 24); + color[0] = (float) (0xFF & rgba); + color[1] = (float) ((0xFF00 & rgba) >> 8); + color[2] = (float) ((0xFF0000 & rgba) >> 16); + color[3] = (float) ((0xFF000000 & rgba) >> 24); return GLFW_TRUE; } @@ -1189,7 +1189,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l case WM_THEMECHANGED: case WM_SETTINGCHANGE: { - if (window->theme.variation == GLFW_THEME_DEFAULT) { + if (window->theme.internal.variation == GLFW_THEME_DEFAULT) { applySystemTheme(window->win32.handle); } } break; @@ -2426,20 +2426,37 @@ const char* _glfwGetClipboardStringWin32(void) void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme) { - if (!theme || theme->variation == GLFW_THEME_DEFAULT) + _GLFWtheme* currentTheme = &window->theme.internal; + _GLFWtheme newTheme; + + if (!theme) + _glfwInitDefaultTheme(&newTheme); + else + memcpy(&newTheme, theme, sizeof(_GLFWtheme)); + + if (newTheme->variation == GLFW_THEME_DEFAULT) { applySystemTheme(window->win32.handle); - return; } - - GLFWbool darkMode = theme->variation == GLFW_THEME_DARK; - - DwmSetWindowAttribute(window->win32.handle, - DWMWA_USE_IMMERSIVE_DARK_MODE, - &darkMode, - sizeof(darkMode)); + else + { + GLFWbool darkMode = theme->variation == GLFW_THEME_DARK; + + DwmSetWindowAttribute(window->win32.handle, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &darkMode, + sizeof(darkMode)); + } // TODO: set accent color + + memcpy(currentTheme, &newTheme, sizeof(_GLFWtheme)); + + // Not available for setting in Win32. + currentTheme->flags &= ~(GLFW_THEME_ATTRIBUTE_HIGH_CONTRAST | + GLFW_THEME_ATTRIBUTE_REDUCE_TRANSPARENCY | + GLFW_THEME_ATTRIBUTE_REDUCE_MOTION| + GLFW_THEME_COLOR_MAIN); } _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults) @@ -2447,7 +2464,6 @@ _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults) _GLFWtheme* theme = &window->theme.external; memcpy(theme, &window->theme.internal, sizeof(_GLFWtheme)); - // FIXME: fix not overriding specified properties. if (!inlineDefaults) return theme; @@ -2461,7 +2477,7 @@ _GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults) if ((theme->flags & GLFW_THEME_COLOR_MAIN) == 0) { - if (getAccentColor(&theme->color)) + if (getAccentColor(theme->color)) theme->flags |= GLFW_THEME_COLOR_MAIN; else memset(&theme->color, 0, sizeof(float) * 4); From a30e29fa0a762849f09aa986cc51988a411d96b7 Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Fri, 24 Feb 2023 00:22:58 +0100 Subject: [PATCH 30/31] Fix compilation issues on X11, WL. --- src/null_window.c | 1 + src/wl_window.c | 3 ++- src/x11_window.c | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/null_window.c b/src/null_window.c index 1fc861b5..a22fc1a4 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -30,6 +30,7 @@ #include "internal.h" #include +#include static void applySizeLimits(_GLFWwindow* window, int* width, int* height) { diff --git a/src/wl_window.c b/src/wl_window.c index 2b05af60..7b8a813c 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -2775,9 +2775,10 @@ const char* _glfwGetClipboardStringWayland(void) return _glfw.wl.clipboardString; } -void _glfwSetThemeWayland(_GLFWwindow* window, _GLFWtheme* theme) +void _glfwSetThemeWayland(_GLFWwindow* window, const _GLFWtheme* theme) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + // TODO: implement } _GLFWtheme* _glfwGetThemeWayland(_GLFWwindow* window, int inlineDefaults) diff --git a/src/x11_window.c b/src/x11_window.c index 9ee0f2ee..28320dea 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3085,6 +3085,7 @@ const char* _glfwGetClipboardStringX11(void) void _glfwSetThemeX11(_GLFWwindow* window, const _GLFWtheme* theme) { _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + // TODO: implement } _GLFWtheme* _glfwGetThemeX11(_GLFWwindow* window, int inlineDefaults) From 73330df28a6ff08031fd7eeaf7000eff6dc5d55a Mon Sep 17 00:00:00 2001 From: ws909 <37029098+ws909@users.noreply.github.com> Date: Fri, 24 Feb 2023 00:27:41 +0100 Subject: [PATCH 31/31] Fix _glfwSetThemeWin32 --- src/win32_window.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/win32_window.c b/src/win32_window.c index 1a8c168b..6fe55c4d 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2434,13 +2434,13 @@ void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme) else memcpy(&newTheme, theme, sizeof(_GLFWtheme)); - if (newTheme->variation == GLFW_THEME_DEFAULT) + if (newTheme.variation == GLFW_THEME_DEFAULT) { applySystemTheme(window->win32.handle); } else { - GLFWbool darkMode = theme->variation == GLFW_THEME_DARK; + GLFWbool darkMode = newTheme.variation == GLFW_THEME_DARK; DwmSetWindowAttribute(window->win32.handle, DWMWA_USE_IMMERSIVE_DARK_MODE,