From d3f14ffbf414377743f8e8d76ef24dd173dfc730 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 4 Jul 2023 11:59:50 +0530 Subject: [PATCH] macOS: Fix window shadows not being drawn for transparent windows Re-organize the whole infrastructure for setting window chrome, doing it in a single function that has access to all settings. Fixes #2827 Fixes #6416 --- docs/changelog.rst | 2 + glfw/cocoa_window.m | 114 ++++++++++++++++++++++++------- glfw/glfw.py | 3 +- glfw/internal.h | 4 +- kitty/boss.py | 3 + kitty/cocoa_window.m | 70 ------------------- kitty/fast_data_types.pyi | 2 +- kitty/glfw-wrapper.c | 6 +- kitty/glfw-wrapper.h | 8 +-- kitty/glfw.c | 120 +++++++++++++++++++++++---------- kitty/options/definition.py | 11 +-- kitty/options/to-c-generated.h | 30 +++++++++ kitty/state.c | 13 ++-- kitty/state.h | 18 ++++- kitty/tabs.py | 11 ++- kitty/window.py | 17 +---- 16 files changed, 249 insertions(+), 183 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index abd7bd59d..ce76e315d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -85,6 +85,8 @@ Detailed list of changes - Fix a regression in 0.27.0 that broke setting of specific edge padding/margin via remote control (:iss:`6333`) +- macOS: Fix window shadows not being drawn for transparent windows (:iss:`2827`, :pull:`6416`) + 0.28.1 [2023-04-21] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index de8607ad4..88ef8c47d 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -26,8 +26,9 @@ // It is fine to use C99 in this file because it will not be built with VS //======================================================================== -#include "internal.h" #include "../kitty/monotonic.h" +#include "glfw3.h" +#include "internal.h" #include #import @@ -35,6 +36,7 @@ #include #include +#define debug(...) if (_glfw.hints.init.debugRendering) fprintf(stderr, __VA_ARGS__); GLFWAPI int glfwCocoaSetBackgroundBlur(GLFWwindow *w, int radius); @@ -293,6 +295,7 @@ vk_to_unicode_key_with_current_layout(uint16_t keycode) static NSUInteger getStyleMask(_GLFWwindow* window) { NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; + if (window->ns.titlebar_hidden) styleMask |= NSWindowStyleMaskFullSizeContentView; if (window->monitor || !window->decorated) styleMask |= NSWindowStyleMaskBorderless; @@ -2922,29 +2925,6 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) return window->ns.object; } -GLFWAPI void glfwHideCocoaTitlebar(GLFWwindow* handle, bool yes) { - @autoreleasepool { - _GLFWwindow* w = (_GLFWwindow*) handle; - NSWindow *window = w->ns.object; - w->ns.titlebar_hidden = yes; - NSButton *button; - button = [window standardWindowButton: NSWindowCloseButton]; - if (button) button.hidden = yes; - button = [window standardWindowButton: NSWindowMiniaturizeButton]; - if (button) button.hidden = yes; - button = [window standardWindowButton: NSWindowZoomButton]; - [window setTitlebarAppearsTransparent:yes]; - if (button) button.hidden = yes; - if (yes) { - [window setTitleVisibility:NSWindowTitleHidden]; - [window setStyleMask: [window styleMask] | NSWindowStyleMaskFullSizeContentView]; - } else { - [window setTitleVisibility:NSWindowTitleVisible]; - [window setStyleMask: [window styleMask] & ~NSWindowStyleMaskFullSizeContentView]; - } - } // autoreleasepool -} - GLFWAPI GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow *handle, GLFWcocoatextinputfilterfun callback) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(nil); @@ -2984,6 +2964,92 @@ GLFWAPI int glfwCocoaSetBackgroundBlur(GLFWwindow *w, int radius) { return orig; } +GLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool use_system_color, unsigned int system_color, int background_blur, unsigned int hide_window_decorations, bool show_text_in_titlebar, int color_space, float background_opacity, bool resizable) { @autoreleasepool { + _GLFWwindow* window = (_GLFWwindow*)w; + const bool is_transparent = ![window->ns.object isOpaque]; + if (!is_transparent) { background_opacity = 1.0; background_blur = 0; } + NSColor *background = nil; + NSAppearance *appearance = nil; + bool titlebar_transparent = false; + NSWindowStyleMask current_style_mask = [window->ns.object styleMask]; + bool in_fullscreen = ((current_style_mask & NSWindowStyleMaskFullScreen) != 0) || window->ns.in_traditional_fullscreen; + if (use_system_color || background_opacity < 1.0) { + if (is_transparent) { + // prevent blurring of shadows at window corners with desktop background by setting a low alpha background + background = background_blur > 0 ? [NSColor colorWithWhite: 0 alpha: 0.001f] : [NSColor clearColor]; + } else background = [NSColor clearColor]; + switch (system_color) { + case 1: + appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]; break; + case 2: + appearance = [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]; break; + } + } else { + // set a background color and make the title bar transparent so the background color is visible + double red = ((color >> 16) & 0xFF) / 255.0; + double green = ((color >> 8) & 0xFF) / 255.0; + double blue = (color & 0xFF) / 255.0; + double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue; + background = [NSColor colorWithSRGBRed:red green:green blue:blue alpha:1.f]; + appearance = [NSAppearance appearanceNamed:(luma < 0.5 ? NSAppearanceNameVibrantDark : NSAppearanceNameVibrantLight)]; + titlebar_transparent = true; + } + [window->ns.object setBackgroundColor:background]; + [window->ns.object setAppearance:appearance]; + glfwCocoaSetBackgroundBlur(w, background_blur); + bool has_shadow = false; + const char *decorations_desc = "full"; + window->ns.titlebar_hidden = false; + switch (hide_window_decorations) { + case 1: + decorations_desc = "none"; + window->decorated = false; + break; + case 2: + decorations_desc = "no-titlebar"; + window->decorated = true; + has_shadow = true; + titlebar_transparent = true; + window->ns.titlebar_hidden = true; + show_text_in_titlebar = false; + break; + default: + window->decorated = true; + has_shadow = true; + break; + } + bool hide_titlebar_buttons = !in_fullscreen && window->ns.titlebar_hidden; + [window->ns.object setTitlebarAppearsTransparent:titlebar_transparent]; + [window->ns.object setHasShadow:has_shadow]; + [window->ns.object setTitleVisibility:(show_text_in_titlebar) ? NSWindowTitleVisible : NSWindowTitleHidden]; + NSColorSpace *cs = nil; + switch (color_space) { + case SRGB_COLORSPACE: cs = [NSColorSpace sRGBColorSpace]; break; + case DISPLAY_P3_COLORSPACE: cs = [NSColorSpace displayP3ColorSpace]; break; + case DEFAULT_COLORSPACE: cs = [NSColorSpace deviceRGBColorSpace]; break; + } + window->resizable = resizable; + [window->ns.object setColorSpace:cs]; + [[window->ns.object standardWindowButton: NSWindowCloseButton] setHidden:hide_titlebar_buttons]; + [[window->ns.object standardWindowButton: NSWindowMiniaturizeButton] setHidden:hide_titlebar_buttons]; + [[window->ns.object standardWindowButton: NSWindowZoomButton] setHidden:hide_titlebar_buttons]; + debug( + "Window Chrome state:\n\tbackground: %s\n\tappearance: %s color_space: %s\n\t" + "blur: %d has_shadow: %d resizable: %d decorations: %s (%d)\n\t" + "titlebar: transparent: %d title_visibility: %d hidden: %d buttons_hidden: %d" + "\n", + background ? [background.description UTF8String] : "", + appearance ? [appearance.name UTF8String] : "", + cs ? (cs.localizedName ? [cs.localizedName UTF8String] : [cs.description UTF8String]) : "", + background_blur, has_shadow, resizable, decorations_desc, window->decorated, titlebar_transparent, + show_text_in_titlebar, window->ns.titlebar_hidden, hide_titlebar_buttons + ); + window->ns.pre_full_screen_style_mask = getStyleMask(window); + [window->ns.object setStyleMask:window->ns.pre_full_screen_style_mask]; + // HACK: Changing the style mask can cause the first responder to be cleared + [window->ns.object makeFirstResponder:window->ns.view]; +}} + GLFWAPI int glfwGetCurrentSystemColorTheme(void) { int theme_type = 0; NSAppearance *changedAppearance = NSApp.effectiveAppearance; diff --git a/glfw/glfw.py b/glfw/glfw.py index e77c2dd69..73d56e741 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -239,7 +239,6 @@ def generate_wrappers(glfw_header: str) -> None: functions.append(Function(decl)) for line in '''\ void* glfwGetCocoaWindow(GLFWwindow* window) - void glfwHideCocoaTitlebar(GLFWwindow* window, bool yes) void* glfwGetNSGLContext(GLFWwindow *window) uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor) GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback) @@ -254,6 +253,8 @@ def generate_wrappers(glfw_header: str) -> None: void* glfwGetX11Display(void) int32_t glfwGetX11Window(GLFWwindow* window) void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string) + void glfwCocoaSetWindowChrome(GLFWwindow* window, unsigned int color, bool use_system_color, unsigned int system_color,\ + int background_blur, unsigned int hide_window_decorations, bool show_text_in_titlebar, int color_space, float background_opacity, bool resizable) const char* glfwGetPrimarySelectionString(GLFWwindow* window, void) int glfwGetNativeKeyForName(const char* key_name, int case_sensitive) void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback) diff --git a/glfw/internal.h b/glfw/internal.h index a26b043ac..d3c2832ba 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -92,11 +92,11 @@ typedef int (* _GLFWextensionsupportedfun)(const char*); typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); -#define GL_VERSION 0x1f02 +#define GL_VERSION 0x1F02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_UNSIGNED_BYTE 0x1401 -#define GL_EXTENSIONS 0x1f03 +#define GL_EXTENSIONS 0x1F03 #define GL_NUM_EXTENSIONS 0x821d #define GL_CONTEXT_FLAGS 0x821e #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 diff --git a/kitty/boss.py b/kitty/boss.py index 1c423acef..f6b00bd4f 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -108,6 +108,7 @@ from .fast_data_types import ( set_options, set_os_window_size, set_os_window_title, + set_window_chrome, thread_write, toggle_fullscreen, toggle_maximized, @@ -1473,6 +1474,7 @@ class Boss: t = tm.tab_for_id(w.tab_id) if t is not None: t.relayout_borders() + set_window_chrome(w.os_window_id) def dispatch_action( self, @@ -2397,6 +2399,7 @@ class Boss: t = tm.active_tab if t is not None: t.relayout_borders() + set_window_chrome(tm.os_window_id) patch_global_colors(spec, configured) def apply_new_options(self, opts: Options) -> None: diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 6cfd30c89..be4d37b18 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -699,25 +699,6 @@ cocoa_update_menu_bar_title(PyObject *pytitle) { [m release]; } // }}} -bool -cocoa_make_window_resizable(void *w, bool resizable) { - NSWindow *window = (NSWindow*)w; - - @try { - if (resizable) { - [window setStyleMask: - [window styleMask] | NSWindowStyleMaskResizable]; - } else { - [window setStyleMask: - [window styleMask] & ~NSWindowStyleMaskResizable]; - } - } @catch (NSException *e) { - log_error("Failed to set style mask: %s: %s", [[e name] UTF8String], [[e reason] UTF8String]); - return false; - } - return true; -} - #define NSLeftAlternateKeyMask (0x000020 | NSEventModifierFlagOption) #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption) @@ -813,46 +794,6 @@ cocoa_set_activation_policy(bool hide_from_tasks) { [NSApp setActivationPolicy:(hide_from_tasks ? NSApplicationActivationPolicyAccessory : NSApplicationActivationPolicyRegular)]; } -void -cocoa_set_titlebar_appearance(void *w, unsigned int theme) -{ - if (!theme) return; - @autoreleasepool { - NSWindow *window = (NSWindow*)w; - [window setAppearance:[NSAppearance appearanceNamed:((theme == 2) ? NSAppearanceNameVibrantDark : NSAppearanceNameVibrantLight)]]; - } // autoreleasepool -} - -void -cocoa_set_titlebar_color(void *w, color_type titlebar_color) -{ - @autoreleasepool { - - NSWindow *window = (NSWindow*)w; - - double red = ((titlebar_color >> 16) & 0xFF) / 255.0; - double green = ((titlebar_color >> 8) & 0xFF) / 255.0; - double blue = (titlebar_color & 0xFF) / 255.0; - - NSColor *background = - [NSColor colorWithSRGBRed:red - green:green - blue:blue - alpha:1.0]; - [window setTitlebarAppearsTransparent:YES]; - [window setBackgroundColor:background]; - - double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue; - - if (luma < 0.5) { - [window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; - } else { - [window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; - } - - } // autoreleasepool -} - static PyObject* cocoa_set_url_handler(PyObject UNUSED *self, PyObject *args) { @autoreleasepool { @@ -969,17 +910,6 @@ cleanup(void) { } // autoreleasepool } -void -cocoa_hide_window_title(void *w) -{ - @autoreleasepool { - - NSWindow *window = (NSWindow*)w; - [window setTitleVisibility:NSWindowTitleHidden]; - - } // autoreleasepool -} - void cocoa_system_beep(const char *path) { if (!path) { NSBeep(); return; } diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 6d4d3dd1a..f2ef86e5d 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -466,7 +466,7 @@ def init_cell_program() -> None: pass -def set_titlebar_color(os_window_id: int, color: int, use_system_color: bool = False, system_color: int = 0) -> bool: +def set_window_chrome(os_window_id: int) -> bool: pass diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 281940403..73afb2688 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -413,9 +413,6 @@ load_glfw(const char* path) { *(void **) (&glfwGetCocoaWindow_impl) = dlsym(handle, "glfwGetCocoaWindow"); if (glfwGetCocoaWindow_impl == NULL) dlerror(); // clear error indicator - *(void **) (&glfwHideCocoaTitlebar_impl) = dlsym(handle, "glfwHideCocoaTitlebar"); - if (glfwHideCocoaTitlebar_impl == NULL) dlerror(); // clear error indicator - *(void **) (&glfwGetNSGLContext_impl) = dlsym(handle, "glfwGetNSGLContext"); if (glfwGetNSGLContext_impl == NULL) dlerror(); // clear error indicator @@ -458,6 +455,9 @@ load_glfw(const char* path) { *(void **) (&glfwSetPrimarySelectionString_impl) = dlsym(handle, "glfwSetPrimarySelectionString"); if (glfwSetPrimarySelectionString_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwCocoaSetWindowChrome_impl) = dlsym(handle, "glfwCocoaSetWindowChrome"); + if (glfwCocoaSetWindowChrome_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwGetPrimarySelectionString_impl) = dlsym(handle, "glfwGetPrimarySelectionString"); if (glfwGetPrimarySelectionString_impl == NULL) dlerror(); // clear error indicator diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 9d7c7913b..4d0878c65 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -2181,10 +2181,6 @@ typedef void* (*glfwGetCocoaWindow_func)(GLFWwindow*); GFW_EXTERN glfwGetCocoaWindow_func glfwGetCocoaWindow_impl; #define glfwGetCocoaWindow glfwGetCocoaWindow_impl -typedef void (*glfwHideCocoaTitlebar_func)(GLFWwindow*, bool); -GFW_EXTERN glfwHideCocoaTitlebar_func glfwHideCocoaTitlebar_impl; -#define glfwHideCocoaTitlebar glfwHideCocoaTitlebar_impl - typedef void* (*glfwGetNSGLContext_func)(GLFWwindow*); GFW_EXTERN glfwGetNSGLContext_func glfwGetNSGLContext_impl; #define glfwGetNSGLContext glfwGetNSGLContext_impl @@ -2241,6 +2237,10 @@ typedef void (*glfwSetPrimarySelectionString_func)(GLFWwindow*, const char*); GFW_EXTERN glfwSetPrimarySelectionString_func glfwSetPrimarySelectionString_impl; #define glfwSetPrimarySelectionString glfwSetPrimarySelectionString_impl +typedef void (*glfwCocoaSetWindowChrome_func)(GLFWwindow*, unsigned int, bool, unsigned int, int, unsigned int, bool, int, float, bool); +GFW_EXTERN glfwCocoaSetWindowChrome_func glfwCocoaSetWindowChrome_impl; +#define glfwCocoaSetWindowChrome glfwCocoaSetWindowChrome_impl + typedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*); GFW_EXTERN glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl; #define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 651352a86..b17082e23 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -15,15 +15,11 @@ #ifndef __APPLE__ #include "freetype_render_ui_text.h" #endif -extern bool cocoa_make_window_resizable(void *w, bool); extern void cocoa_focus_window(void *w); extern long cocoa_window_number(void *w); extern void cocoa_create_global_menu(void); -extern void cocoa_hide_window_title(void *w); extern void cocoa_system_beep(const char*); extern void cocoa_set_activation_policy(bool); -extern void cocoa_set_titlebar_appearance(void *w, unsigned int theme); -extern void cocoa_set_titlebar_color(void *w, color_type color); extern bool cocoa_alt_option_key_pressed(unsigned long); extern void cocoa_toggle_secure_keyboard_entry(void); extern void cocoa_hide(void); @@ -844,16 +840,87 @@ intercept_cocoa_fullscreen(GLFWwindow *w) { } #endif -void -set_titlebar_color(OSWindow *w, color_type color, bool use_system_color, unsigned int system_color UNUSED) { - if (w->handle && (!w->last_titlebar_color || (w->last_titlebar_color & 0xffffff) != (color & 0xffffff))) { - w->last_titlebar_color = (1 << 24) | (color & 0xffffff); +static void +init_window_chrome_state(WindowChromeState *s, color_type active_window_bg, bool is_semi_transparent, float background_opacity) { + zero_at_ptr(s); + const bool should_blur = background_opacity < 1.f && OPT(background_blur) > 0 && is_semi_transparent; +#define SET_TCOL(val) \ + s->use_system_color = false; \ + switch (val & 0xff) { \ + case 0: s->use_system_color = true; break; \ + case 1: s->color = active_window_bg; break; \ + default: s->color = val >> 8; break; \ + } + #ifdef __APPLE__ - if (!use_system_color) cocoa_set_titlebar_color(glfwGetCocoaWindow(w->handle), color); - else cocoa_set_titlebar_appearance(glfwGetCocoaWindow(w->handle), system_color); + if (OPT(macos_titlebar_color) < 0) { + s->use_system_color = true; + s->system_color = -OPT(macos_titlebar_color); + } else { + unsigned long val = OPT(macos_titlebar_color); + SET_TCOL(val); + } + s->macos_colorspace = OPT(macos_colorspace); + s->resizable = OPT(macos_window_resizable); #else - if (global_state.is_wayland && glfwWaylandSetTitlebarColor) glfwWaylandSetTitlebarColor(w->handle, color, use_system_color); + if (global_state.is_wayland) { SET_TCOL(OPT(wayland_titlebar_color)); } #endif + s->background_blur = should_blur ? OPT(background_blur) : 0; + s->hide_window_decorations = OPT(hide_window_decorations); + s->show_title_in_titlebar = (OPT(macos_show_window_title_in) & WINDOW) != 0; + s->background_opacity = background_opacity; +} + +#ifdef __APPLE__ +static void +apply_window_chrome_state(GLFWwindow *w, WindowChromeState new_state, int width, int height, bool window_decorations_changed) { + glfwCocoaSetWindowChrome(w, + new_state.color, new_state.use_system_color, new_state.system_color, + new_state.background_blur, new_state.hide_window_decorations, + new_state.show_title_in_titlebar, new_state.macos_colorspace, + new_state.background_opacity, new_state.resizable + ); + // Need to resize the window again after hiding decorations or title bar to take up screen space + if (window_decorations_changed) glfwSetWindowSize(w, width, height); +} +#endif + +void +set_window_chrome(OSWindow *w) { + if (!w->handle) return; + color_type bg = OPT(background); + if (w->num_tabs > w->active_tab) { + Tab *tab = w->tabs + w->active_tab; + if (tab->num_windows > tab->active_window) { + Window *window = tab->windows + tab->active_window; + ColorProfile *c; + if (window->render_data.screen && (c=window->render_data.screen->color_profile)) { + bg = colorprofile_to_color(c, c->overridden.default_bg, c->configured.default_bg).rgb; + } + } + } + + WindowChromeState new_state; + init_window_chrome_state(&new_state, bg, w->is_semi_transparent, w->background_opacity); + if (memcmp(&new_state, &w->last_window_chrome, sizeof(WindowChromeState)) != 0) { + int width, height; + glfwGetWindowSize(w->handle, &width, &height); + bool window_decorations_changed = new_state.hide_window_decorations != w->last_window_chrome.hide_window_decorations; +#ifdef __APPLE__ + apply_window_chrome_state(w->handle, new_state, width, height, window_decorations_changed); +#else + if (window_decorations_changed) { + bool hide_window_decorations = new_state.hide_window_decorations & 1; + glfwSetWindowAttrib(w->handle, GLFW_DECORATED, !hide_window_decorations); + glfwSetWindowSize(w->handle, width, height); + } + if (global_state.is_wayland) { + if (glfwWaylandSetTitlebarColor) glfwWaylandSetTitlebarColor(w->handle, new_state.color, new_state.use_system_color); + } else { + glfwSetX11WindowBlurred(w->handle, new_state.background_blur > 0); + } +#endif + w->last_window_chrome = new_state; } } @@ -886,7 +953,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { // We don't use depth and stencil buffers glfwWindowHint(GLFW_DEPTH_BITS, 0); glfwWindowHint(GLFW_STENCIL_BITS, 0); - if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false); glfwSetApplicationCloseCallback(application_close_requested_callback); glfwSetCurrentSelectionCallback(get_current_selection); glfwSetHasCurrentSelectionCallback(has_current_selection); @@ -899,6 +965,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { glfwSetApplicationWillFinishLaunching(cocoa_create_global_menu); #endif } + if (OPT(hide_window_decorations) & 1) glfwWindowHint(GLFW_DECORATED, false); const bool set_blur = OPT(background_blur) > 0 && OPT(background_opacity) < 1.f; #ifdef __APPLE__ @@ -1012,18 +1079,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { w->fonts_data = fonts_data; w->shown_once = true; w->last_focused_counter = ++focus_counter; -#ifdef __APPLE__ - if (OPT(hide_window_decorations) & 2) { - glfwHideCocoaTitlebar(glfw_window, true); - } else if (!(OPT(macos_show_window_title_in) & WINDOW)) { - if (glfwGetCocoaWindow) cocoa_hide_window_title(glfwGetCocoaWindow(glfw_window)); - else log_error("Failed to load glfwGetCocoaWindow"); - } - if (OPT(hide_window_decorations)) { - // Need to resize the window again after hiding decorations or title bar to take up screen space - glfwSetWindowSize(glfw_window, width, height); - } -#endif os_window_update_size_increments(w); #ifdef __APPLE__ if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option); @@ -1033,9 +1088,6 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { if (logo.pixels && logo.width && logo.height) glfwSetWindowIcon(glfw_window, 1, &logo); glfwSetCursor(glfw_window, standard_cursor); update_os_window_viewport(w, false); -#ifdef __APPLE__ - if (glfwGetCocoaWindow) cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window), OPT(macos_window_resizable)); -#endif glfwSetWindowPosCallback(glfw_window, window_pos_callback); // missing size callback glfwSetWindowCloseCallback(glfw_window, window_close_callback); @@ -1066,6 +1118,10 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) { warned = true; } } + init_window_chrome_state(&w->last_window_chrome, OPT(background), w->is_semi_transparent, w->background_opacity); +#ifdef __APPLE__ + apply_window_chrome_state(w->handle, w->last_window_chrome, width, height, OPT(hide_window_decorations) != 0); +#endif // Update window state // We do not call glfwWindowHint to set GLFW_MAXIMIZED before the window is created. // That would cause the window to be set to maximize immediately after creation and use the wrong initial size when restored. @@ -1088,16 +1144,6 @@ os_window_update_size_increments(OSWindow *window) { } } -void -update_background_blur(OSWindow *os_window) { - const bool should_blur = os_window->background_opacity < 1.f && OPT(background_blur) > 0 && os_window->is_semi_transparent; -#ifdef __APPLE__ - glfwCocoaSetBackgroundBlur(os_window->handle, should_blur ? OPT(background_blur) : 0); -#else - if (!global_state.is_wayland) glfwSetX11WindowBlurred(os_window->handle, should_blur); -#endif -} - #ifdef __APPLE__ static bool window_in_same_cocoa_workspace(void *w, size_t *source_workspaces, size_t source_workspace_count) { diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 1b1739248..1455f11f1 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -3084,9 +3084,7 @@ egr() # }}} # os {{{ agr('os', 'OS specific tweaks') -opt('wayland_titlebar_color', 'system', - option_type='titlebar_color', - long_text=''' +opt('wayland_titlebar_color', 'system', option_type='titlebar_color', ctype='uint', long_text=''' The color of the kitty window's titlebar on Wayland systems with client side window decorations such as GNOME. A value of :code:`system` means to use the default system color, a value of :code:`background` means to use the @@ -3095,9 +3093,7 @@ arbitrary color, such as :code:`#12af59` or :code:`red`. ''' ) -opt('macos_titlebar_color', 'system', - option_type='macos_titlebar_color', - long_text=''' +opt('macos_titlebar_color', 'system', option_type='macos_titlebar_color', ctype='int', long_text=''' The color of the kitty window's titlebar on macOS. A value of :code:`system` means to use the default system color, :code:`light` or :code:`dark` can also be used to set it explicitly. A value of @@ -3151,8 +3147,7 @@ opt('macos_window_resizable', 'yes', option_type='to_bool', ctype='bool', long_text=''' Disable this if you want kitty top-level OS windows to not be resizable on -macOS. Changing this option by reloading the config will only affect newly -created OS windows. +macOS. ''' ) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index aebbb451f..867e25179 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -941,6 +941,32 @@ convert_from_opts_allow_hyperlinks(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_wayland_titlebar_color(PyObject *val, Options *opts) { + opts->wayland_titlebar_color = PyLong_AsUnsignedLong(val); +} + +static void +convert_from_opts_wayland_titlebar_color(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "wayland_titlebar_color"); + if (ret == NULL) return; + convert_from_python_wayland_titlebar_color(ret, opts); + Py_DECREF(ret); +} + +static void +convert_from_python_macos_titlebar_color(PyObject *val, Options *opts) { + opts->macos_titlebar_color = PyLong_AsLong(val); +} + +static void +convert_from_opts_macos_titlebar_color(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "macos_titlebar_color"); + if (ret == NULL) return; + convert_from_python_macos_titlebar_color(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_macos_option_as_alt(PyObject *val, Options *opts) { opts->macos_option_as_alt = PyLong_AsUnsignedLong(val); @@ -1204,6 +1230,10 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_allow_hyperlinks(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_wayland_titlebar_color(py_opts, opts); + if (PyErr_Occurred()) return false; + convert_from_opts_macos_titlebar_color(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_macos_option_as_alt(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_macos_hide_from_tasks(py_opts, opts); diff --git a/kitty/state.c b/kitty/state.c index d14a489c6..5faabf310 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -516,6 +516,7 @@ set_active_window(id_type os_window_id, id_type tab_id, id_type window_id) { (void)window; tab->active_window = w; osw->needs_render = true; + set_window_chrome(osw); END_WITH_WINDOW; } @@ -877,14 +878,11 @@ PYWRAP1(run_with_activation_token) { Py_RETURN_NONE; } -PYWRAP1(set_titlebar_color) { +PYWRAP1(set_window_chrome) { id_type os_window_id; - unsigned int color; - int use_system_color = 0; - unsigned int system_color = 0; - PA("KI|pI", &os_window_id, &color, &use_system_color, &system_color); + PA("K", &os_window_id); WITH_OS_WINDOW(os_window_id) - set_titlebar_color(os_window, color, use_system_color, system_color); + set_window_chrome(os_window); Py_RETURN_TRUE; END_WITH_OS_WINDOW Py_RETURN_FALSE; @@ -1093,7 +1091,6 @@ PYWRAP0(apply_options_update) { get_platform_dependent_config_values(os_window->handle); os_window->background_opacity = OPT(background_opacity); os_window->is_damaged = true; - update_background_blur(os_window); for (size_t t = 0; t < os_window->num_tabs; t++) { Tab *tab = os_window->tabs + t; for (size_t w = 0; w < tab->num_windows; w++) { @@ -1363,7 +1360,7 @@ static PyMethodDef module_methods[] = { MW(mark_os_window_for_close, METH_VARARGS), MW(set_application_quit_request, METH_VARARGS), MW(current_application_quit_request, METH_NOARGS), - MW(set_titlebar_color, METH_VARARGS), + MW(set_window_chrome, METH_VARARGS), MW(focus_os_window, METH_VARARGS), MW(mark_tab_bar_dirty, METH_O), MW(run_with_activation_token, METH_O), diff --git a/kitty/state.h b/kitty/state.h index dd126347c..6d9393054 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -90,6 +90,8 @@ typedef struct { } underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline; bool show_hyperlink_targets; int background_blur; + long macos_titlebar_color; + unsigned long wayland_titlebar_color; } Options; typedef struct WindowLogoRenderData { @@ -186,6 +188,17 @@ typedef struct { unsigned int width, height, num_of_resize_events; } LiveResizeInfo; +typedef struct WindowChromeState { + color_type color; + bool use_system_color; + unsigned system_color; + int background_blur; + unsigned hide_window_decorations; + bool show_title_in_titlebar; + bool resizable; + int macos_colorspace; + float background_opacity; +} WindowChromeState; typedef struct { void *handle; @@ -218,7 +231,7 @@ typedef struct { LiveResizeInfo live_resize; bool has_pending_resizes, is_semi_transparent, shown_once, is_damaged; unsigned int clear_count; - color_type last_titlebar_color; + WindowChromeState last_window_chrome; float background_opacity; FONTS_DATA_HANDLE fonts_data; id_type temp_font_group_id; @@ -301,7 +314,7 @@ void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool, boo void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*); void blank_canvas(float, color_type); void blank_os_window(OSWindow *); -void set_titlebar_color(OSWindow *w, color_type color, bool use_system_color, unsigned int system_color); +void set_window_chrome(OSWindow *w); FONTS_DATA_HANDLE load_fonts_data(double, double, double); void send_prerendered_sprites_for_window(OSWindow *w); #ifdef __APPLE__ @@ -366,4 +379,3 @@ void update_ime_position(Window* w, Screen *screen); bool update_ime_position_for_window(id_type window_id, bool force, int update_focus); void set_ignore_os_keyboard_processing(bool enabled); void update_menu_bar_title(PyObject *title UNUSED); -void update_background_blur(OSWindow *); diff --git a/kitty/tabs.py b/kitty/tabs.py index ea6c8c07a..6ada6fe8d 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -167,9 +167,10 @@ class Tab: # {{{ self._set_current_layout(self.enabled_layouts[0]) self.relayout() - def apply_options(self) -> None: + def apply_options(self, is_active: bool) -> None: + aw = self.active_window for window in self: - window.apply_options() + window.apply_options(is_active and aw is window) self.set_enabled_layouts(get_options().enabled_layouts) def take_over_from(self, other_tab: 'Tab') -> None: @@ -287,15 +288,12 @@ class Tab: # {{{ def relayout_borders(self) -> None: tm = self.tab_manager_ref() if tm is not None: - w = self.active_window ly = self.current_layout self.borders( all_windows=self.windows, current_layout=ly, tab_bar_rects=tm.tab_bar_rects, draw_window_borders=(ly.needs_window_borders and self.windows.num_visble_groups > 1) or ly.must_draw_borders ) - if w is not None: - w.change_titlebar_color() def create_layout_object(self, name: str) -> Layout: return create_layout_object_for(name, self.os_window_id, self.id) @@ -1218,8 +1216,9 @@ class TabManager: # {{{ del self.tabs def apply_options(self) -> None: + at = self.active_tab for tab in self: - tab.apply_options() + tab.apply_options(at is tab) self.tab_bar_hidden = get_options().tab_bar_style == 'hidden' self.tab_bar.apply_options() self.update_tab_bar_data() diff --git a/kitty/window.py b/kitty/window.py index 985c59429..4888c3b60 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -38,7 +38,6 @@ from .constants import ( appname, clear_handled_signals, config_dir, - is_macos, wakeup_io_loop, ) from .fast_data_types import ( @@ -70,7 +69,6 @@ from .fast_data_types import ( mouse_selection, move_cursor_to_mouse_if_in_prompt, pt_to_px, - set_titlebar_color, set_window_logo, set_window_padding, set_window_render_data, @@ -619,10 +617,9 @@ class Window: val = round(val) return int(val) - def apply_options(self) -> None: + def apply_options(self, is_active: bool) -> None: opts = get_options() self.update_effective_padding() - self.change_titlebar_color() setup_colors(self.screen, opts) @property @@ -1018,18 +1015,6 @@ class Window: tab.relayout_borders() tab.on_bell(self) - def change_titlebar_color(self) -> None: - opts = get_options() - val = opts.macos_titlebar_color if is_macos else opts.wayland_titlebar_color - if val > 0: - if (val & 0xff) == 1: - val = self.screen.color_profile.default_bg - else: - val = val >> 8 - set_titlebar_color(self.os_window_id, val) - else: - set_titlebar_color(self.os_window_id, 0, True, -val) - def change_colors(self, changes: Dict[DynamicColor, Optional[str]]) -> None: dirtied = default_bg_changed = False