Wayland: Generate a XDG_ACTIVATION_TOKEN when opening URLs or running programs in the background via the launch action

This commit is contained in:
Kovid Goyal 2022-09-11 14:25:49 +05:30
parent 58a3baaf0f
commit dcf2152a7a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
10 changed files with 89 additions and 17 deletions

View File

@ -53,6 +53,8 @@ Detailed list of changes
- Wayland: Fix for a bug preventing kitty from starting on Hyprland when using a non-unit scale (:iss:`5467`)
- Wayland: Generate a XDG_ACTIVATION_TOKEN when opening URLs or running programs in the background via the launch action
0.26.2 [2022-09-05]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -246,6 +246,7 @@ def generate_wrappers(glfw_header: str) -> None:
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)
void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data)
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)

6
glfw/wl_window.c vendored
View File

@ -2223,6 +2223,12 @@ GLFWAPI void glfwWaylandActivateWindow(GLFWwindow* handle, const char *activatio
if (activation_token && activation_token[0]) xdg_activation_v1_activate(_glfw.wl.xdg_activation_v1, activation_token, window->wl.surface);
}
GLFWAPI void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data) {
_GLFWwindow* window = (_GLFWwindow*) handle;
_GLFW_REQUIRE_INIT();
get_activation_token(window, _glfw.wl.input_serial, cb, cb_data);
}
GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
return glfw_xkb_keysym_from_name(keyName, caseSensitive);
}

View File

