diff --git a/docs/changelog.rst b/docs/changelog.rst index 49f89f8e6..b35635cfc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -32,6 +32,9 @@ To update |kitty|, :doc:`follow the instructions `. - GNOME: Fix maximize state not being remembered when focus changes and window decorations are hidden (:iss:`3507`) +- GNOME: Add a new :opt:`wayland_titlebar_color` option to control the color of the + kitty window titlebar + - Fix reading :option:`kitty --session` from ``STDIN`` not working when the :option:`kitty --detach` option is used (:iss:`3523`) diff --git a/glfw/glfw.py b/glfw/glfw.py index e841ed8d8..bdc6bf4c4 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -218,6 +218,7 @@ def generate_wrappers(glfw_header: str) -> None: 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) + bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color) unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data) void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) diff --git a/glfw/wl_client_side_decorations.c b/glfw/wl_client_side_decorations.c index 1aef40cd0..3fc79a194 100644 --- a/glfw/wl_client_side_decorations.c +++ b/glfw/wl_client_side_decorations.c @@ -146,6 +146,17 @@ static void render_title_bar(_GLFWwindow *window, bool to_front_buffer) { const bool is_focused = window->id == _glfw.focusedWindowId; uint32_t bg_color = is_focused ? active_bg_color : passive_bg_color; + uint32_t fg_color = is_focused ? 0xff444444 : 0xff888888; + if (decs.use_custom_titlebar_color) { + bg_color = 0xff000000 | (decs.titlebar_color & 0xffffff); + double red = ((bg_color >> 16) & 0xFF) / 255.0; + double green = ((bg_color >> 8) & 0xFF) / 255.0; + double blue = (bg_color & 0xFF) / 255.0; + double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue; + if (luma < 0.5) { + fg_color = is_focused ? 0xffeeeeee : 0xff888888; + } + } uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back; // render shadow part @@ -169,7 +180,6 @@ render_title_bar(_GLFWwindow *window, bool to_front_buffer) { // render text part output += decs.top.buffer.stride * margin; if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) { - uint32_t fg_color = is_focused ? 0xff444444 : 0xff888888; if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height - margin, 0, 0, 0)) return; } for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) { @@ -413,3 +423,16 @@ set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) { *height -= decs.metrics.visible_titlebar_height; } } + +void +set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color) { + bool use_custom_color = !use_system_color; + if (use_custom_color != decs.use_custom_titlebar_color || color != decs.titlebar_color) { + decs.use_custom_titlebar_color = use_custom_color; + decs.titlebar_color = color; + } + if (window->decorated && decs.top.surface) { + update_title_bar(window); + damage_csd(top, decs.top.buffer.front); + } +} diff --git a/glfw/wl_client_side_decorations.h b/glfw/wl_client_side_decorations.h index 8ea3d105f..e42e8f27f 100644 --- a/glfw/wl_client_side_decorations.h +++ b/glfw/wl_client_side_decorations.h @@ -14,3 +14,4 @@ void free_csd_surfaces(_GLFWwindow *window); void change_csd_title(_GLFWwindow *window); bool ensure_csd_resources(_GLFWwindow *window); void set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height); +void set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color); diff --git a/glfw/wl_platform.h b/glfw/wl_platform.h index 8a8d2c4ab..f35b55e94 100644 --- a/glfw/wl_platform.h +++ b/glfw/wl_platform.h @@ -195,6 +195,9 @@ typedef struct _GLFWwindowWayland size_t for_decoration_size, stride, segments, corner_size; } shadow_tile; monotonic_t last_click_on_top_decoration_at; + + uint32_t titlebar_color; + bool use_custom_titlebar_color; } decorations; struct { diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 9aaa8de69..f7e329609 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2001,3 +2001,12 @@ GLFWAPI unsigned long long glfwDBusUserNotify(const char *app_name, const char* GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) { glfw_dbus_set_user_notification_activated_handler(handler); } + +GLFWAPI bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color) { + _GLFWwindow* window = (_GLFWwindow*) handle; + if (!window->wl.decorations.serverSide) { + set_titlebar_color(window, color, use_system_color); + return true; + } + return false; +} diff --git a/kitty/config_data.py b/kitty/config_data.py index b9d5a5558..c6608554d 100644 --- a/kitty/config_data.py +++ b/kitty/config_data.py @@ -1260,6 +1260,14 @@ def macos_titlebar_color(x: str) -> int: return (color_as_int(to_color(x)) << 8) | 2 +o('wayland_titlebar_color', 'system', option_type=macos_titlebar_color, long_text=_(''' +Change 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 background color +of the currently active window and finally you can use +an arbitrary color, such as :code:`#12af59` or :code:`red`. +''')) + o('macos_titlebar_color', 'system', option_type=macos_titlebar_color, long_text=_(''' Change the color of the kitty window's titlebar on macOS. A value of :code:`system` means to use the default system color, a value of :code:`background` means to use diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index dbff90136..f100240f2 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -437,7 +437,7 @@ def init_cell_program() -> None: pass -def set_titlebar_color(os_window_id: int, color: int) -> bool: +def set_titlebar_color(os_window_id: int, color: int, use_system_color: bool = False) -> bool: pass diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 1c78bba04..ad47a3f4d 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -432,6 +432,9 @@ load_glfw(const char* path) { *(void **) (&glfwRequestWaylandFrameEvent_impl) = dlsym(handle, "glfwRequestWaylandFrameEvent"); if (glfwRequestWaylandFrameEvent_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwWaylandSetTitlebarColor_impl) = dlsym(handle, "glfwWaylandSetTitlebarColor"); + if (glfwWaylandSetTitlebarColor_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwDBusUserNotify_impl) = dlsym(handle, "glfwDBusUserNotify"); if (glfwDBusUserNotify_impl == NULL) dlerror(); // clear error indicator diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 0795adf1f..5003dadf3 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -2154,6 +2154,10 @@ typedef void (*glfwRequestWaylandFrameEvent_func)(GLFWwindow*, unsigned long lon GFW_EXTERN glfwRequestWaylandFrameEvent_func glfwRequestWaylandFrameEvent_impl; #define glfwRequestWaylandFrameEvent glfwRequestWaylandFrameEvent_impl +typedef bool (*glfwWaylandSetTitlebarColor_func)(GLFWwindow*, uint32_t, bool); +GFW_EXTERN glfwWaylandSetTitlebarColor_func glfwWaylandSetTitlebarColor_impl; +#define glfwWaylandSetTitlebarColor glfwWaylandSetTitlebarColor_impl + typedef unsigned long long (*glfwDBusUserNotify_func)(const char*, const char*, const char*, const char*, const char*, int32_t, GLFWDBusnotificationcreatedfun, void*); GFW_EXTERN glfwDBusUserNotify_func glfwDBusUserNotify_impl; #define glfwDBusUserNotify glfwDBusUserNotify_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 4fe3ebe30..83501a14f 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -573,11 +573,13 @@ intercept_cocoa_fullscreen(GLFWwindow *w) { #endif void -set_titlebar_color(OSWindow *w, color_type color) { +set_titlebar_color(OSWindow *w, color_type color, bool use_system_color) { if (w->handle && (!w->last_titlebar_color || (w->last_titlebar_color & 0xffffff) != (color & 0xffffff))) { w->last_titlebar_color = (1 << 24) | (color & 0xffffff); #ifdef __APPLE__ - cocoa_set_titlebar_color(glfwGetCocoaWindow(w->handle), color); + if (!use_system_color) cocoa_set_titlebar_color(glfwGetCocoaWindow(w->handle), color); +#else + if (global_state.is_wayland && glfwWaylandSetTitlebarColor) glfwWaylandSetTitlebarColor(w->handle, color, use_system_color); #endif } } diff --git a/kitty/state.c b/kitty/state.c index 8d7800ee0..f74e2ea46 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -918,9 +918,10 @@ PYWRAP1(focus_os_window) { PYWRAP1(set_titlebar_color) { id_type os_window_id; unsigned int color; - PA("KI", &os_window_id, &color); + int use_system_color = 0; + PA("KI|p", &os_window_id, &color, &use_system_color); WITH_OS_WINDOW(os_window_id) - set_titlebar_color(os_window, color); + set_titlebar_color(os_window, color, use_system_color); Py_RETURN_TRUE; END_WITH_OS_WINDOW Py_RETURN_FALSE; diff --git a/kitty/state.h b/kitty/state.h index 042e1924e..fef3d1cf7 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -256,7 +256,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); +void set_titlebar_color(OSWindow *w, color_type color, bool use_system_color); FONTS_DATA_HANDLE load_fonts_data(double, double, double); void send_prerendered_sprites_for_window(OSWindow *w); #ifdef __APPLE__ diff --git a/kitty/window.py b/kitty/window.py index fff388873..ce82a9a47 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -19,7 +19,7 @@ from typing import ( from .child import ProcessDesc from .cli_stub import CLIOptions from .config import build_ansi_color_table -from .constants import appname, wakeup +from .constants import appname, wakeup, is_macos from .fast_data_types import ( BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, DCS, DECORATION, DIM, GLFW_MOD_CONTROL, @@ -629,13 +629,15 @@ class Window: tab.on_bell(self) def change_titlebar_color(self) -> None: - val = self.opts.macos_titlebar_color + val = self.opts.macos_titlebar_color if is_macos else self.opts.wayland_titlebar_color if val: 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) def change_colors(self, changes: Dict[DynamicColor, Optional[str]]) -> None: dirtied = default_bg_changed = False