From 58a3baaf0fe171ea370f2de7607a12ca604c46b7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Sep 2022 13:30:14 +0530 Subject: [PATCH] Wayland: Use XDG_ACTIVATION_TOKEN when present at launch --- glfw/glfw.py | 1 + glfw/wl_window.c | 6 ++++++ kitty/boss.py | 14 ++++++++++---- kitty/child.py | 1 + kitty/fast_data_types.pyi | 2 +- kitty/glfw-wrapper.c | 3 +++ kitty/glfw-wrapper.h | 5 +++++ kitty/glfw.c | 8 ++++++-- kitty/main.py | 2 +- kitty/mouse.c | 2 +- kitty/state.c | 5 +++-- kitty/state.h | 2 +- 12 files changed, 39 insertions(+), 12 deletions(-) diff --git a/glfw/glfw.py b/glfw/glfw.py index 32529a7e7..8558ab90d 100755 --- a/glfw/glfw.py +++ b/glfw/glfw.py @@ -245,6 +245,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) + void glfwWaylandActivateWindow(GLFWwindow *handle, const char *activation_token) 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) diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 6def91bee..eacc86c78 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -2217,6 +2217,12 @@ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) return window->wl.surface; } +GLFWAPI void glfwWaylandActivateWindow(GLFWwindow* handle, const char *activation_token) { + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT(); + if (activation_token && activation_token[0]) xdg_activation_v1_activate(_glfw.wl.xdg_activation_v1, activation_token, window->wl.surface); +} + GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) { return glfw_xkb_keysym_from_name(keyName, caseSensitive); } diff --git a/kitty/boss.py b/kitty/boss.py index 3b3498ce1..1e543eb4d 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -296,7 +296,8 @@ class Boss: def startup_first_child(self, os_window_id: Optional[int], startup_sessions: Iterable[Session] = ()) -> None: si = startup_sessions or create_sessions(get_options(), self.args, default_session=get_options().startup_session) - focused_os_window = 0 + focused_os_window = wid = 0 + token = os.environ.pop('XDG_ACTIVATION_TOKEN', '') for startup_session in si: wid = self.add_os_window(startup_session, os_window_id=os_window_id) if startup_session.focus_os_window: @@ -308,7 +309,9 @@ class Boss: else: change_os_window_state(self.args.start_as) if focused_os_window > 0: - focus_os_window(focused_os_window, True) + focus_os_window(focused_os_window, True, token) + elif token and is_wayland() and wid: + focus_os_window(wid, True, token) def add_os_window( self, @@ -630,6 +633,7 @@ class Boss: if isinstance(data, dict) and data.get('cmd') == 'new_instance': from .cli_stub import CLIOptions startup_id = data.get('startup_id') + activation_token = data.get('activation_token', '') args, rest = parse_args(data['args'][1:], result_class=CLIOptions) cmdline_args_for_open = data.get('cmdline_args_for_open') if cmdline_args_for_open: @@ -642,7 +646,7 @@ class Boss: args.session = PreReadSession(data['stdin']) if not os.path.isabs(args.directory): args.directory = os.path.join(data['cwd'], args.directory) - focused_os_window = 0 + focused_os_window = os_window_id = 0 for session in create_sessions(opts, args, respect_cwd=True): os_window_id = self.add_os_window( session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id, @@ -654,7 +658,9 @@ class Boss: if data.get('notify_on_os_window_death'): self.os_window_death_actions[os_window_id] = partial(self.notify_on_os_window_death, data['notify_on_os_window_death']) if focused_os_window > 0: - focus_os_window(focused_os_window, True) + focus_os_window(focused_os_window, True, activation_token or '') + elif activation_token and is_wayland() and os_window_id: + focus_os_window(os_window_id, True, activation_token or '') else: log_error('Unknown message received from peer, ignoring') return None diff --git a/kitty/child.py b/kitty/child.py index b5152d04b..c0561907a 100644 --- a/kitty/child.py +++ b/kitty/child.py @@ -148,6 +148,7 @@ def process_env() -> Dict[str, str]: ssl_env_var = getattr(sys, 'kitty_ssl_env_var', None) if ssl_env_var is not None: ans.pop(ssl_env_var, None) + ans.pop('XDG_ACTIVATION_TOKEN', None) return ans diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index f9938c6ac..f89621576 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -754,7 +754,7 @@ def global_font_size(val: float = -1.) -> float: pass -def focus_os_window(os_window_id: int, also_raise: bool = True) -> bool: +def focus_os_window(os_window_id: int, also_raise: bool = True, activation_token: Optional[str] = None) -> bool: pass diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 46c3d8048..038099338 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -449,6 +449,9 @@ load_glfw(const char* path) { *(void **) (&glfwRequestWaylandFrameEvent_impl) = dlsym(handle, "glfwRequestWaylandFrameEvent"); if (glfwRequestWaylandFrameEvent_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwWaylandActivateWindow_impl) = dlsym(handle, "glfwWaylandActivateWindow"); + if (glfwWaylandActivateWindow_impl == NULL) dlerror(); // clear error indicator + *(void **) (&glfwWaylandSetTitlebarColor_impl) = dlsym(handle, "glfwWaylandSetTitlebarColor"); if (glfwWaylandSetTitlebarColor_impl == NULL) dlerror(); // clear error indicator diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 43cc3e6f7..33e4bbc0b 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1454,6 +1454,7 @@ typedef void (* GLFWjoystickfun)(int,int); typedef void (* GLFWuserdatafun)(unsigned long long, void*); typedef void (* GLFWtickcallback)(void*); +typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data); typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin); typedef char* (* GLFWcurrentselectionfun)(void); typedef void (* GLFWclipboarddatafreefun)(void* data); @@ -2205,6 +2206,10 @@ typedef void (*glfwRequestWaylandFrameEvent_func)(GLFWwindow*, unsigned long lon GFW_EXTERN glfwRequestWaylandFrameEvent_func glfwRequestWaylandFrameEvent_impl; #define glfwRequestWaylandFrameEvent glfwRequestWaylandFrameEvent_impl +typedef void (*glfwWaylandActivateWindow_func)(GLFWwindow*, const char*); +GFW_EXTERN glfwWaylandActivateWindow_func glfwWaylandActivateWindow_impl; +#define glfwWaylandActivateWindow glfwWaylandActivateWindow_impl + typedef bool (*glfwWaylandSetTitlebarColor_func)(GLFWwindow*, uint32_t, bool); GFW_EXTERN glfwWaylandSetTitlebarColor_func glfwWaylandSetTitlebarColor_impl; #define glfwWaylandSetTitlebarColor glfwWaylandSetTitlebarColor_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index ea489a3b0..82ed7e0ce 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1037,13 +1037,17 @@ destroy_os_window(OSWindow *w) { } void -focus_os_window(OSWindow *w, bool also_raise) { +focus_os_window(OSWindow *w, bool also_raise, const char *activation_token) { if (w->handle) { #ifdef __APPLE__ if (!also_raise) cocoa_focus_window(glfwGetCocoaWindow(w->handle)); else glfwFocusWindow(w->handle); + (void)activation_token; #else - (void)also_raise; + if (global_state.is_wayland && activation_token && activation_token[0] && also_raise) { + glfwWaylandActivateWindow(w->handle, activation_token); + return; + } glfwFocusWindow(w->handle); #endif } diff --git a/kitty/main.py b/kitty/main.py index ad5d63820..f078caa6c 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -61,7 +61,7 @@ def talk_to_instance(args: CLIOptions) -> None: if args.session == '-': stdin = sys.stdin.read() data = {'cmd': 'new_instance', 'args': tuple(sys.argv), 'cmdline_args_for_open': getattr(sys, 'cmdline_args_for_open', []), - 'startup_id': os.environ.get('DESKTOP_STARTUP_ID'), + 'startup_id': os.environ.get('DESKTOP_STARTUP_ID'), 'activation_token': os.environ.get('XDG_ACTIVATION_TOKEN'), 'cwd': os.getcwd(), 'stdin': stdin} notify_socket = None if args.wait_for_single_instance_window_close: diff --git a/kitty/mouse.c b/kitty/mouse.c index 821d9a07b..dd610e31f 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -653,7 +653,7 @@ enter_event() { // On cocoa there is no way to configure the window manager to // focus windows on mouse enter, so we do it ourselves if (OPT(focus_follows_mouse) && !global_state.callback_os_window->is_focused) { - focus_os_window(global_state.callback_os_window, false); + focus_os_window(global_state.callback_os_window, false, NULL); } #endif } diff --git a/kitty/state.c b/kitty/state.c index c51327ce4..4012803d8 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -822,9 +822,10 @@ PYWRAP0(current_application_quit_request) { PYWRAP1(focus_os_window) { id_type os_window_id; int also_raise = 1; - PA("K|p", &os_window_id, &also_raise); + const char *activation_token = NULL; + PA("K|pz", &os_window_id, &also_raise, &activation_token); WITH_OS_WINDOW(os_window_id) - if (!os_window->is_focused) focus_os_window(os_window, also_raise); + if (!os_window->is_focused || (activation_token && activation_token[0])) focus_os_window(os_window, also_raise, activation_token); Py_RETURN_TRUE; END_WITH_OS_WINDOW Py_RETURN_FALSE; diff --git a/kitty/state.h b/kitty/state.h index 6b1526527..b4ef7b8b8 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -270,7 +270,7 @@ bool make_window_context_current(id_type); void hide_mouse(OSWindow *w); bool is_mouse_hidden(OSWindow *w); void destroy_os_window(OSWindow *w); -void focus_os_window(OSWindow *w, bool also_raise); +void focus_os_window(OSWindow *w, bool also_raise, const char *activation_token); void set_os_window_title(OSWindow *w, const char *title); OSWindow* os_window_for_kitty_window(id_type); OSWindow* add_os_window(void);