@ -20,7 +20,10 @@ from weakref import WeakValueDictionary
from .child import cached_process_data, default_env, set_default_env
from .cli import create_opts, parse_args
from .cli_stub import CLIOptions
from .clipboard import Clipboard, get_primary_selection, set_primary_selection, get_clipboard_string, set_clipboard_string
from .clipboard import (
Clipboard, get_clipboard_string, get_primary_selection,
set_clipboard_string, set_primary_selection
)
from .conf.utils import BadLine, KeyAction, to_cmdline
from .config import common_opts_as_dict, prepare_config_file_for_editing
from .constants import (
@ -38,11 +41,11 @@ from .fast_data_types import (
current_application_quit_request, current_os_window, destroy_global_data,
focus_os_window, get_boss, get_options, get_os_window_size,
global_font_size, mark_os_window_for_close, os_window_font_size,
patch_global_colors, redirect_mouse_handling, ring_bell, safe_pipe,
send_data_to_peer, set_application_quit_request, set_background_image,
set_boss, set_in_sequence_mode, set_options, set_os_window_size,
set_os_window_title, thread_write, toggle_fullscreen, toggle_maximized,
toggle_secure_input
patch_global_colors, redirect_mouse_handling, ring_bell,
run_with_activation_token, safe_pipe, send_data_to_peer,
set_application_quit_request, set_background_image, set_boss,
set_in_sequence_mode, set_options, set_os_window_size, set_os_window_title,
thread_write, toggle_fullscreen, toggle_maximized, toggle_secure_input
)
from .key_encoding import get_name_to_functional_number_map
from .keys import get_shortcut, shortcut_matches
@ -1751,7 +1754,16 @@ class Boss:
extra_env = {}
if self.listening_on:
extra_env['KITTY_LISTEN_ON'] = self.listening_on
open_url(url, program or get_options().open_url_with, cwd=cwd, extra_env=extra_env)
def doit(activation_token: str = '') -> None:
if activation_token:
extra_env['XDG_ACTIVATION_TOKEN'] = activation_token
open_url(url, program or get_options().open_url_with, cwd=cwd, extra_env=extra_env)
if is_wayland():
run_with_activation_token(doit)
else:
doit()
@ac('misc', 'Click a URL using the keyboard')
def open_url_with_hints(self) -> None:
@ -1945,18 +1957,28 @@ class Boss:
with suppress(Exception):
cwd = cwd_from.cwd_of_child
if stdin:
r, w = safe_pipe(False)
try:
subprocess.Popen(cmd, env=env, stdin=r, cwd=cwd, preexec_fn=clear_handled_signals)
except Exception:
os.close(w)
def doit(activation_token: str = '') -> None:
nonlocal env
if activation_token:
if env is None:
env = default_env().copy()
env['XDG_ACTIVATION_TOKEN'] = activation_token
if stdin:
r, w = safe_pipe(False)
try:
subprocess.Popen(cmd, env=env, stdin=r, cwd=cwd, preexec_fn=clear_handled_signals)
except Exception:
os.close(w)
else:
thread_write(w, stdin)
finally:
os.close(r)
else:
thread_write(w, stdin)
finally:
os.close(r)
subprocess.Popen(cmd, env=env, cwd=cwd, preexec_fn=clear_handled_signals)
if is_wayland():
run_with_activation_token(doit)
else:
subprocess.Popen(cmd, env=env, cwd=cwd, preexec_fn=clear_handled_signals)
doit()
def pipe(self, source: str, dest: str, exe: str, *args: str) -> Optional[Window]:
cmd = [exe] + list(args)

View File

@ -1474,3 +1474,4 @@ def get_docs_ref_map() -> bytes: ...
def clearenv() -> None: ...
def set_clipboard_data_types(ct: int, mime_types: Tuple[str, ...]) -> None: ...
def get_clipboard_mime(ct: int, mime: Optional[str], callback: Callable[[bytes], None]) -> None: ...
def run_with_activation_token(func: Callable[[str], None]) -> None: ...

3
kitty/glfw-wrapper.c generated
View File

@ -452,6 +452,9 @@ load_glfw(const char* path) {
*(void **) (&glfwWaylandActivateWindow_impl) = dlsym(handle, "glfwWaylandActivateWindow");
if (glfwWaylandActivateWindow_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwWaylandRunWithActivationToken_impl) = dlsym(handle, "glfwWaylandRunWithActivationToken");
if (glfwWaylandRunWithActivationToken_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwWaylandSetTitlebarColor_impl) = dlsym(handle, "glfwWaylandSetTitlebarColor");
if (glfwWaylandSetTitlebarColor_impl == NULL) dlerror(); // clear error indicator

4
kitty/glfw-wrapper.h generated
View File

@ -2210,6 +2210,10 @@ typedef void (*glfwWaylandActivateWindow_func)(GLFWwindow*, const char*);
GFW_EXTERN glfwWaylandActivateWindow_func glfwWaylandActivateWindow_impl;
#define glfwWaylandActivateWindow glfwWaylandActivateWindow_impl
typedef void (*glfwWaylandRunWithActivationToken_func)(GLFWwindow*, GLFWactivationcallback, void*);
GFW_EXTERN glfwWaylandRunWithActivationToken_func glfwWaylandRunWithActivationToken_impl;
#define glfwWaylandRunWithActivationToken glfwWaylandRunWithActivationToken_impl
typedef bool (*glfwWaylandSetTitlebarColor_func)(GLFWwindow*, uint32_t, bool);
GFW_EXTERN glfwWaylandSetTitlebarColor_func glfwWaylandSetTitlebarColor_impl;
#define glfwWaylandSetTitlebarColor glfwWaylandSetTitlebarColor_impl

View File

@ -1317,6 +1317,26 @@ find_os_window(id_type os_window_id) {
return NULL;
}
static void
activation_token_callback(GLFWwindow *window UNUSED, const char *token, void *data) {
if (!token || !token[0]) {
token = "";
log_error("Wayland: Did not get activation token from compositor. Use a better compositor.");
}
PyObject *ret = PyObject_CallFunction(data, "s", token);
if (ret == NULL) PyErr_Print();
else Py_DECREF(ret);
Py_CLEAR(data);
}
void
run_with_activation_token_in_os_window(OSWindow *w, PyObject *callback) {
if (global_state.is_wayland) {
Py_INCREF(callback);
glfwWaylandRunWithActivationToken(w->handle, activation_token_callback, callback);
}
}
static PyObject*
toggle_fullscreen(PyObject UNUSED *self, PyObject *args) {
id_type os_window_id = 0;

View File

@ -831,6 +831,17 @@ PYWRAP1(focus_os_window) {
Py_RETURN_FALSE;
}
PYWRAP1(run_with_activation_token) {
for (size_t o = 0; o < global_state.num_os_windows; o++) {
OSWindow *os_window = global_state.os_windows + o;
if (os_window->is_focused) {
run_with_activation_token_in_os_window(os_window, args);
break;
}
}
Py_RETURN_NONE;
}
PYWRAP1(set_titlebar_color) {
id_type os_window_id;
unsigned int color;
@ -1292,6 +1303,7 @@ static PyMethodDef module_methods[] = {
MW(set_titlebar_color, METH_VARARGS),
MW(focus_os_window, METH_VARARGS),
MW(mark_tab_bar_dirty, METH_O),
MW(run_with_activation_token, METH_O),
MW(change_background_opacity, METH_VARARGS),
MW(background_opacity_of, METH_O),
MW(update_window_visibility, METH_VARARGS),

View File

@ -271,6 +271,7 @@ 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, const char *activation_token);
void run_with_activation_token_in_os_window(OSWindow *w, PyObject *callback);
void set_os_window_title(OSWindow *w, const char *title);
OSWindow* os_window_for_kitty_window(id_type);
OSWindow* add_os_window(void);