-
Notifications
You must be signed in to change notification settings - Fork 188
Description
Problem description
I think this is probably a duplicate of #2723, but I'm not entirely sure. If it's not a duplicate, it's at least very likely related.
If:
- an application that uses the
virtual-keyboard-unstable-v1.xmlprotocol is running and has a virtual keyboard actively attached to the compositor, and - the compositor has not yet received a keystroke event originating from the machine's physical keyboard, and
- the user (somehow) modifies
~/.config/labwc/environmentand sets a different keyboard layout with theXKB_DEFAULT_LAYOUToption, and - the user then attempts to make that keyboard layout change take effect by running
labwc --reconfigure,
then labwc will not broadcast the keyboard layout change to all running applications. The user must press a physical key on the keyboard in order to cause the new keyboard layout to be broadcast to all applications.
This sounds like a ridiculous edge case no one will ever hit, especially since pressing a single key on the system's physical keyboard solves the issue, but there is one instance in which this can pose a serious problem, and that is when you have a Wayland client running that grabs all keyboard input devices on the system and proxies them to the compositor via the virtual-keyboard-unstable-v1.xml protocol. kloak is one such application. Because it both provides a virtual keyboard to labwc, and grabs exclusive access to all input devices, labwc doesn't broadcast keyboard layout changes because of the virtual keyboard, and will never start broadcasting them because it no longer is receiving keystrokes from the physical keyboard.
Steps to reproduce
There's probably an eaasier way to reproduce this, but the way I came up with was to use a custom Wayland client. I wrote this initially trying to figure out where the bug was in kloak, before discovering the issue was in labwc.
- Create a directory at
~/virt-kb-test. - Paste the following code into
~/virt-kb-test/kb_test.c:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <linux/input.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <wayland-client.h>
#include "virtual-keyboard.h"
struct disp_state {
struct wl_display *display;
struct wl_registry *registry;
struct wl_seat *seat;
struct wl_keyboard *kb;
uint32_t seat_caps;
bool seat_set;
struct zwp_virtual_keyboard_manager_v1 *virt_kb_manager;
struct zwp_virtual_keyboard_v1 *virt_kb;
char *old_kb_map_shm;
int32_t old_kb_map_shm_size;
};
static void registry_handle_global(void *data, struct wl_registry * registry,
uint32_t name, const char * interface, uint32_t version);
static void registry_handle_global_remove(void *data,
struct wl_registry * registry, uint32_t name);
static void seat_handle_name(void *data, struct wl_seat *seat,
const char *name);
static void seat_handle_capabilities(void *data, struct wl_seat *seat,
uint32_t capabilities);
static void kb_handle_keymap(void *data, struct wl_keyboard *kb,
uint32_t format, int32_t fd, uint32_t size);
static void kb_handle_enter(void *data, struct wl_keyboard *kb,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys);
static void kb_handle_leave(void *data, struct wl_keyboard *kb,
uint32_t serial, struct wl_surface *surface);
static void kb_handle_key(void *data, struct wl_keyboard *kb, uint32_t serial,
uint32_t time, uint32_t key, uint32_t state);
static void kb_handle_modifiers(void *data, struct wl_keyboard *kb,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group);
static void kb_handle_repeat_info(void *data, struct wl_keyboard *kb,
int32_t rate, int32_t delay);
static const struct wl_registry_listener registry_listener = {
.global = registry_handle_global,
.global_remove = registry_handle_global_remove,
};
static const struct wl_seat_listener seat_listener = {
.name = seat_handle_name,
.capabilities = seat_handle_capabilities,
};
static const struct wl_keyboard_listener kb_listener = {
.keymap = kb_handle_keymap,
.enter = kb_handle_enter,
.leave = kb_handle_leave,
.key = kb_handle_key,
.modifiers = kb_handle_modifiers,
.repeat_info = kb_handle_repeat_info,
};
struct disp_state state = { 0 };
static void registry_handle_global(void *data, struct wl_registry * registry,
uint32_t name, const char * interface, uint32_t version) {
struct disp_state *param_state = data;
if (strcmp(interface, wl_seat_interface.name) == 0) {
if (!param_state->seat_set) {
param_state->seat = wl_registry_bind(registry, name, &wl_seat_interface, 9);
wl_seat_add_listener(param_state->seat, &seat_listener, &state);
param_state->seat_set = true;
} else {
fprintf(stderr,
"WARNING: Multiple seats detected, all but first will be ignored.\n");
}
} else if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0) {
param_state->virt_kb_manager = wl_registry_bind(registry, name, &zwp_virtual_keyboard_manager_v1_interface, 1);
}
}
static void registry_handle_global_remove(void *data,
struct wl_registry * registry, uint32_t name) {
;
}
static void seat_handle_name(void *data, struct wl_seat *seat,
const char *name) {
;
}
static void seat_handle_capabilities(void *data, struct wl_seat *seat,
uint32_t capabilities) {
struct disp_state *param_state = data;
param_state->seat_caps = capabilities;
if (capabilities | WL_SEAT_CAPABILITY_KEYBOARD) {
param_state->kb = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(param_state->kb, &kb_listener, param_state);
} else {
fprintf(stderr,
"FATAL ERROR: No keyboard capability for seat, cannot continue.\n");
exit(1);
}
}
static void kb_handle_keymap(void *data, struct wl_keyboard *kb,
uint32_t format, int32_t fd, uint32_t size) {
fprintf(stderr, "Keymap changed!\n");
if (state.virt_kb == NULL && state.virt_kb_manager != NULL) {
state.virt_kb = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
state.virt_kb_manager, state.seat);
}
char *kb_map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (kb_map_shm == MAP_FAILED) {
fprintf(stderr, "FATAL ERROR: Could nto mmap xkb layout!\n");
exit(1);
}
if (state.old_kb_map_shm) {
if (size == state.old_kb_map_shm_size && memcmp(state.old_kb_map_shm, kb_map_shm, size) == 0) {
munmap(kb_map_shm, size);
close(fd);
return;
} else {
munmap(state.old_kb_map_shm, (size_t)(state.old_kb_map_shm_size));
}
}
if (state.virt_kb != NULL) {
zwp_virtual_keyboard_v1_keymap(state.virt_kb, format, fd, size);
}
state.old_kb_map_shm = kb_map_shm;
state.old_kb_map_shm_size = (int32_t)(size);
close(fd);
}
static void kb_handle_enter(void *data, struct wl_keyboard *kb,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
wl_array_release(keys);
}
static void kb_handle_leave(void *data, struct wl_keyboard *kb,
uint32_t serial, struct wl_surface *surface) {
;
}
static void kb_handle_key(void *data, struct wl_keyboard *kb, uint32_t serial,
uint32_t time, uint32_t key, uint32_t state) {
;
}
static void kb_handle_modifiers(void *data, struct wl_keyboard *kb,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
;
}
static void kb_handle_repeat_info(void *data, struct wl_keyboard *kb,
int32_t rate, int32_t delay) {
;
}
int main(int argc, char **argv) {
state.virt_kb = NULL;
state.virt_kb_manager = NULL;
state.display = wl_display_connect(NULL);
if (!state.display) {
fprintf(stderr, "FATAL ERROR: Could not get Wayland display!\n");
exit(1);
}
state.registry = wl_display_get_registry(state.display);
if (!state.registry) {
fprintf(stderr, "FATAL ERROR: Could not get Wayland registry!\n");
exit(1);
}
wl_registry_add_listener(state.registry, ®istry_listener, &state);
wl_display_roundtrip(state.display);
while (wl_display_dispatch(state.display)) {
;
}
}- Preferably review the above code before running it since it's always a bad idea to trust random code from strangers on the Internet. :P
- Obtain
virtual-keyboard-unstable-v1.xmlfrom wlroots, i.e. from https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/5adf325333602d5b1e7ccdeb122633bbc8040ace/protocol/virtual-keyboard-unstable-v1.xml - Create virtual-keyboard.c:
wayland-scanner private-code virtual-keyboard-unstable-v1.xml virtual-keyboard.c - Create virtual-keyboard.h:
wayland-scanner client-header virtual-keyboard-unstable-v1.xml virtual-keyboard.c - Compile the client:
gcc kb_test.c virtual-keyboard.c -lwayland-client - Before executing the client, SSH into the machine.
- In the SSH console, run
export LABWC_PID="$(pgrep labwc)"so you can reconfigure the compositor from here later. - On the physical machine, ensure you are in the
virt-kb-testdirectory, then runsleep 1; ./a.out. Do not touch the physical system's keyboard after starting the program, if you do you will have to stop and restart the program to reproduce the issue. - Wait for a couple of "Keymap changed!" messages to be printed.
- In the SSH console, run
mkdir -p ~/.config/labwc; cd ~/.config/labwc; vim environment, and setXKB_DEFAULT_LAYOUTto something other than whatever it is set to currently. (I've been switching betweendeandusfor my testing.) - In the SSH console, save and close the file, then run
labwc --reconfigure. You should not see a new "Keymap changed!" line appear on the testing system. - In the SSH console, change
XKB_DEFAULT_LAYOUTin~/.config/labwc/environmentagain, then runlabwc --reconfigureagain. You should still not see a new "Keymap changed!" line appear on the testing system. - On the testing system, tap any key once. I usually just tap the left control key. You should immediately see a new "Keymap changed!" line appear.
- In the SSH console, change
XKB_DEFAULT_LAYOUTin~/.config/labwc/environmentagain, then runlabwc --reconfigureagain. This time, you should see a new "Keymap changed!" line appear on the testing system.
labwc build source
Release
labwc version
0.9.1
labwc environment
From a TTY or some display manager like lightdm
Distribution
Arch Linux