diff --git a/meson.build b/meson.build index c36d7644..b78079c3 100644 --- a/meson.build +++ b/meson.build @@ -95,6 +95,7 @@ foreach prot : [ wayland_protocols_datadir + '/unstable/primary-selection/primary-selection-unstable-v1.xml', wayland_protocols_datadir + '/stable/presentation-time/presentation-time.xml', wayland_protocols_datadir + '/unstable/text-input/text-input-unstable-v3.xml', + wayland_protocols_datadir + '/staging/xdg-activation/xdg-activation-v1.xml', ] wl_proto_headers += custom_target( diff --git a/terminal.c b/terminal.c index f3513a08..a9712789 100644 --- a/terminal.c +++ b/terminal.c @@ -2632,16 +2632,24 @@ term_bell(struct terminal *term) if (!term->kbd_focus) { if (term->conf->bell.urgent) { - /* There's no 'urgency' hint in Wayland - we just paint the - * margins red */ - term->render.urgency = true; - term_damage_margins(term); + if (!wayl_win_set_urgent(term->window)) { + /* + * Urgency (xdg-activation) is relatively new in + * Wayland. Fallback to our old, “faked”, urgency - + * rendering our window margins in red + */ + term->render.urgency = true; + term_damage_margins(term); + } } + if (term->conf->bell.notify) notify_notify(term, "Bell", "Bell in terminal"); } - if ((term->conf->bell.command.argv != NULL) && (!term->kbd_focus || term->conf->bell.command_focused)) { + if ((term->conf->bell.command.argv != NULL) && + (!term->kbd_focus || term->conf->bell.command_focused)) + { int devnull = open("/dev/null", O_RDONLY); spawn(term->reaper, NULL, term->conf->bell.command.argv, devnull, -1, -1); diff --git a/wayland.c b/wayland.c index 102b29c5..f7f89d72 100644 --- a/wayland.c +++ b/wayland.c @@ -12,13 +12,9 @@ #include #include -#include #include #include -#include -#include -#include #define LOG_MODULE "wayland" #define LOG_ENABLE_DBG 0 @@ -989,6 +985,15 @@ handle_global(void *data, struct wl_registry *registry, } } + else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) { + const uint32_t required = 1; + if (!verify_iface_version(interface, version, required)) + return; + + wayl->xdg_activation = wl_registry_bind( + wayl->registry, name, &xdg_activation_v1_interface, required); + } + #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) { const uint32_t required = 1; @@ -1187,6 +1192,12 @@ wayl_init(const struct config *conf, struct fdm *fdm) if (wayl->primary_selection_device_manager == NULL) LOG_WARN("no primary selection available"); + if (wayl->xdg_activation == NULL && conf->bell.urgent) { + LOG_WARN( + "no XDG activation support; " + "bell.urgent will fall back to coloring the window margins red"); + } + if (conf->presentation_timings && wayl->presentation == NULL) { LOG_ERR("presentation time interface not implemented by compositor"); goto out; @@ -1275,6 +1286,8 @@ wayl_destroy(struct wayland *wayl) zwp_text_input_manager_v3_destroy(wayl->text_input_manager); #endif + if (wayl->xdg_activation != NULL) + xdg_activation_v1_destroy(wayl->xdg_activation); if (wayl->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); if (wayl->shell != NULL) @@ -1452,6 +1465,8 @@ wayl_win_destroy(struct wl_window *win) wayl_win_subsurface_destroy(&win->scrollback_indicator); wayl_win_subsurface_destroy(&win->render_timer); + if (win->xdg_activation_token != NULL) + xdg_activation_token_v1_destroy(win->xdg_activation_token); if (win->frame_callback != NULL) wl_callback_destroy(win->frame_callback); if (win->xdg_toplevel_decoration != NULL) @@ -1573,6 +1588,50 @@ wayl_roundtrip(struct wayland *wayl) wayl_flush(wayl); } +static void +activation_token_done(void *data, struct xdg_activation_token_v1 *xdg_token, const char *token) +{ + struct wl_window *win = data; + struct wayland *wayl = win->term->wl; + + LOG_DBG("activation token: %s", token); + + xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface); + + xassert(win->xdg_activation_token == xdg_token); + xdg_activation_token_v1_destroy(xdg_token); + win->xdg_activation_token = NULL; +} + +static const struct xdg_activation_token_v1_listener activation_token_listener = { + .done = &activation_token_done, +}; + +bool +wayl_win_set_urgent(struct wl_window *win) +{ + struct wayland *wayl = win->term->wl; + + if (wayl->xdg_activation == NULL) + return false; + + if (win->xdg_activation_token != NULL) + return true; + + struct xdg_activation_token_v1 *token = + xdg_activation_v1_get_activation_token(wayl->xdg_activation); + + if (token == NULL) { + LOG_ERR("failed to retrieve XDG activation token"); + return false; + } + + xdg_activation_token_v1_add_listener(token, &activation_token_listener, win); + xdg_activation_token_v1_commit(token); + win->xdg_activation_token = token; + return true; +} + bool wayl_win_subsurface_new_with_custom_parent( struct wl_window *win, struct wl_surface *parent, diff --git a/wayland.h b/wayland.h index 0b916ccc..dd6637cd 100644 --- a/wayland.h +++ b/wayland.h @@ -8,8 +8,14 @@ #include #include -#include +/* Wayland protocols */ #include +#include +#include +#include +#include +#include +#include #include @@ -377,6 +383,7 @@ struct wl_window { struct wl_surface *surface; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; + struct xdg_activation_token_v1 *xdg_activation_token; struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration; @@ -444,6 +451,8 @@ struct wayland { struct wl_data_device_manager *data_device_manager; struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager; + struct xdg_activation_v1 *xdg_activation; + struct wp_presentation *presentation; uint32_t presentation_clock_id; @@ -469,6 +478,8 @@ void wayl_roundtrip(struct wayland *wayl); struct wl_window *wayl_win_init(struct terminal *term); void wayl_win_destroy(struct wl_window *win); +bool wayl_win_set_urgent(struct wl_window *win); + bool wayl_win_subsurface_new( struct wl_window *win, struct wl_surf_subsurf *surf); bool wayl_win_subsurface_new_with_custom_parent(