From 50f64d598191aac965b4c1d6a0318eba35b2fb31 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 12 Feb 2025 12:02:46 +0530 Subject: [PATCH] Only use window occlusion on platforms where it is supported --- docs/changelog.rst | 4 ++-- glfw/cocoa_init.m | 3 ++- glfw/glfw3.h | 2 +- glfw/init.c | 5 +++-- glfw/internal.h | 2 +- glfw/wl_init.c | 3 ++- glfw/x11_init.c | 3 ++- kitty/constants.py | 10 ++++++++++ kitty/fast_data_types.pyi | 2 +- kitty/glfw-wrapper.h | 2 +- kitty/glfw.c | 8 ++++---- kitty/main.py | 5 ++++- kitty/notifications.py | 11 +++++++---- kitty/options/definition.py | 4 ++-- 14 files changed, 42 insertions(+), 22 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index ffa99db85..1a80b543a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -107,8 +107,8 @@ Detailed list of changes many cells to render a piece of text in (:iss:`8226`) - **Behavior change**: The :opt:`notify_on_cmd_finish` option now uses OS - Window visibility instead of focus state when set to ``invisible`` - (:iss:`8320`) + Window visibility instead of focus state when set to ``invisible`` on + platforms that support querying OS window visibility (:iss:`8320`) - launch: Add options :option:`launch --source-window` and :option:`launch --next-to` to allow specifying which window is used as the data source and destination location independently of the diff --git a/glfw/cocoa_init.m b/glfw/cocoa_init.m index ffdf50957..4295d0887 100644 --- a/glfw/cocoa_init.m +++ b/glfw/cocoa_init.m @@ -791,10 +791,11 @@ GLFWAPI GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunch return previous; } -int _glfwPlatformInit(void) +int _glfwPlatformInit(bool *supports_window_occlusion) { @autoreleasepool { + *supports_window_occlusion = true; _glfw.ns.helper = [[GLFWHelper alloc] init]; [NSThread detachNewThreadSelector:@selector(doNothing:) diff --git a/glfw/glfw3.h b/glfw/glfw3.h index f1a30cbcb..f9119231d 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1964,7 +1964,7 @@ typedef struct GLFWgamepadstate * * @ingroup init */ -GLFWAPI int glfwInit(monotonic_t start_time); +GLFWAPI int glfwInit(monotonic_t start_time, bool *supports_window_occlusion); GLFWAPI void glfwRunMainLoop(GLFWtickcallback callback, void *callback_data); GLFWAPI void glfwStopMainLoop(void); GLFWAPI unsigned long long glfwAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void * callback_data, GLFWuserdatafun free_callback); diff --git a/glfw/init.c b/glfw/init.c index 470eef49a..9315d215a 100644 --- a/glfw/init.c +++ b/glfw/init.c @@ -223,8 +223,9 @@ _glfwDebug(const char *format, ...) { ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// -GLFWAPI int glfwInit(monotonic_t start_time) +GLFWAPI int glfwInit(monotonic_t start_time, bool *supports_window_occlusion) { + *supports_window_occlusion = false; if (_glfw.initialized) return true; monotonic_start_time = start_time; @@ -233,7 +234,7 @@ GLFWAPI int glfwInit(monotonic_t start_time) _glfw.hints.init = _glfwInitHints; _glfw.ignoreOSKeyboardProcessing = false; - if (!_glfwPlatformInit()) + if (!_glfwPlatformInit(supports_window_occlusion)) { terminate(); return false; diff --git a/glfw/internal.h b/glfw/internal.h index d30bee73c..da6035c86 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -672,7 +672,7 @@ extern _GLFWlibrary _glfw; ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// -int _glfwPlatformInit(void); +int _glfwPlatformInit(bool*); void _glfwPlatformTerminate(void); const char* _glfwPlatformGetVersionString(void); diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 5063353da..8e134db57 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -721,7 +721,7 @@ get_compositor_missing_capabilities(void) { GLFWAPI const char* glfwWaylandMissingCapabilities(void) { return get_compositor_missing_capabilities(); } -int _glfwPlatformInit(void) +int _glfwPlatformInit(bool *supports_window_occlusion) { int i; _GLFWmonitor* monitor; @@ -812,6 +812,7 @@ int _glfwPlatformInit(void) const char *mc = get_compositor_missing_capabilities(); if (mc && mc[0]) debug("Compositor missing capabilities: %s\n", mc); } + *supports_window_occlusion = _glfw.wl.xdg_wm_base_version > 5; return true; } diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 219ef61ab..2e4ae7d2c 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -621,8 +621,9 @@ GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialize void _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { } -int _glfwPlatformInit(void) +int _glfwPlatformInit(bool *supports_window_occlusion) { + *supports_window_occlusion = false; XInitThreads(); XrmInitialize(); diff --git a/kitty/constants.py b/kitty/constants.py index ef92f537d..c482a2d87 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -330,3 +330,13 @@ def local_docs() -> str: def wrapped_kitten_names() -> frozenset[str]: import kitty.fast_data_types as f return frozenset(f.wrapped_kitten_names()) + + +_supports_window_occlusion = False + + +def supports_window_occlusion(set: bool | None = None) -> bool: + global _supports_window_occlusion + if set is not None: + _supports_window_occlusion = set + return _supports_window_occlusion diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 36e48af4e..6a77edefe 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -677,7 +677,7 @@ def glfw_terminate() -> None: def glfw_init( path: str, edge_spacing_func: Callable[[EdgeLiteral], float], debug_keyboard: bool = False, debug_rendering: bool = False, wayland_enable_ime: bool = True -) -> bool: +) -> tuple[bool, bool]: pass diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 8b2a0a991..543e5d00f 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1713,7 +1713,7 @@ typedef void (* GLFWcocoarenderframefun)(GLFWwindow*); typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id); typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*); typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, int, const char*); -typedef int (*glfwInit_func)(monotonic_t); +typedef int (*glfwInit_func)(monotonic_t, bool*); GFW_EXTERN glfwInit_func glfwInit_impl; #define glfwInit glfwInit_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 9bc1ffdfa..8657bef7e 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1503,8 +1503,9 @@ glfw_init(PyObject UNUSED *self, PyObject *args) { glfwDBusSetUserNotificationHandler(dbus_user_notification_activated); } #endif - PyObject *ans = glfwInit(monotonic_start_time) ? Py_True: Py_False; - if (ans == Py_True) { + bool supports_window_occlusion = false; + bool ok = glfwInit(monotonic_start_time, &supports_window_occlusion); + if (ok) { #ifdef __APPLE__ glfwSetCocoaURLOpenCallback(apple_url_open_callback); #else @@ -1513,8 +1514,7 @@ glfw_init(PyObject UNUSED *self, PyObject *args) { get_window_dpi(NULL, &global_state.default_dpi.x, &global_state.default_dpi.y); edge_spacing_func = edge_sf; Py_INCREF(edge_spacing_func); } - Py_INCREF(ans); - return ans; + return Py_BuildValue("OO", ok ? Py_True : Py_False, supports_window_occlusion ? Py_True : Py_False); } static PyObject* diff --git a/kitty/main.py b/kitty/main.py index ca6045a4e..930ea59ce 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -28,6 +28,7 @@ from .constants import ( kitty_exe, logo_png_file, running_in_kitty, + supports_window_occlusion, website_url, ) from .fast_data_types import ( @@ -89,8 +90,10 @@ def load_all_shaders(semi_transparent: bool = False) -> None: def init_glfw_module(glfw_module: str, debug_keyboard: bool = False, debug_rendering: bool = False, wayland_enable_ime: bool = True) -> None: - if not glfw_init(glfw_path(glfw_module), edge_spacing, debug_keyboard, debug_rendering, wayland_enable_ime): + ok, swo = glfw_init(glfw_path(glfw_module), edge_spacing, debug_keyboard, debug_rendering, wayland_enable_ime) + if not ok: raise SystemExit('GLFW initialization failed') + supports_window_occlusion(swo) def init_glfw(opts: Options, debug_keyboard: bool = False, debug_rendering: bool = False) -> str: diff --git a/kitty/notifications.py b/kitty/notifications.py index c3a6637c4..3dcd1b0f3 100644 --- a/kitty/notifications.py +++ b/kitty/notifications.py @@ -12,7 +12,7 @@ from itertools import count from typing import Any, NamedTuple, Set from weakref import ReferenceType, ref -from .constants import cache_dir, config_dir, is_macos, logo_png_file, standard_icon_names, standard_sound_names +from .constants import cache_dir, config_dir, is_macos, logo_png_file, standard_icon_names, standard_sound_names, supports_window_occlusion from .fast_data_types import ( ESC_OSC, StreamingBase64Decoder, @@ -801,9 +801,12 @@ class Channel: has_focus = is_visible = False boss = get_boss() if w := self.window_for_id(channel_id): - has_focus = w.is_active and w.os_window_id == current_focused_os_window_id() - # window is in the active OS window and the active tab and is visible in the tab layout - is_visible = not os_window_is_invisible(w.os_window_id) and w.tabref() is boss.active_tab and w.is_visible_in_layout + os_window_active = w.os_window_id == current_focused_os_window_id() + has_focus = w.is_active and os_window_active + is_visible = os_window_active + if supports_window_occlusion(): + is_visible = not os_window_is_invisible(w.os_window_id) + is_visible = is_visible and w.tabref() is boss.active_tab and w.is_visible_in_layout return UIState(has_focus, is_visible) def send(self, channel_id: int, osc_escape_code: str) -> bool: diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 759b1a0b1..43a1667f6 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -3306,8 +3306,8 @@ The possible values are: :code:`invisible` Only send a notification when the window both is unfocused and not visible to the user, for example, because it is in an inactive tab or its OS window - is not currently visible (note that OS Window visibility is not implemented - by some Wayland compositors such as sway and also not implemented in X11). + is not currently visible (on platforms that support OS window visibility querying + this considers an OS Window visible iff it is active). :code:`always` Always send a notification, regardless of window state.