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
This commit is contained in:
Kovid Goyal 2023-07-04 11:59:50 +05:30
parent b9bb9248f0
commit d3f14ffbf4
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
16 changed files with 249 additions and 183 deletions

View file

@ -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]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -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 <Availability.h>
#import <CoreServices/CoreServices.h>
@ -35,6 +36,7 @@
#include <float.h>
#include <string.h>
#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] : "<nil>",
appearance ? [appearance.name UTF8String] : "<nil>",
cs ? (cs.localizedName ? [cs.localizedName UTF8String] : [cs.description UTF8String]) : "<nil>",
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;

View file

@ -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)

4
glfw/internal.h vendored
View file

@ -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

View file

@ -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:

View file

@ -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; }

View file

@ -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

6
kitty/glfw-wrapper.c generated
View file

@ -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

8
kitty/glfw-wrapper.h generated
View file

@ -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

View file

@ -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) {

View file

@ -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.
'''
)

View file

@ -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);

View file

@ -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),

View file

@ -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 *);

View file

@ -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()

View file

@ -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