GNOME Bugzilla – Bug 790309
Wayland shell surface doesn't capture cursor or keyboard input
Last modified: 2018-01-24 11:06:57 UTC
I was working on a window manager (like GLFW) with Wayland, and the surface that I created with `wl_shell_get_shell_surface()` doesn't capture certain inputs: - Keyboard enter/leave input works: if you select other window, the surface created with `wl_shell*` is notified. If you Alt-Tab to the surface created `wl_shell*`, the surface will also be notified. - However, if you click on top of the surface, the surface isn't notified, and the click "pass-through" the surface, clicking on whatever window is behind it, making the surface loose focus. - Cursor motion input isn't notified to the surface. - While the surface is in focus, keyboard input (press/release) isn't notified to the surface, either. This is happening on GNOME v3.26.2. I tested GLFW with Wayland backend, and it also happens with GLFW (in different versions of GLFW). However, if I run inside Weston, the input notifications are sent normally to the surface, so I suspect it might be a mutter/GNOME bug.
That sounds like an input shape issue. Can you provide a simple reproducer program?
Correction on this item: - While the surface is in focus, keyboard input (press/release) isn't notified to the surface, either. Keyboard input works. The problems with motion/button remain (doesn't work). Here is a minimal example (with EGL): https://gist.github.com/ferreiradaselva/69574ef6998faf3e40018eb84effadc8 ```c /* gcc -c wl.c gcc -o wl ./wl.o -lwayland-client -lwayland-egl -lEGL -lGL */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wayland-client.h> #include <wayland-server.h> #include <wayland-client-protocol.h> #include <wayland-egl.h> #include <EGL/egl.h> #include <GLES2/gl2.h> struct wl_display *display = NULL; struct wl_compositor *compositor = NULL; struct wl_surface *surface; struct wl_egl_window *egl_window; struct wl_region *region; struct wl_shell *shell; struct wl_shell_surface *shell_surface; struct wl_seat *seat; struct wl_keyboard *keyboard; struct wl_pointer *pointer; EGLDisplay egl_display; EGLConfig egl_conf; EGLSurface egl_surface; EGLContext egl_context; static void pointer_listener_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { } static void pointer_listener_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { } static void pointer_listener_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { printf("motion event:"); printf(" x: %u\n", wl_fixed_to_double(x)); printf(" y: %u\n", wl_fixed_to_double(y)); } static void pointer_listener_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { printf("button event:"); printf(" button: %u\n", button); printf(" state: %u\n", state); } static void pointer_listener_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { } static void keyboard_listener_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { } static void keyboard_listener_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { } static void keyboard_listener_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { } static void keyboard_listener_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { } static void keyboard_listener_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { printf("key event:"); printf(" key: %u\n", key); printf(" state: %u\n", key); } static struct wl_keyboard_listener keyboard_listener = { keyboard_listener_keymap, keyboard_listener_enter, keyboard_listener_leave, keyboard_listener_key, keyboard_listener_modifiers }; static struct wl_pointer_listener pointer_listener = { pointer_listener_enter, pointer_listener_leave, pointer_listener_motion, pointer_listener_button, pointer_listener_axis }; static void seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability capabilities) { if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { keyboard = wl_seat_get_keyboard(seat); wl_keyboard_add_listener(keyboard, &keyboard_listener, NULL); } if (capabilities & WL_SEAT_CAPABILITY_POINTER) { pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(pointer, &pointer_listener, NULL); } } static const struct wl_seat_listener seat_listener = { seat_handle_capabilities, }; static void shell_surface_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { wl_shell_surface_pong(shell_surface, serial); } static void shell_surface_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { } static const struct wl_shell_surface_listener shell_surface_listener = { shell_surface_ping, shell_surface_configure }; static void global_registry_handler(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1); } else if (strcmp(interface, "wl_shell") == 0) { shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); } else if (strcmp(interface, "wl_seat") == 0) { seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); wl_seat_add_listener(seat, &seat_listener, NULL); } } static void global_registry_remover(void *data, struct wl_registry *registry, uint32_t id) { } static const struct wl_registry_listener registry_listener = { global_registry_handler, global_registry_remover }; static void create_opaque_region(void) { region = wl_compositor_create_region(compositor); wl_region_add(region, 0, 0, 256, 256); wl_surface_set_opaque_region(surface, region); } static void create_egl_window(void) { egl_window = wl_egl_window_create(surface, 256, 256); egl_surface = eglCreateWindowSurface(egl_display, egl_conf, egl_window, NULL); eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); glClearColor(0.0, 0.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); eglSwapBuffers(egl_display, egl_surface); } static void initialize_egl(void) { EGLint major, minor, count, n, size; EGLConfig *configs; int i; EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; static const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; egl_display = eglGetDisplay((EGLNativeDisplayType) display); eglInitialize(egl_display, &major, &minor); eglGetConfigs(egl_display, NULL, 0, &count); configs = calloc(count, sizeof *configs); eglChooseConfig(egl_display, config_attribs, configs, count, &n); for (i = 0; i < n; i++) { eglGetConfigAttrib(egl_display, configs[i], EGL_BUFFER_SIZE, &size); eglGetConfigAttrib(egl_display, configs[i], EGL_RED_SIZE, &size); egl_conf = configs[i]; break; } egl_context = eglCreateContext(egl_display, egl_conf, EGL_NO_CONTEXT, context_attribs); } static void get_server_references(void) { display = wl_display_connect(NULL); struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_dispatch(display); wl_display_roundtrip(display); } int main(int argc, char **argv) { get_server_references(); surface = wl_compositor_create_surface(compositor); shell_surface = wl_shell_get_shell_surface(shell, surface); wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, NULL); wl_shell_surface_set_toplevel(shell_surface); create_opaque_region(); initialize_egl(); create_egl_window(); while (wl_display_dispatch(display) != -1) { /* main loop */ } wl_display_disconnect(display); return EXIT_SUCCESS; } ```
Two easily available apps that exhibit this problem: (1) /usr/lib/weston/weston-simple-touch (2) mpv version 0.27 and earlier Both work in Weston but not gnome-shell. In gnome shell their pointer events are ignored, even pass straight through to the surface below. Indeed it appears that using the (deprecated) wl_shell interface is the common factor. Although deprecated, gnome-shell advertises the interface is supported so it should work better than this.
*** Bug 791865 has been marked as a duplicate of this bug. ***
Created attachment 366364 [details] [review] 0001-wayland-Ensure-wl_shell_surfaces-are-set-reactive.patch Here's a fix that works nicely.
Review of attachment 366364 [details] [review]: lgtm
The following fix has been pushed: commit 1c8aebd wayland: Ensure wl_shell_surfaces are set reactive
Created attachment 366563 [details] [review] wayland: Ensure wl_shell_surfaces are set reactive Wayland clients using the wl_shell interface were never receiving mouse input. It meant they also couldn't be raised with a click. This was because the call to meta_wayland_surface_set_window for wl_shell surfaces did nothing while surface->window == window already. As such, it never called clutter_actor_set_reactive() and the wl_shell window remained a non-reactive actor. Just make sure surface->window isn't already set before calling meta_wayland_surface_set_window so it can actually do what it's meant to.
*** Bug 792855 has been marked as a duplicate of this bug. ***