After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 790309 - Wayland shell surface doesn't capture cursor or keyboard input
Wayland shell surface doesn't capture cursor or keyboard input
Status: RESOLVED FIXED
Product: mutter
Classification: Core
Component: wayland
3.26.x
Other Linux
: Normal normal
: ---
Assigned To: mutter-maint
mutter-maint
: 791865 792855 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2017-11-14 04:22 UTC by Felipe Ferreira
Modified: 2018-01-24 11:06 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
0001-wayland-Ensure-wl_shell_surfaces-are-set-reactive.patch (1.70 KB, patch)
2018-01-05 09:58 UTC, Daniel van Vugt
committed Details | Review
wayland: Ensure wl_shell_surfaces are set reactive (1.69 KB, patch)
2018-01-09 16:28 UTC, Marco Trevisan (Treviño)
committed Details | Review

Description Felipe Ferreira 2017-11-14 04:22:55 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.
Comment 1 Olivier Fourdan 2017-11-30 14:35:25 UTC
That sounds like an input shape issue. Can you provide a simple reproducer program?
Comment 2 Felipe Ferreira 2017-11-30 18:07:27 UTC
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, &registry_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;
}
```
Comment 3 Daniel van Vugt 2018-01-05 07:21:52 UTC
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.
Comment 4 Daniel van Vugt 2018-01-05 07:22:24 UTC
*** Bug 791865 has been marked as a duplicate of this bug. ***
Comment 5 Daniel van Vugt 2018-01-05 09:58:05 UTC
Created attachment 366364 [details] [review]
0001-wayland-Ensure-wl_shell_surfaces-are-set-reactive.patch

Here's a fix that works nicely.
Comment 6 Jonas Ådahl 2018-01-06 13:59:25 UTC
Review of attachment 366364 [details] [review]:

lgtm
Comment 7 Marco Trevisan (Treviño) 2018-01-09 16:28:17 UTC
The following fix has been pushed:
commit 1c8aebd wayland: Ensure wl_shell_surfaces are set reactive
Comment 8 Marco Trevisan (Treviño) 2018-01-09 16:28:24 UTC
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.
Comment 9 Jonas Ådahl 2018-01-24 11:06:57 UTC
*** Bug 792855 has been marked as a duplicate of this bug. ***