xdg-activation: focus windows by token
Let launchers focus existing windows via xdg-activation-v1. On request_activate, find the kick window owning the surface and switch to its tab (set_active_tab + window_enter, both gated on session_locked so activation can never bypass the lock screen). Add a --activate wltest mode (two windows; second steals focus, then a token activates the first) and two smoke tests proving the global is advertised and the inactive window receives an activated configure.
This commit is contained in:
parent
0475216913
commit
9ea0766bd3
4 changed files with 250 additions and 0 deletions
160
tests/wltest.c
160
tests/wltest.c
|
|
@ -29,6 +29,7 @@
|
|||
#include "wlr-output-power-management-unstable-v1-client.h"
|
||||
#include "virtual-keyboard-unstable-v1-client.h"
|
||||
#include "wlr-virtual-pointer-unstable-v1-client.h"
|
||||
#include "xdg-activation-v1-client.h"
|
||||
|
||||
/* Minimal deterministic xdg toplevel test client: shows a solid-color shm
|
||||
buffer and runs until killed. Used by test.sh to give the smoke test two
|
||||
|
|
@ -130,6 +131,7 @@ static struct zwp_keyboard_shortcuts_inhibit_manager_v1 *kb_inhibit_mgr;
|
|||
static struct zwlr_output_power_manager_v1 *power_mgr;
|
||||
static struct zwp_virtual_keyboard_manager_v1 *virtual_kb_mgr;
|
||||
static struct zwlr_virtual_pointer_manager_v1 *virtual_ptr_mgr;
|
||||
static struct xdg_activation_v1 *activation_mgr;
|
||||
static struct wl_output *wl_output;
|
||||
|
||||
struct foreign_state {
|
||||
|
|
@ -774,6 +776,11 @@ static void registry_global(void *data, struct wl_registry *reg, uint32_t name,
|
|||
virtual_ptr_mgr = wl_registry_bind(reg, name,
|
||||
&zwlr_virtual_pointer_manager_v1_interface,
|
||||
version < 1 ? version : 1);
|
||||
} else if (strcmp(interface,
|
||||
xdg_activation_v1_interface.name) == 0) {
|
||||
activation_mgr = wl_registry_bind(reg, name,
|
||||
&xdg_activation_v1_interface,
|
||||
version < 1 ? version : 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1574,6 +1581,144 @@ static void virtual_motion_run(void) {
|
|||
wl_display_roundtrip(display);
|
||||
}
|
||||
|
||||
/* --- Xdg-activation (xdg-activation-v1) ---
|
||||
--activate [--activate-out <file>]: prove a launcher can focus an existing
|
||||
inactive window by token. Opens win0 (maps focused), then win1 (maps and
|
||||
steals focus so win0 goes inactive), fetches a token, activates win0's
|
||||
surface, and writes "activated" once win0 receives an activated configure.
|
||||
Exits after. */
|
||||
static bool activate_mode;
|
||||
static const char *activate_out;
|
||||
static struct wl_surface *act_surf[2];
|
||||
static struct xdg_surface *act_xdg[2];
|
||||
static struct xdg_toplevel *act_top[2];
|
||||
static struct wl_buffer *act_buf[2];
|
||||
static bool act_active[2];
|
||||
static char act_token[256];
|
||||
static bool act_got_token;
|
||||
|
||||
static void act_note(const char *s) {
|
||||
if (activate_out) write_outfile(activate_out, s, strlen(s));
|
||||
}
|
||||
|
||||
static void act_make_buf(int i) {
|
||||
if (act_buf[i]) return;
|
||||
int W_ = 64, H_ = 64, stride = W_ * 4;
|
||||
size_t size = (size_t)stride * (size_t)H_;
|
||||
int fd;
|
||||
struct wl_shm_pool *pool = make_pool(size, &fd);
|
||||
if (!pool) return;
|
||||
uint8_t *d = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (d != MAP_FAILED) {
|
||||
memset(d, 0x80, size);
|
||||
munmap(d, size);
|
||||
}
|
||||
close(fd);
|
||||
act_buf[i] = wl_shm_pool_create_buffer(pool, 0, W_, H_, stride,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
}
|
||||
|
||||
static void act_xdg_configure(void *data, struct xdg_surface *xs,
|
||||
uint32_t serial) {
|
||||
int i = (int)(intptr_t)data;
|
||||
xdg_surface_ack_configure(xs, serial);
|
||||
act_make_buf(i);
|
||||
wl_surface_attach(act_surf[i], act_buf[i], 0, 0);
|
||||
wl_surface_commit(act_surf[i]);
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener act_xdg_listener = {
|
||||
.configure = act_xdg_configure,
|
||||
};
|
||||
|
||||
static void act_top_configure(void *data, struct xdg_toplevel *xp, int32_t w,
|
||||
int32_t h, struct wl_array *states) {
|
||||
(void)xp; (void)w; (void)h;
|
||||
int i = (int)(intptr_t)data;
|
||||
act_active[i] = false;
|
||||
for (const uint32_t *s = states->data;
|
||||
s < (const uint32_t *)((const char *)states->data + states->size);
|
||||
s++) {
|
||||
if (*s == (uint32_t)XDG_TOPLEVEL_STATE_ACTIVATED) {
|
||||
act_active[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener act_top_listener = {
|
||||
.configure = act_top_configure,
|
||||
.close = xdg_toplevel_close,
|
||||
};
|
||||
|
||||
static void act_token_done(void *data,
|
||||
struct xdg_activation_token_v1 *tok,
|
||||
const char *token) {
|
||||
(void)data; (void)tok;
|
||||
snprintf(act_token, sizeof(act_token), "%s", token ? token : "");
|
||||
act_got_token = true;
|
||||
}
|
||||
|
||||
static const struct xdg_activation_token_v1_listener act_token_listener = {
|
||||
.done = act_token_done,
|
||||
};
|
||||
|
||||
static void act_open(int i, const char *title) {
|
||||
act_surf[i] = wl_compositor_create_surface(compositor);
|
||||
act_xdg[i] = xdg_wm_base_get_xdg_surface(wm_base, act_surf[i]);
|
||||
act_top[i] = xdg_surface_get_toplevel(act_xdg[i]);
|
||||
xdg_toplevel_set_title(act_top[i], title);
|
||||
xdg_surface_add_listener(act_xdg[i], &act_xdg_listener,
|
||||
(void *)(intptr_t)i);
|
||||
xdg_toplevel_add_listener(act_top[i], &act_top_listener,
|
||||
(void *)(intptr_t)i);
|
||||
wl_surface_commit(act_surf[i]);
|
||||
}
|
||||
|
||||
static void activate_run(void) {
|
||||
display = wl_display_connect(NULL);
|
||||
if (!display) {
|
||||
fprintf(stderr, "wltest: activate: cannot connect\n");
|
||||
exit(1);
|
||||
}
|
||||
struct wl_registry *reg = wl_display_get_registry(display);
|
||||
wl_registry_add_listener(reg, ®istry_listener, NULL);
|
||||
wl_display_roundtrip(display);
|
||||
if (!activation_mgr) {
|
||||
fprintf(stderr, "wltest: activate: xdg_activation_v1 missing\n");
|
||||
exit(1);
|
||||
}
|
||||
xdg_wm_base_add_listener(wm_base, &wm_base_listener, NULL);
|
||||
/* win0 maps focused; win1 maps and steals focus so win0 is inactive. */
|
||||
act_open(0, "act0");
|
||||
wl_display_roundtrip(display);
|
||||
act_open(1, "act1");
|
||||
wl_display_roundtrip(display);
|
||||
/* Fetch a token (app_id hint only; no input serial needed). */
|
||||
struct xdg_activation_token_v1 *tok =
|
||||
xdg_activation_v1_get_activation_token(activation_mgr);
|
||||
xdg_activation_token_v1_add_listener(tok, &act_token_listener, NULL);
|
||||
xdg_activation_token_v1_set_app_id(tok, "wltest-act");
|
||||
xdg_activation_token_v1_commit(tok);
|
||||
while (!act_got_token) {
|
||||
if (wl_display_dispatch(display) < 0) {
|
||||
fprintf(stderr, "wltest: activate: no token\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
/* Activate the inactive win0; wait until it reports activated. */
|
||||
xdg_activation_v1_activate(activation_mgr, act_token, act_surf[0]);
|
||||
for (;;) {
|
||||
if (wl_display_dispatch(display) < 0)
|
||||
break;
|
||||
if (act_active[0]) {
|
||||
act_note("activated");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pull the current offer and act on it. In --primary-demand mode write the
|
||||
payload to the file and exit; in --primary-demand-expect mode keep waiting
|
||||
until the payload matches the expected text (the host may already hold its
|
||||
|
|
@ -1913,10 +2058,25 @@ int main(int argc, char **argv) {
|
|||
} else if (strcmp(argv[i], "--virtual-click") == 0) {
|
||||
virtual_click = true;
|
||||
i += 1;
|
||||
} else if (strcmp(argv[i], "--activate") == 0) {
|
||||
activate_mode = true;
|
||||
i += 1;
|
||||
} else if (strcmp(argv[i], "--activate-out") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "wltest: --activate-out needs a path\n");
|
||||
return 1;
|
||||
}
|
||||
activate_out = argv[i + 1];
|
||||
i += 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Xdg-activation: focus an inactive window by token, then exit. */
|
||||
if (activate_mode) {
|
||||
activate_run();
|
||||
return 0;
|
||||
}
|
||||
/* Virtual keyboard: synthesise keys, then exit. */
|
||||
if (virtual_key_spec) {
|
||||
virtual_key_run();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue