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 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..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) { @@ -551,13 +552,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_init.c b/src/win32_init.c index 3bba1f65..2a91bd83 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; } @@ -193,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 @@ -739,9 +756,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..b5f048e2 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 @@ -623,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 0a73f36b..6fe55c4d 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -37,6 +37,47 @@ #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 + +// 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, + &value, + sizeof(value)); + } +} + + +static int getAccentColor(float color[4]) +{ + if (!_glfw.win32.uxtheme.uxThemeAvailable) + return GLFW_FALSE; + + 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] = (float) (0xFF & rgba); + color[1] = (float) ((0xFF00 & rgba) >> 8); + color[2] = (float) ((0xFF0000 & rgba) >> 16); + color[3] = (float) ((0xFF000000 & rgba) >> 24); + + return GLFW_TRUE; +} + // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) @@ -1146,6 +1187,13 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l return 0; } + case WM_THEMECHANGED: + case WM_SETTINGCHANGE: { + if (window->theme.internal.variation == GLFW_THEME_DEFAULT) { + applySystemTheme(window->win32.handle); + } + } break; + case WM_GETDPISCALEDSIZE: { if (window->win32.scaleToMonitor) @@ -1436,6 +1484,9 @@ static int createNativeWindow(_GLFWwindow* window, _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); + // Use the system default when creating a window + applySystemTheme(window->win32.handle); + return GLFW_TRUE; } @@ -2373,15 +2424,66 @@ const char* _glfwGetClipboardStringWin32(void) return _glfw.win32.clipboardString; } -void _glfwSetThemeWin32(_GLFWwindow* window, _GLFWtheme* theme) +void _glfwSetThemeWin32(_GLFWwindow* window, const _GLFWtheme* theme) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); + _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); + } + else + { + GLFWbool darkMode = newTheme.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) +_GLFWtheme* _glfwGetThemeWin32(_GLFWwindow* window, int inlineDefaults) { - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, NULL); - return NULL; // TODO: implement + _GLFWtheme* theme = &window->theme.external; + memcpy(theme, &window->theme.internal, sizeof(_GLFWtheme)); + + if (!inlineDefaults) + return theme; + + if (theme->variation == GLFW_THEME_DEFAULT) + { + theme->variation = GLFW_THEME_LIGHT; + + 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; } EGLenum _glfwGetEGLPlatformWin32(EGLint** attribs) @@ -2512,4 +2614,3 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) } #endif // _GLFW_WIN32 - 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..7b8a813c 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -2775,12 +2775,13 @@ 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) +_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..28320dea 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -3082,12 +3082,13 @@ 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); + // TODO: implement } -_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);