mutter/src/wayland/meta-wayland.c
Olivier Fourdan 072afa5fa3 wayland: Add Xwayland grab keyboard support
This protocol is limited to Xwayland only and is not visible/usable by
any other client.

Mutter uses the following mechanisms to determine if an X11 client
should be granted a grab:

 - is "xwayland-allow-grabs" set?
 - if set, is the client blacklisted?
 - otherwise, has the client set the X11 window property
   _XWAYLAND_MAY_GRAB_KEYBOARD on the window using a client message?
 - if not, is it a client white-listed either via the default system
   list or the settings "xwayland-grab-access-rules"?

https://bugzilla.gnome.org/show_bug.cgi?id=783342
2017-12-18 13:15:20 +01:00

485 lines
15 KiB
C

/*
* Wayland Support
*
* Copyright (C) 2012,2013 Intel Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "config.h"
#include "meta-wayland.h"
#include <clutter/clutter.h>
#include <clutter/wayland/clutter-wayland-compositor.h>
#include <clutter/wayland/clutter-wayland-surface.h>
#include <sys/time.h>
#include <string.h>
#include <stdlib.h>
#include <wayland-server.h>
#include "meta-wayland-private.h"
#include "meta-xwayland-private.h"
#include "meta-wayland-region.h"
#include "meta-wayland-seat.h"
#include "meta-wayland-outputs.h"
#include "meta-wayland-data-device.h"
#include "meta-wayland-tablet-manager.h"
#include "meta-wayland-xdg-foreign.h"
#include "meta-wayland-dma-buf.h"
#include "meta-wayland-inhibit-shortcuts.h"
#include "meta-wayland-inhibit-shortcuts-dialog.h"
#include "meta-xwayland-grab-keyboard.h"
static MetaWaylandCompositor _meta_wayland_compositor;
static char *_display_name_override;
MetaWaylandCompositor *
meta_wayland_compositor_get_default (void)
{
return &_meta_wayland_compositor;
}
typedef struct
{
GSource source;
struct wl_display *display;
} WaylandEventSource;
static gboolean
wayland_event_source_prepare (GSource *base,
int *timeout)
{
WaylandEventSource *source = (WaylandEventSource *)base;
*timeout = -1;
wl_display_flush_clients (source->display);
return FALSE;
}
static gboolean
wayland_event_source_dispatch (GSource *base,
GSourceFunc callback,
void *data)
{
WaylandEventSource *source = (WaylandEventSource *)base;
struct wl_event_loop *loop = wl_display_get_event_loop (source->display);
wl_event_loop_dispatch (loop, 0);
return TRUE;
}
static GSourceFuncs wayland_event_source_funcs =
{
wayland_event_source_prepare,
NULL,
wayland_event_source_dispatch,
NULL
};
static GSource *
wayland_event_source_new (struct wl_display *display)
{
WaylandEventSource *source;
struct wl_event_loop *loop = wl_display_get_event_loop (display);
source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
sizeof (WaylandEventSource));
source->display = display;
g_source_add_unix_fd (&source->source,
wl_event_loop_get_fd (loop),
G_IO_IN | G_IO_ERR);
return &source->source;
}
void
meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
MetaWindow *window)
{
MetaWaylandSurface *surface = window ? window->surface : NULL;
meta_wayland_seat_set_input_focus (compositor->seat, surface);
}
void
meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
{
meta_wayland_seat_repick (compositor->seat);
}
static void
wl_compositor_create_surface (struct wl_client *client,
struct wl_resource *resource,
uint32_t id)
{
MetaWaylandCompositor *compositor = wl_resource_get_user_data (resource);
meta_wayland_surface_create (compositor, client, resource, id);
}
static void
wl_compositor_create_region (struct wl_client *client,
struct wl_resource *resource,
uint32_t id)
{
MetaWaylandCompositor *compositor = wl_resource_get_user_data (resource);
meta_wayland_region_create (compositor, client, resource, id);
}
static const struct wl_compositor_interface meta_wayland_wl_compositor_interface = {
wl_compositor_create_surface,
wl_compositor_create_region
};
static void
compositor_bind (struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
MetaWaylandCompositor *compositor = data;
struct wl_resource *resource;
resource = wl_resource_create (client, &wl_compositor_interface, version, id);
wl_resource_set_implementation (resource,
&meta_wayland_wl_compositor_interface,
compositor, NULL);
}
/**
* meta_wayland_compositor_update:
* @compositor: the #MetaWaylandCompositor instance
* @event: the #ClutterEvent used to update @seat's state
*
* This is used to update display server state like updating cursor
* position and keeping track of buttons and keys pressed. It must be
* called for all input events coming from the underlying devices.
*/
void
meta_wayland_compositor_update (MetaWaylandCompositor *compositor,
const ClutterEvent *event)
{
if (meta_wayland_tablet_manager_consumes_event (compositor->tablet_manager, event))
meta_wayland_tablet_manager_update (compositor->tablet_manager, event);
else
meta_wayland_seat_update (compositor->seat, event);
}
void
meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor)
{
gint64 current_time = g_get_monotonic_time ();
while (!wl_list_empty (&compositor->frame_callbacks))
{
MetaWaylandFrameCallback *callback =
wl_container_of (compositor->frame_callbacks.next, callback, link);
wl_callback_send_done (callback->resource, current_time / 1000);
wl_resource_destroy (callback->resource);
}
}
/**
* meta_wayland_compositor_handle_event:
* @compositor: the #MetaWaylandCompositor instance
* @event: the #ClutterEvent to be sent
*
* This method sends events to the focused wayland client, if any.
*
* Return value: whether @event was sent to a wayland client.
*/
gboolean
meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor,
const ClutterEvent *event)
{
if (meta_wayland_tablet_manager_handle_event (compositor->tablet_manager,
event))
return TRUE;
return meta_wayland_seat_handle_event (compositor->seat, event);
}
/* meta_wayland_compositor_update_key_state:
* @compositor: the #MetaWaylandCompositor
* @key_vector: bit vector of key states
* @key_vector_len: length of @key_vector
* @offset: the key for the first evdev keycode is found at this offset in @key_vector
*
* This function is used to resynchronize the key state that Mutter
* is tracking with the actual keyboard state. This is useful, for example,
* to handle changes in key state when a nested compositor doesn't
* have focus. We need to fix up the XKB modifier tracking and deliver
* any modifier changes to clients.
*/
void
meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor,
char *key_vector,
int key_vector_len,
int offset)
{
meta_wayland_keyboard_update_key_state (compositor->seat->keyboard,
key_vector, key_vector_len, offset);
}
void
meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor,
MetaWaylandSurface *surface)
{
MetaWaylandFrameCallback *callback, *next;
wl_list_for_each_safe (callback, next, &compositor->frame_callbacks, link)
{
if (callback->surface == surface)
wl_resource_destroy (callback->resource);
}
}
static void
set_gnome_env (const char *name,
const char *value)
{
GDBusConnection *session_bus;
GError *error = NULL;
setenv (name, value, TRUE);
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (session_bus);
g_dbus_connection_call_sync (session_bus,
"org.gnome.SessionManager",
"/org/gnome/SessionManager",
"org.gnome.SessionManager",
"Setenv",
g_variant_new ("(ss)", name, value),
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &error);
if (error)
{
char *remote_error;
remote_error = g_dbus_error_get_remote_error (error);
if (g_strcmp0 (remote_error, "org.gnome.SessionManager.NotInInitialization") != 0)
meta_warning ("Failed to set environment variable %s for gnome-session: %s\n", name, error->message);
g_free (remote_error);
g_error_free (error);
}
}
static void meta_wayland_log_func (const char *, va_list) G_GNUC_PRINTF (1, 0);
static void
meta_wayland_log_func (const char *fmt,
va_list arg)
{
char *str = g_strdup_vprintf (fmt, arg);
g_warning ("WL: %s", str);
g_free (str);
}
static void
meta_wayland_compositor_init (MetaWaylandCompositor *compositor)
{
memset (compositor, 0, sizeof (MetaWaylandCompositor));
wl_list_init (&compositor->frame_callbacks);
}
void
meta_wayland_pre_clutter_init (void)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
meta_wayland_compositor_init (compositor);
wl_log_set_handler_server (meta_wayland_log_func);
compositor->wayland_display = wl_display_create ();
if (compositor->wayland_display == NULL)
g_error ("Failed to create the global wl_display");
clutter_wayland_set_compositor_display (compositor->wayland_display);
}
static bool
meta_xwayland_global_filter (const struct wl_client *client,
const struct wl_global *global,
void *data)
{
MetaWaylandCompositor *compositor = (MetaWaylandCompositor *) data;
MetaXWaylandManager *xwayland_manager = &compositor->xwayland_manager;
/* Keyboard grabbing protocol is for Xwayland only */
if (client != xwayland_manager->client)
return (wl_global_get_interface (global) !=
&zwp_xwayland_keyboard_grab_manager_v1_interface);
/* All others are visible to all clients */
return true;
}
void
meta_wayland_override_display_name (char *display_name)
{
g_clear_pointer (&_display_name_override, g_free);
_display_name_override = g_strdup (display_name);
}
void
meta_wayland_init (void)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
GSource *wayland_event_source;
wayland_event_source = wayland_event_source_new (compositor->wayland_display);
/* XXX: Here we are setting the wayland event source to have a
* slightly lower priority than the X event source, because we are
* much more likely to get confused being told about surface changes
* relating to X clients when we don't know what's happened to them
* according to the X protocol.
*/
g_source_set_priority (wayland_event_source, GDK_PRIORITY_EVENTS + 1);
g_source_attach (wayland_event_source, NULL);
if (!wl_global_create (compositor->wayland_display,
&wl_compositor_interface,
META_WL_COMPOSITOR_VERSION,
compositor, compositor_bind))
g_error ("Failed to register the global wl_compositor");
wl_display_init_shm (compositor->wayland_display);
meta_wayland_outputs_init (compositor);
meta_wayland_data_device_manager_init (compositor);
meta_wayland_shell_init (compositor);
meta_wayland_pointer_gestures_init (compositor);
meta_wayland_tablet_manager_init (compositor);
meta_wayland_seat_init (compositor);
meta_wayland_relative_pointer_init (compositor);
meta_wayland_pointer_constraints_init (compositor);
meta_wayland_xdg_foreign_init (compositor);
meta_wayland_dma_buf_init (compositor);
meta_wayland_keyboard_shortcuts_inhibit_init (compositor);
meta_wayland_surface_inhibit_shortcuts_dialog_init ();
/* Xwayland specific protocol, needs to be filtered out for all other clients */
if (meta_xwayland_grab_keyboard_init (compositor))
wl_display_set_global_filter (compositor->wayland_display,
meta_xwayland_global_filter,
compositor);
if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display))
g_error ("Failed to start X Wayland");
if (_display_name_override)
{
compositor->display_name = g_steal_pointer (&_display_name_override);
if (wl_display_add_socket (compositor->wayland_display,
compositor->display_name) != 0)
g_error ("Failed to create_socket");
}
else
{
const char *display_name;
display_name = wl_display_add_socket_auto (compositor->wayland_display);
if (!display_name)
g_error ("Failed to create socket");
compositor->display_name = g_strdup (display_name);
}
set_gnome_env ("DISPLAY", meta_wayland_get_xwayland_display_name (compositor));
set_gnome_env ("WAYLAND_DISPLAY", meta_wayland_get_wayland_display_name (compositor));
}
const char *
meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor)
{
return compositor->display_name;
}
const char *
meta_wayland_get_xwayland_display_name (MetaWaylandCompositor *compositor)
{
return compositor->xwayland_manager.display_name;
}
void
meta_wayland_finalize (void)
{
MetaWaylandCompositor *compositor;
compositor = meta_wayland_compositor_get_default ();
meta_xwayland_stop (&compositor->xwayland_manager);
g_clear_pointer (&compositor->display_name, g_free);
}
void
meta_wayland_compositor_restore_shortcuts (MetaWaylandCompositor *compositor,
ClutterInputDevice *source)
{
MetaWaylandKeyboard *keyboard;
/* Clutter is not multi-seat aware yet, use the default seat instead */
keyboard = compositor->seat->keyboard;
if (!keyboard || !keyboard->focus_surface)
return;
if (!meta_wayland_surface_is_shortcuts_inhibited (keyboard->focus_surface,
compositor->seat))
return;
meta_wayland_surface_restore_shortcuts (keyboard->focus_surface,
compositor->seat);
}
gboolean
meta_wayland_compositor_is_shortcuts_inhibited (MetaWaylandCompositor *compositor,
ClutterInputDevice *source)
{
MetaWaylandKeyboard *keyboard;
if (clutter_input_device_get_device_type (source) != CLUTTER_KEYBOARD_DEVICE)
return FALSE;
/* Clutter is not multi-seat aware yet, use the default seat instead */
keyboard = compositor->seat->keyboard;
if (keyboard && keyboard->focus_surface != NULL)
return meta_wayland_surface_is_shortcuts_inhibited (keyboard->focus_surface,
compositor->seat);
return FALSE;
}
void
meta_wayland_compositor_flush_clients (MetaWaylandCompositor *compositor)
{
wl_display_flush_clients (compositor->wayland_display);
}