1
0
Fork 0
mutter-performance-source/src/wayland/meta-wayland.c
Neil Roberts 268ebb1b18 wayland: Add basic input support
This copies the basic input support from the Clayland demo compositor.
It adds a basic wl_seat implementation which can convert Clutter mouse
events to Wayland events. For this to work all of the wayland surface
actors need to be made reactive.

The wayland keyboard input focus surface is updated whenever Mutter
sees a FocusIn event so that it will stay in synch with whatever
surface Mutter wants as the focus. Wayland surfaces don't get this
event so for now it will just give them focus whenever they are
clicked as a hack to test the code.

Authored-by: Neil Roberts <neil@linux.intel.com>
Authored-by: Giovanni Campagna <gcampagna@src.gnome.org>
2013-08-10 19:13:49 -04:00

1425 lines
44 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 <clutter/clutter.h>
#include <clutter/wayland/clutter-wayland-compositor.h>
#include <clutter/wayland/clutter-wayland-surface.h>
#include <glib.h>
#include <sys/time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <wayland-server.h>
#include "xserver-server-protocol.h"
#include "meta-wayland-private.h"
#include "meta-xwayland-private.h"
#include "meta-window-actor-private.h"
#include "meta-wayland-seat.h"
#include "meta-wayland-keyboard.h"
#include "meta-wayland-data-device.h"
#include "display-private.h"
#include "window-private.h"
#include <meta/types.h>
#include <meta/main.h>
#include "frame.h"
static MetaWaylandCompositor _meta_wayland_compositor;
MetaWaylandCompositor *
meta_wayland_compositor_get_default (void)
{
return &_meta_wayland_compositor;
}
static guint32
get_time (void)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
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_check (GSource *base)
{
WaylandEventSource *source = (WaylandEventSource *)base;
return source->pfd.revents;
}
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,
wayland_event_source_check,
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;
source->pfd.fd = wl_event_loop_get_fd (loop);
source->pfd.events = G_IO_IN | G_IO_ERR;
g_source_add_poll (&source->source, &source->pfd);
return &source->source;
}
static void
meta_wayland_buffer_destroy_handler (struct wl_listener *listener,
void *data)
{
MetaWaylandBuffer *buffer =
wl_container_of (listener, buffer, destroy_listener);
wl_signal_emit (&buffer->destroy_signal, buffer);
g_slice_free (MetaWaylandBuffer, buffer);
}
static MetaWaylandBuffer *
meta_wayland_buffer_from_resource (struct wl_resource *resource)
{
MetaWaylandBuffer *buffer;
struct wl_listener *listener;
listener =
wl_resource_get_destroy_listener (resource,
meta_wayland_buffer_destroy_handler);
if (listener)
{
buffer = wl_container_of (listener, buffer, destroy_listener);
}
else
{
buffer = g_slice_new0 (MetaWaylandBuffer);
buffer->resource = resource;
wl_signal_init (&buffer->destroy_signal);
buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler;
wl_resource_add_destroy_listener (resource, &buffer->destroy_listener);
}
return buffer;
}
static void
meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandBufferReference *ref =
wl_container_of (listener, ref, destroy_listener);
g_assert (data == ref->buffer);
ref->buffer = NULL;
}
static void
meta_wayland_buffer_reference (MetaWaylandBufferReference *ref,
MetaWaylandBuffer *buffer)
{
if (ref->buffer && buffer != ref->buffer)
{
ref->buffer->busy_count--;
if (ref->buffer->busy_count == 0)
{
g_assert (wl_resource_get_client (ref->buffer->resource));
wl_resource_queue_event (ref->buffer->resource, WL_BUFFER_RELEASE);
}
wl_list_remove (&ref->destroy_listener.link);
}
if (buffer && buffer != ref->buffer)
{
buffer->busy_count++;
wl_signal_add (&buffer->destroy_signal, &ref->destroy_listener);
}
ref->buffer = buffer;
ref->destroy_listener.notify = meta_wayland_buffer_reference_handle_destroy;
}
static void
surface_process_damage (MetaWaylandSurface *surface,
cairo_region_t *region)
{
if (surface->window &&
surface->buffer_ref.buffer)
{
MetaWindowActor *window_actor =
META_WINDOW_ACTOR (meta_window_get_compositor_private (surface->window));
if (window_actor)
{
int i, n_rectangles = cairo_region_num_rectangles (region);
for (i = 0; i < n_rectangles; i++)
{
cairo_rectangle_int_t rectangle;
cairo_region_get_rectangle (region, i, &rectangle);
meta_window_actor_process_wayland_damage (window_actor,
rectangle.x,
rectangle.y,
rectangle.width,
rectangle.height);
}
}
}
}
static void
meta_wayland_surface_destroy (struct wl_client *wayland_client,
struct wl_resource *wayland_resource)
{
wl_resource_destroy (wayland_resource);
}
static void
meta_wayland_surface_attach (struct wl_client *wayland_client,
struct wl_resource *wayland_surface_resource,
struct wl_resource *wayland_buffer_resource,
gint32 sx, gint32 sy)
{
MetaWaylandSurface *surface =
wl_resource_get_user_data (wayland_surface_resource);
MetaWaylandBuffer *buffer;
if (wayland_buffer_resource)
buffer = meta_wayland_buffer_from_resource (wayland_buffer_resource);
else
buffer = NULL;
/* Attach without commit in between does not send wl_buffer.release */
if (surface->pending.buffer)
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
surface->pending.sx = sx;
surface->pending.sy = sy;
surface->pending.buffer = buffer;
surface->pending.newly_attached = TRUE;
if (buffer)
wl_signal_add (&buffer->destroy_signal,
&surface->pending.buffer_destroy_listener);
}
static void
meta_wayland_surface_damage (struct wl_client *client,
struct wl_resource *surface_resource,
gint32 x,
gint32 y,
gint32 width,
gint32 height)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
cairo_rectangle_int_t rectangle = { x, y, width, height };
cairo_region_union_rectangle (surface->pending.damage, &rectangle);
}
static void
destroy_frame_callback (struct wl_resource *callback_resource)
{
MetaWaylandFrameCallback *callback =
wl_resource_get_user_data (callback_resource);
wl_list_remove (&callback->link);
g_slice_free (MetaWaylandFrameCallback, callback);
}
static void
meta_wayland_surface_frame (struct wl_client *client,
struct wl_resource *surface_resource,
guint32 callback_id)
{
MetaWaylandFrameCallback *callback;
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
callback = g_slice_new0 (MetaWaylandFrameCallback);
callback->compositor = surface->compositor;
callback->resource = wl_resource_create (client,
&wl_callback_interface, 1,
callback_id);
wl_resource_set_user_data (callback->resource, callback);
wl_resource_set_destructor (callback->resource, destroy_frame_callback);
wl_list_insert (surface->pending.frame_callback_list.prev, &callback->link);
}
static void
meta_wayland_surface_set_opaque_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region)
{
g_warning ("TODO: support set_opaque_region request");
}
static void
meta_wayland_surface_set_input_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region)
{
g_warning ("TODO: support set_input_region request");
}
static void
empty_region (cairo_region_t *region)
{
cairo_rectangle_int_t rectangle = { 0, 0, 0, 0 };
cairo_region_intersect_rectangle (region, &rectangle);
}
static void
meta_wayland_surface_commit (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
MetaWaylandCompositor *compositor = surface->compositor;
/* wl_surface.attach */
if (surface->pending.newly_attached &&
surface->buffer_ref.buffer != surface->pending.buffer)
{
/* Note: we set this before informing any window-actor since the
* window actor will expect to find the new buffer within the
* surface. */
meta_wayland_buffer_reference (&surface->buffer_ref,
surface->pending.buffer);
if (surface->pending.buffer)
{
MetaWaylandBuffer *buffer = surface->pending.buffer;
if (surface->window)
{
MetaWindow *window = surface->window;
MetaWindowActor *window_actor =
META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
MetaRectangle rect;
meta_window_get_input_rect (surface->window, &rect);
if (window_actor)
meta_window_actor_attach_wayland_buffer (window_actor, buffer);
/* XXX: we resize X based surfaces according to X events */
if (surface->xid == 0 &&
(buffer->width != rect.width || buffer->height != rect.height))
meta_window_resize (surface->window, FALSE, buffer->width, buffer->height);
}
}
}
if (surface->pending.buffer)
{
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
surface->pending.buffer = NULL;
}
surface->pending.sx = 0;
surface->pending.sy = 0;
surface->pending.newly_attached = FALSE;
surface_process_damage (surface, surface->pending.damage);
empty_region (surface->pending.damage);
/* wl_surface.frame */
wl_list_insert_list (&compositor->frame_callbacks,
&surface->pending.frame_callback_list);
wl_list_init (&surface->pending.frame_callback_list);
}
static void
meta_wayland_surface_set_buffer_transform (struct wl_client *client,
struct wl_resource *resource,
int32_t transform)
{
g_warning ("TODO: support set_buffer_transform request");
}
static void
meta_wayland_surface_set_buffer_scale (struct wl_client *client,
struct wl_resource *resource,
int scale)
{
g_warning ("TODO: support set_buffer_scale request");
}
const struct wl_surface_interface meta_wayland_surface_interface = {
meta_wayland_surface_destroy,
meta_wayland_surface_attach,
meta_wayland_surface_damage,
meta_wayland_surface_frame,
meta_wayland_surface_set_opaque_region,
meta_wayland_surface_set_input_region,
meta_wayland_surface_commit,
meta_wayland_surface_set_buffer_transform,
meta_wayland_surface_set_buffer_scale
};
void
meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
MetaWindow *window)
{
MetaWaylandSurface *surface = window ? window->surface : NULL;
meta_wayland_keyboard_set_focus (&compositor->seat->keyboard,
surface);
meta_wayland_data_device_set_keyboard_focus (compositor->seat);
}
static void
window_destroyed_cb (void *user_data, GObject *old_object)
{
MetaWaylandSurface *surface = user_data;
surface->window = NULL;
}
void
meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
{
meta_wayland_seat_repick (compositor->seat,
get_time (),
NULL);
}
static void
meta_wayland_surface_free (MetaWaylandSurface *surface)
{
MetaWaylandCompositor *compositor = surface->compositor;
MetaWaylandFrameCallback *cb, *next;
compositor->surfaces = g_list_remove (compositor->surfaces, surface);
meta_wayland_buffer_reference (&surface->buffer_ref, NULL);
if (surface->window)
g_object_weak_unref (G_OBJECT (surface->window),
window_destroyed_cb,
surface);
/* NB: If the surface corresponds to an X window then we will be
* sure to free the MetaWindow according to some X event. */
if (surface->window &&
surface->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
{
MetaDisplay *display = meta_get_display ();
guint32 timestamp = meta_display_get_current_time_roundtrip (display);
meta_window_unmanage (surface->window, timestamp);
}
if (surface->pending.buffer)
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
cairo_region_destroy (surface->pending.damage);
wl_list_for_each_safe (cb, next,
&surface->pending.frame_callback_list, link)
wl_resource_destroy (cb->resource);
g_slice_free (MetaWaylandSurface, surface);
meta_wayland_compositor_repick (compositor);
if (compositor->implicit_grab_surface == surface)
compositor->implicit_grab_surface = compositor->seat->pointer.current;
}
static void
meta_wayland_surface_resource_destroy_cb (struct wl_resource *resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
meta_wayland_surface_free (surface);
}
static void
surface_handle_pending_buffer_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandSurface *surface =
wl_container_of (listener, surface, pending.buffer_destroy_listener);
surface->pending.buffer = NULL;
}
static void
meta_wayland_compositor_create_surface (struct wl_client *wayland_client,
struct wl_resource *wayland_compositor_resource,
guint32 id)
{
MetaWaylandCompositor *compositor =
wl_resource_get_user_data (wayland_compositor_resource);
MetaWaylandSurface *surface = g_slice_new0 (MetaWaylandSurface);
surface->compositor = compositor;
/* a surface inherits the version from the compositor */
surface->resource = wl_resource_create (wayland_client,
&wl_surface_interface,
wl_resource_get_version (wayland_compositor_resource),
id);
wl_resource_set_implementation (surface->resource, &meta_wayland_surface_interface, surface,
meta_wayland_surface_resource_destroy_cb);
surface->pending.damage = cairo_region_create ();
surface->pending.buffer_destroy_listener.notify =
surface_handle_pending_buffer_destroy;
wl_list_init (&surface->pending.frame_callback_list);
compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
}
static void
meta_wayland_region_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
meta_wayland_region_add (struct wl_client *client,
struct wl_resource *resource,
gint32 x,
gint32 y,
gint32 width,
gint32 height)
{
MetaWaylandRegion *region = wl_resource_get_user_data (resource);
cairo_rectangle_int_t rectangle = { x, y, width, height };
cairo_region_union_rectangle (region->region, &rectangle);
}
static void
meta_wayland_region_subtract (struct wl_client *client,
struct wl_resource *resource,
gint32 x,
gint32 y,
gint32 width,
gint32 height)
{
MetaWaylandRegion *region = wl_resource_get_user_data (resource);
cairo_rectangle_int_t rectangle = { x, y, width, height };
cairo_region_subtract_rectangle (region->region, &rectangle);
}
const struct wl_region_interface meta_wayland_region_interface = {
meta_wayland_region_destroy,
meta_wayland_region_add,
meta_wayland_region_subtract
};
static void
meta_wayland_region_resource_destroy_cb (struct wl_resource *resource)
{
MetaWaylandRegion *region = wl_resource_get_user_data (resource);
cairo_region_destroy (region->region);
g_slice_free (MetaWaylandRegion, region);
}
static void
meta_wayland_compositor_create_region (struct wl_client *wayland_client,
struct wl_resource *compositor_resource,
uint32_t id)
{
MetaWaylandRegion *region = g_slice_new0 (MetaWaylandRegion);
region->resource = wl_resource_create (wayland_client,
&wl_region_interface, 1,
id);
wl_resource_set_implementation (region->resource,
&meta_wayland_region_interface, region,
meta_wayland_region_resource_destroy_cb);
region->region = cairo_region_create ();
}
static void
bind_output (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
MetaWaylandOutput *output = data;
struct wl_resource *resource =
wl_resource_create (client, &wl_output_interface, version, id);
GList *l;
wl_resource_post_event (resource,
WL_OUTPUT_GEOMETRY,
output->x, output->y,
output->width_mm,
output->height_mm,
0, /* subpixel: unknown */
"unknown", /* make */
"unknown"); /* model */
for (l = output->modes; l; l = l->next)
{
MetaWaylandMode *mode = l->data;
wl_resource_post_event (resource,
WL_OUTPUT_MODE,
mode->flags,
mode->width,
mode->height,
mode->refresh);
}
}
static void
meta_wayland_compositor_create_output (MetaWaylandCompositor *compositor,
int x,
int y,
int width,
int height,
int width_mm,
int height_mm)
{
MetaWaylandOutput *output = g_slice_new0 (MetaWaylandOutput);
MetaWaylandMode *mode;
float final_width, final_height;
/* XXX: eventually we will support sliced stages and an output should
* correspond to a slice/CoglFramebuffer, but for now we only support
* one output so we make sure it always matches the size of the stage
*/
clutter_actor_set_size (compositor->stage, width, height);
/* Read back the actual size we were given.
* XXX: This really needs re-thinking later though so we know the
* correct output geometry to use. */
clutter_actor_get_size (compositor->stage, &final_width, &final_height);
width = final_width;
height = final_height;
output->wayland_output.interface = &wl_output_interface;
output->x = x;
output->y = y;
output->width_mm = width_mm;
output->height_mm = height_mm;
wl_global_create (compositor->wayland_display,
&wl_output_interface, 2,
output, bind_output);
mode = g_slice_new0 (MetaWaylandMode);
mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
mode->width = width;
mode->height = height;
mode->refresh = 60;
output->modes = g_list_prepend (output->modes, mode);
compositor->outputs = g_list_prepend (compositor->outputs, output);
}
const static struct wl_compositor_interface meta_wayland_compositor_interface = {
meta_wayland_compositor_create_surface,
meta_wayland_compositor_create_region
};
static void
paint_finished_cb (ClutterActor *self, void *user_data)
{
MetaWaylandCompositor *compositor = user_data;
while (!wl_list_empty (&compositor->frame_callbacks))
{
MetaWaylandFrameCallback *callback =
wl_container_of (compositor->frame_callbacks.next, callback, link);
wl_resource_post_event (callback->resource,
WL_CALLBACK_DONE, get_time ());
wl_resource_destroy (callback->resource);
}
}
static void
compositor_bind (struct wl_client *client,
void *data,
guint32 version,
guint32 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_compositor_interface, compositor, NULL);
}
static void
shell_surface_pong (struct wl_client *client,
struct wl_resource *resource,
guint32 serial)
{
}
static void
shell_surface_move (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat,
guint32 serial)
{
}
static void
shell_surface_resize (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat,
guint32 serial,
guint32 edges)
{
g_warning ("TODO: support shell_surface_resize request");
}
static void
ensure_surface_window (MetaWaylandSurface *surface)
{
MetaDisplay *display = meta_get_display ();
if (!surface->window)
{
int width, height;
if (surface->buffer_ref.buffer)
{
MetaWaylandBuffer *buffer = surface->buffer_ref.buffer;
width = buffer->width;
height = buffer->width;
}
else
{
width = 0;
height = 0;
}
surface->window =
meta_window_new_for_wayland (display, width, height, surface);
/* If the MetaWindow becomes unmanaged (surface->window will be
* freed in this case) we need to make sure to clear our
* ->window pointers. */
g_object_weak_ref (G_OBJECT (surface->window),
window_destroyed_cb,
surface);
meta_window_calc_showing (surface->window);
}
}
static void
shell_surface_set_toplevel (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
/* NB: Surfaces from xwayland become managed based on X events. */
if (client == compositor->xwayland_client)
return;
ensure_surface_window (surface);
meta_window_unmake_fullscreen (surface->window);
}
static void
shell_surface_set_transient (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *parent,
int x,
int y,
guint32 flags)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
/* NB: Surfaces from xwayland become managed based on X events. */
if (client == compositor->xwayland_client)
return;
ensure_surface_window (surface);
}
static void
shell_surface_set_fullscreen (struct wl_client *client,
struct wl_resource *resource,
guint32 method,
guint32 framerate,
struct wl_resource *output)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
MetaWaylandSurface *surface = shell_surface->surface;
/* NB: Surfaces from xwayland become managed based on X events. */
if (client == compositor->xwayland_client)
return;
ensure_surface_window (surface);
meta_window_make_fullscreen (surface->window);
}
static void
shell_surface_set_popup (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *seat,
guint32 serial,
struct wl_resource *parent,
gint32 x,
gint32 y,
guint32 flags)
{
}
static void
shell_surface_set_maximized (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *output)
{
g_warning ("TODO: support shell_surface_set_maximized request");
}
static void
shell_surface_set_title (struct wl_client *client,
struct wl_resource *resource,
const char *title)
{
g_warning ("TODO: support shell_surface_set_title request");
}
static void
shell_surface_set_class (struct wl_client *client,
struct wl_resource *resource,
const char *class_)
{
g_warning ("TODO: support shell_surface_set_class request");
}
static const struct wl_shell_surface_interface meta_wayland_shell_surface_interface =
{
shell_surface_pong,
shell_surface_move,
shell_surface_resize,
shell_surface_set_toplevel,
shell_surface_set_transient,
shell_surface_set_fullscreen,
shell_surface_set_popup,
shell_surface_set_maximized,
shell_surface_set_title,
shell_surface_set_class
};
static void
shell_handle_surface_destroy (struct wl_listener *listener,
void *data)
{
MetaWaylandShellSurface *shell_surface =
wl_container_of (listener, shell_surface, surface_destroy_listener);
shell_surface->surface->has_shell_surface = FALSE;
shell_surface->surface = NULL;
wl_resource_destroy (shell_surface->resource);
}
static void
destroy_shell_surface (struct wl_resource *resource)
{
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
/* In case cleaning up a dead client destroys shell_surface first */
if (shell_surface->surface)
{
wl_list_remove (&shell_surface->surface_destroy_listener.link);
shell_surface->surface->has_shell_surface = FALSE;
}
g_free (shell_surface);
}
static void
get_shell_surface (struct wl_client *client,
struct wl_resource *resource,
guint32 id,
struct wl_resource *surface_resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandShellSurface *shell_surface;
if (surface->has_shell_surface)
{
wl_resource_post_error (surface_resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"wl_shell::get_shell_surface already requested");
return;
}
shell_surface = g_new0 (MetaWaylandShellSurface, 1);
/* a shell surface inherits the version from the shell */
shell_surface->resource =
wl_resource_create (client, &wl_shell_surface_interface,
wl_resource_get_version (resource), id);
wl_resource_set_implementation (shell_surface->resource, &meta_wayland_shell_surface_interface,
shell_surface, destroy_shell_surface);
shell_surface->surface = surface;
shell_surface->surface_destroy_listener.notify = shell_handle_surface_destroy;
wl_resource_add_destroy_listener (surface->resource,
&shell_surface->surface_destroy_listener);
surface->has_shell_surface = TRUE;
}
static const struct wl_shell_interface meta_wayland_shell_interface =
{
get_shell_surface
};
static void
bind_shell (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &wl_shell_interface, version, id);
wl_resource_set_implementation (resource, &meta_wayland_shell_interface, data, NULL);
}
static void
xserver_set_window_id (struct wl_client *client,
struct wl_resource *compositor_resource,
struct wl_resource *surface_resource,
guint32 xid)
{
MetaWaylandCompositor *compositor =
wl_resource_get_user_data (compositor_resource);
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaDisplay *display = meta_get_display ();
MetaWindow *window;
g_return_if_fail (surface->xid == None);
surface->xid = xid;
g_hash_table_insert (compositor->window_surfaces, &xid, surface);
window = meta_display_lookup_x_window (display, xid);
if (window)
{
MetaWindowActor *window_actor =
META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
meta_window_actor_set_wayland_surface (window_actor, surface);
surface->window = window;
window->surface = surface;
/* If the MetaWindow becomes unmanaged (surface->window will be
* freed in this case) we need to make sure to clear our
* ->window pointers in this case. */
g_object_weak_ref (G_OBJECT (surface->window),
window_destroyed_cb,
surface);
/* If the window is already meant to have focus then the
* original attempt to call this in response to the FocusIn
* event will have been lost because there was no surface
* yet. */
if (window->has_focus)
meta_wayland_compositor_set_input_focus (compositor, window);
}
#warning "FIXME: Handle surface destroy and remove window_surfaces mapping"
}
MetaWaylandSurface *
meta_wayland_lookup_surface_for_xid (guint32 xid)
{
return g_hash_table_lookup (_meta_wayland_compositor.window_surfaces, &xid);
}
static const struct xserver_interface xserver_implementation = {
xserver_set_window_id
};
static void
bind_xserver (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
MetaWaylandCompositor *compositor = data;
/* If it's a different client than the xserver we launched,
* don't start the wm. */
if (client != compositor->xwayland_client)
return;
compositor->xserver_resource =
wl_resource_create (client, &xserver_interface, version, id);
wl_resource_set_implementation (compositor->xserver_resource,
&xserver_implementation, compositor, NULL);
wl_resource_post_event (compositor->xserver_resource,
XSERVER_LISTEN_SOCKET,
compositor->xwayland_abstract_fd);
wl_resource_post_event (compositor->xserver_resource,
XSERVER_LISTEN_SOCKET,
compositor->xwayland_unix_fd);
/* Make sure xwayland will recieve the above sockets in a finite
* time before unblocking the initialization mainloop since we are
* then going to immediately try and connect to those as the window
* manager. */
wl_client_flush (client);
/* At this point xwayland is all setup to start accepting
* connections so we can quit the transient initialization mainloop
* and unblock meta_wayland_init() to continue initializing mutter.
* */
g_main_loop_quit (compositor->init_loop);
compositor->init_loop = NULL;
}
static void
stage_destroy_cb (void)
{
meta_quit (META_EXIT_SUCCESS);
}
#define N_BUTTONS 5
static void
synthesize_motion_event (MetaWaylandCompositor *compositor,
const ClutterEvent *event)
{
/* We want to synthesize X events for mouse motion events so that we
don't have to rely on the X server's window position being
synched with the surface position. See the comment in
event_callback() in display.c */
MetaWaylandSeat *seat = compositor->seat;
MetaWaylandPointer *pointer = &seat->pointer;
MetaWaylandSurface *surface;
XGenericEventCookie generic_event;
XIDeviceEvent device_event;
unsigned char button_mask[(N_BUTTONS + 7) / 8] = { 0 };
MetaDisplay *display = meta_get_display ();
ClutterModifierType state;
int i;
generic_event.type = GenericEvent;
generic_event.serial = 0;
generic_event.send_event = False;
generic_event.display = display->xdisplay;
generic_event.extension = display->xinput_opcode;
generic_event.evtype = XI_Motion;
/* Mutter assumes the data for the event is already retrieved by GDK
* so we don't need the cookie */
generic_event.cookie = 0;
generic_event.data = &device_event;
memcpy (&device_event, &generic_event, sizeof (XGenericEvent));
device_event.time = clutter_event_get_time (event);
device_event.deviceid = clutter_event_get_device_id (event);
device_event.sourceid = 0; /* not used, not sure what this should be */
device_event.detail = 0;
device_event.root = DefaultRootWindow (display->xdisplay);
device_event.flags = 0 /* not used for motion events */;
if (compositor->implicit_grab_surface)
surface = compositor->implicit_grab_surface;
else
surface = pointer->current;
if (surface == pointer->current)
{
device_event.event_x = wl_fixed_to_int (pointer->current_x);
device_event.event_y = wl_fixed_to_int (pointer->current_y);
}
else if (surface && surface->window)
{
ClutterActor *window_actor =
CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
if (window_actor)
{
float ax, ay;
clutter_actor_transform_stage_point (window_actor,
wl_fixed_to_double (pointer->x),
wl_fixed_to_double (pointer->y),
&ax, &ay);
device_event.event_x = ax;
device_event.event_y = ay;
}
else
{
device_event.event_x = wl_fixed_to_double (pointer->x);
device_event.event_y = wl_fixed_to_double (pointer->y);
}
}
else
{
device_event.event_x = wl_fixed_to_double (pointer->x);
device_event.event_y = wl_fixed_to_double (pointer->y);
}
if (surface && surface->xid != None)
device_event.event = surface->xid;
else
device_event.event = device_event.root;
/* Mutter doesn't really know about the sub-windows. This assumes it
doesn't care either */
device_event.child = device_event.event;
device_event.root_x = wl_fixed_to_double (pointer->x);
device_event.root_y = wl_fixed_to_double (pointer->y);
state = clutter_event_get_state (event);
for (i = 0; i < N_BUTTONS; i++)
if ((state & (CLUTTER_BUTTON1_MASK << i)))
XISetMask (button_mask, i + 1);
device_event.buttons.mask_len = N_BUTTONS + 1;
device_event.buttons.mask = button_mask;
device_event.valuators.mask_len = 0;
device_event.valuators.mask = NULL;
device_event.valuators.values = NULL;
memset (&device_event.mods, 0, sizeof (device_event.mods));
device_event.mods.effective =
state & (CLUTTER_MODIFIER_MASK &
~(((CLUTTER_BUTTON1_MASK << N_BUTTONS) - 1) ^
(CLUTTER_BUTTON1_MASK - 1)));
memset (&device_event.group, 0, sizeof (device_event.group));
meta_display_handle_event (display, (XEvent *) &generic_event);
}
static gboolean
event_cb (ClutterActor *stage,
const ClutterEvent *event,
MetaWaylandCompositor *compositor)
{
MetaWaylandSeat *seat = compositor->seat;
MetaWaylandPointer *pointer = &seat->pointer;
MetaWaylandSurface *surface;
MetaDisplay *display;
meta_wayland_seat_handle_event (compositor->seat, event);
/* HACK: for now, the surfaces from Wayland clients aren't
integrated into Mutter's stacking and Mutter won't give them
focus on mouse clicks. As a hack to work around this we can just
give them input focus on mouse clicks so we can at least test the
keyboard support */
if (event->type == CLUTTER_BUTTON_PRESS)
{
surface = pointer->current;
/* Only focus surfaces that wouldn't be handled by the
corresponding X events */
if (surface && surface->xid == 0)
{
meta_wayland_keyboard_set_focus (&seat->keyboard, surface);
meta_wayland_data_device_set_keyboard_focus (seat);
}
}
display = meta_get_display ();
if (!display)
return FALSE;
switch (event->type)
{
case CLUTTER_BUTTON_PRESS:
if (compositor->implicit_grab_surface == NULL)
{
compositor->implicit_grab_button = event->button.button;
compositor->implicit_grab_surface = pointer->current;
}
return FALSE;
case CLUTTER_BUTTON_RELEASE:
if (event->type == CLUTTER_BUTTON_RELEASE &&
compositor->implicit_grab_surface &&
event->button.button == compositor->implicit_grab_button)
compositor->implicit_grab_surface = NULL;
return FALSE;
case CLUTTER_MOTION:
synthesize_motion_event (compositor, event);
return FALSE;
default:
return FALSE;
}
}
static gboolean
event_emission_hook_cb (GSignalInvocationHint *ihint,
guint n_param_values,
const GValue *param_values,
gpointer data)
{
MetaWaylandCompositor *compositor = data;
ClutterActor *actor;
ClutterEvent *event;
g_return_val_if_fail (n_param_values == 2, FALSE);
actor = g_value_get_object (param_values + 0);
event = g_value_get_boxed (param_values + 1);
if (actor == NULL)
return TRUE /* stay connected */;
/* If this event belongs to the corresponding grab for this event
* type then the captured-event signal won't be emitted so we have
* to manually forward it on */
switch (event->type)
{
/* Pointer events */
case CLUTTER_MOTION:
case CLUTTER_ENTER:
case CLUTTER_LEAVE:
case CLUTTER_BUTTON_PRESS:
case CLUTTER_BUTTON_RELEASE:
case CLUTTER_SCROLL:
if (actor == clutter_get_pointer_grab ())
event_cb (clutter_actor_get_stage (actor),
event,
compositor);
break;
/* Keyboard events */
case CLUTTER_KEY_PRESS:
case CLUTTER_KEY_RELEASE:
if (actor == clutter_get_keyboard_grab ())
event_cb (clutter_actor_get_stage (actor),
event,
compositor);
default:
break;
}
return TRUE /* stay connected */;
}
void
meta_wayland_init (void)
{
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
guint event_signal;
memset (compositor, 0, sizeof (MetaWaylandCompositor));
compositor->wayland_display = wl_display_create ();
if (compositor->wayland_display == NULL)
g_error ("failed to create wayland display");
wl_display_init_shm (compositor->wayland_display);
wl_list_init (&compositor->frame_callbacks);
if (!wl_global_create (compositor->wayland_display,
&wl_compositor_interface, 3,
compositor, compositor_bind))
g_error ("Failed to register wayland compositor object");
compositor->wayland_loop =
wl_display_get_event_loop (compositor->wayland_display);
compositor->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.
*
* At some point we could perhaps try and get the X protocol proxied
* over the wayland protocol so that we don't have to worry about
* synchronizing the two command streams. */
g_source_set_priority (compositor->wayland_event_source,
GDK_PRIORITY_EVENTS + 1);
g_source_attach (compositor->wayland_event_source, NULL);
clutter_wayland_set_compositor_display (compositor->wayland_display);
if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS)
g_error ("Failed to initialize Clutter");
compositor->stage = clutter_stage_new ();
clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor->stage), FALSE);
g_signal_connect_after (compositor->stage, "paint",
G_CALLBACK (paint_finished_cb), compositor);
g_signal_connect (compositor->stage, "destroy",
G_CALLBACK (stage_destroy_cb), NULL);
meta_wayland_data_device_manager_init (compositor->wayland_display);
compositor->seat = meta_wayland_seat_new (compositor->wayland_display);
g_signal_connect (compositor->stage,
"captured-event",
G_CALLBACK (event_cb),
compositor);
/* If something sets a grab on an actor then the captured event
* signal won't get emitted but we still want to see these events so
* we can update the cursor position. To make sure we see all events
* we also install an emission hook on the event signal */
event_signal = g_signal_lookup ("event", CLUTTER_TYPE_STAGE);
g_signal_add_emission_hook (event_signal,
0 /* detail */,
event_emission_hook_cb,
compositor, /* hook_data */
NULL /* data_destroy */);
meta_wayland_compositor_create_output (compositor, 0, 0, 1024, 600, 222, 125);
if (wl_global_create (compositor->wayland_display,
&wl_shell_interface, 1,
compositor, bind_shell) == NULL)
g_error ("Failed to register a global shell object");
clutter_actor_show (compositor->stage);
if (wl_display_add_socket (compositor->wayland_display, "wayland-0"))
g_error ("Failed to create socket");
wl_global_create (compositor->wayland_display,
&xserver_interface, 1,
compositor, bind_xserver);
/* We need a mapping from xids to wayland surfaces... */
compositor->window_surfaces = g_hash_table_new (g_int_hash, g_int_equal);
/* XXX: It's important that we only try and start xwayland after we
* have initialized EGL because EGL implements the "wl_drm"
* interface which xwayland requires to determine what drm device
* name it should use.
*
* By waiting until we've shown the stage above we ensure that the
* underlying GL resources for the surface have also been allocated
* and so EGL must be initialized by this point.
*/
if (!meta_xwayland_start (compositor))
g_error ("Failed to start X Wayland");
putenv (g_strdup_printf ("DISPLAY=:%d", compositor->xwayland_display_index));
/* We need to run a mainloop until we know xwayland has a binding
* for our xserver interface at which point we can assume it's
* ready to start accepting connections. */
compositor->init_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (compositor->init_loop);
}
void
meta_wayland_finalize (void)
{
meta_xwayland_stop (meta_wayland_compositor_get_default ());
}
void
meta_wayland_handle_sig_child (void)
{
int status;
pid_t pid = waitpid (-1, &status, WNOHANG);
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
/* The simplest measure to avoid infinitely re-spawning a crashing
* X server */
if (pid == compositor->xwayland_pid)
{
if (!WIFEXITED (status))
g_critical ("X Wayland crashed; aborting");
else
{
/* For now we simply abort if we see the server exit.
*
* In the future X will only be loaded lazily for legacy X support
* but for now it's a hard requirement. */
g_critical ("Spurious exit of X Wayland server");
}
}
}