From c79bba89ff4e02cefbd85659bcd1ecc814426c13 Mon Sep 17 00:00:00 2001 From: Aleksei Gmitron Date: Sun, 7 Jun 2026 17:41:57 +0400 Subject: [PATCH] feat: add topmost functionality for MacOS --- glfw/cocoa_window.m | 12 ++++++++---- glfw/glfw3.h | 2 +- kittens/panel/main.py | 4 +++- kitty/glfw-wrapper.h | 2 +- kitty/glfw.c | 2 ++ kitty/simple_cli_definitions.py | 9 ++++++++- kitty/types.py | 1 + 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index c72a89dbb..04bba77c6 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -2681,9 +2681,10 @@ _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig double spacing_y = top_edge_spacing + bottom_edge_spacing; const unsigned xsz = config.x_size_in_pixels ? (unsigned)(config.x_size_in_pixels * xscale) : (cell_width * config.x_size_in_cells); const unsigned ysz = config.y_size_in_pixels ? (unsigned)(config.y_size_in_pixels * yscale) : (cell_height * config.y_size_in_cells); + NSRect placement_frame = config.use_physical_screen_frame ? screen.frame : screen.visibleFrame; CGFloat dock_height = NSMinY(screen.visibleFrame) - NSMinY(screen.frame); - CGFloat menubar_height = NSHeight(screen.frame) - NSHeight(screen.visibleFrame) - dock_height; - CGFloat x = NSMinX(screen.visibleFrame), y = NSMinY(screen.visibleFrame) - 1, width = NSWidth(screen.visibleFrame), height = NSHeight(screen.visibleFrame) + 2; + CGFloat menubar_height = config.use_physical_screen_frame ? 0 : NSHeight(screen.frame) - NSHeight(screen.visibleFrame) - dock_height; + CGFloat x = NSMinX(placement_frame), y = NSMinY(placement_frame) - 1, width = NSWidth(placement_frame), height = NSHeight(placement_frame) + 2; if (config.type == GLFW_LAYER_SHELL_BACKGROUND || config.edge == GLFW_EDGE_CENTER) { x = NSMinX(screen.frame); height = NSHeight(screen.frame) - menubar_height + 1; y = NSMinY(screen.frame); width = NSWidth(screen.frame); } @@ -2701,6 +2702,9 @@ _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig case GLFW_LAYER_SHELL_PANEL: level = NSNormalWindowLevel - 1; break; case GLFW_LAYER_SHELL_TOP: level--; break; } + if (config.use_physical_screen_frame && config.type == GLFW_LAYER_SHELL_OVERLAY && config.edge == GLFW_EDGE_TOP) { + level = NSMainMenuWindowLevel + 1; + } if (config.type != GLFW_LAYER_SHELL_BACKGROUND && config.edge != GLFW_EDGE_CENTER) { double panel_height = spacing_y + ysz / yscale, panel_width = spacing_x + xsz / xscale; switch (config.edge) { @@ -2724,8 +2728,8 @@ _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig height = panel_height; width = panel_width; break; } - if (width < 1.) width = NSWidth(screen.visibleFrame); - if (height < 1.) height = NSWidth(screen.visibleFrame); + if (width < 1.) width = NSWidth(placement_frame); + if (height < 1.) height = NSHeight(placement_frame); } if (config.edge != GLFW_EDGE_CENTER_SIZED) { diff --git a/glfw/glfw3.h b/glfw/glfw3.h index cbacb7568..07e975d60 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1368,7 +1368,7 @@ typedef struct GLFWLayerShellConfig { unsigned x_size_in_cells, x_size_in_pixels; unsigned y_size_in_cells, y_size_in_pixels; int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin; - int requested_exclusive_zone, hide_on_focus_loss; + int requested_exclusive_zone, hide_on_focus_loss, use_physical_screen_frame; unsigned override_exclusive_zone; void (*size_callback)(GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing); struct { float xscale, yscale; } expected; diff --git a/kittens/panel/main.py b/kittens/panel/main.py index 451239deb..2720463dc 100644 --- a/kittens/panel/main.py +++ b/kittens/panel/main.py @@ -94,6 +94,7 @@ def layer_shell_config(opts: PanelCLIOptions) -> LayerShellConfig: requested_exclusive_zone=opts.exclusive_zone, override_exclusive_zone=opts.override_exclusive_zone, hide_on_focus_loss=opts.hide_on_focus_loss, + use_physical_screen_frame=opts.use_physical_screen_frame, output_name=opts.output_name or '') @@ -112,7 +113,8 @@ def cli_option_to_lsc_configs_map() -> MappingProxyType[str, tuple[str, ...]]: 'focus_policy': ('focus_policy',), 'exclusive_zone': ('requested_exclusive_zone',), 'override_exclusive_zone': ('override_exclusive_zone',), - 'hide_on_focus_loss': ('hide_on_focus_loss',) + 'hide_on_focus_loss': ('hide_on_focus_loss',), + 'use_physical_screen_frame': ('use_physical_screen_frame',) }) diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 38ea7ccde..ffb9398e8 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1096,7 +1096,7 @@ typedef struct GLFWLayerShellConfig { unsigned x_size_in_cells, x_size_in_pixels; unsigned y_size_in_cells, y_size_in_pixels; int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin; - int requested_exclusive_zone, hide_on_focus_loss; + int requested_exclusive_zone, hide_on_focus_loss, use_physical_screen_frame; unsigned override_exclusive_zone; void (*size_callback)(GLFWwindow *window, float xscale, float yscale, unsigned *cell_width, unsigned *cell_height, double *left_edge_spacing, double *top_edge_spacing, double *right_edge_spacing, double *bottom_edge_spacing); struct { float xscale, yscale; } expected; diff --git a/kitty/glfw.c b/kitty/glfw.c index c32a18d73..1d78bc71b 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1642,6 +1642,7 @@ layer_shell_config_to_python(const GLFWLayerShellConfig *c) { A(requested_right_margin, fl); A(requested_exclusive_zone, fl); A(hide_on_focus_loss, b) + A(use_physical_screen_frame, b) A(override_exclusive_zone, b); #undef A #undef fl @@ -1669,6 +1670,7 @@ layer_shell_config_from_python(PyObject *p, GLFWLayerShellConfig *ans) { A(requested_exclusive_zone, PyLong_Check, PyLong_AsLong); A(override_exclusive_zone, PyBool_Check, PyLong_AsLong); A(hide_on_focus_loss, PyBool_Check, PyLong_AsLong); + A(use_physical_screen_frame, PyBool_Check, PyLong_AsLong); #undef A #define A(attr) { \ RAII_PyObject(attr, PyObject_GetAttrString(p, #attr)); if (attr == NULL) return false; \ diff --git a/kitty/simple_cli_definitions.py b/kitty/simple_cli_definitions.py index bcb82b59e..78a305418 100644 --- a/kitty/simple_cli_definitions.py +++ b/kitty/simple_cli_definitions.py @@ -573,7 +573,7 @@ panel_defaults = { 'edge': 'top', 'layer': 'bottom', 'override': '', 'cls': f'{appname}-panel', 'focus_policy': 'not-allowed', 'exclusive_zone': '-1', 'override_exclusive_zone': 'no', 'single_instance': 'no', 'instance_group': '', 'toggle_visibility': 'no', - 'start_as_hidden': 'no', 'detach': 'no', 'detached_log': '', + 'start_as_hidden': 'no', 'detach': 'no', 'detached_log': '', 'use_physical_screen_frame': 'no', } def build_panel_cli_spec(defaults: dict[str, str]) -> str: @@ -647,6 +647,13 @@ on which the panel should be drawn. This parameter is ignored and set to these to appropriate NSWindow *levels*. +--use-physical-screen-frame +type=bool-set +default={use_physical_screen_frame} +On macOS, use the physical screen frame rather than the visible frame when placing the panel. +This allows top overlay panels to draw over the native menu bar. Ignored on other platforms. + + --config -c type=list Path to config file to use for kitty when drawing the panel. diff --git a/kitty/types.py b/kitty/types.py index d3fb4603d..b7df467a6 100644 --- a/kitty/types.py +++ b/kitty/types.py @@ -85,6 +85,7 @@ class LayerShellConfig(NamedTuple): requested_exclusive_zone: int = -1 override_exclusive_zone: bool = False hide_on_focus_loss: bool = False + use_physical_screen_frame: bool = False def mod_to_names(mods: int, has_kitty_mod: bool = False, kitty_mod: int = 0) -> Iterator[str]: