2012-01-07 17:21:32 -05:00
|
|
|
/*
|
|
|
|
* 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 "meta-wayland-private.h"
|
|
|
|
#include "meta-xwayland-private.h"
|
2012-01-18 18:03:23 -05:00
|
|
|
#include "meta-wayland-stage.h"
|
2012-01-07 17:21:32 -05:00
|
|
|
#include "meta-window-actor-private.h"
|
2013-05-03 13:51:22 -04:00
|
|
|
#include "meta-wayland-seat.h"
|
|
|
|
#include "meta-wayland-keyboard.h"
|
2013-06-15 20:49:03 -04:00
|
|
|
#include "meta-wayland-pointer.h"
|
2013-05-03 13:51:22 -04:00
|
|
|
#include "meta-wayland-data-device.h"
|
2013-08-13 06:57:41 -04:00
|
|
|
#include "meta-cursor-tracker-private.h"
|
2012-01-07 17:21:32 -05:00
|
|
|
#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;
|
|
|
|
|
2013-08-12 12:04:34 -04:00
|
|
|
/* X11 unmanaged window */
|
|
|
|
if (!surface)
|
|
|
|
return;
|
|
|
|
|
2012-01-07 17:21:32 -05:00
|
|
|
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 };
|
|
|
|
|
2013-08-12 12:04:34 -04:00
|
|
|
/* X11 unmanaged window */
|
|
|
|
if (!surface)
|
|
|
|
return;
|
|
|
|
|
2012-01-07 17:21:32 -05:00
|
|
|
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);
|
|
|
|
|
2013-08-12 12:04:34 -04:00
|
|
|
/* X11 unmanaged window */
|
|
|
|
if (!surface)
|
|
|
|
return;
|
|
|
|
|
2012-01-07 17:21:32 -05:00
|
|
|
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);
|
2013-08-12 12:04:34 -04:00
|
|
|
MetaWaylandCompositor *compositor;
|
|
|
|
|
|
|
|
/* X11 unmanaged window */
|
|
|
|
if (!surface)
|
|
|
|
return;
|
|
|
|
|
|
|
|
compositor = surface->compositor;
|
2012-01-07 17:21:32 -05:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2012-01-18 18:03:23 -05:00
|
|
|
else if (surface == compositor->seat->sprite)
|
|
|
|
meta_wayland_seat_update_sprite (compositor->seat);
|
2012-01-07 17:21:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2013-05-03 13:51:22 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
|
|
|
|
{
|
|
|
|
meta_wayland_seat_repick (compositor->seat,
|
|
|
|
get_time (),
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2013-08-12 12:04:34 -04:00
|
|
|
void
|
2012-01-07 17:21:32 -05:00
|
|
|
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->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);
|
|
|
|
|
2013-05-03 13:51:22 -04:00
|
|
|
meta_wayland_compositor_repick (compositor);
|
|
|
|
|
2013-08-12 12:04:34 -04:00
|
|
|
/* Check that repick didn't pick the freed surface */
|
|
|
|
g_assert (surface != compositor->seat->pointer.focus);
|
|
|
|
g_assert (surface != compositor->seat->keyboard.focus);
|
|
|
|
|
2013-05-03 13:51:22 -04:00
|
|
|
if (compositor->implicit_grab_surface == surface)
|
|
|
|
compositor->implicit_grab_surface = compositor->seat->pointer.current;
|
2013-08-12 12:04:34 -04:00
|
|
|
|
|
|
|
if (surface->resource)
|
|
|
|
wl_resource_set_user_data (surface->resource, NULL);
|
|
|
|
g_slice_free (MetaWaylandSurface, surface);
|
2012-01-07 17:21:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_wayland_surface_resource_destroy_cb (struct wl_resource *resource)
|
|
|
|
{
|
|
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
|
2013-08-12 12:04:34 -04:00
|
|
|
|
|
|
|
/* There are four cases here:
|
|
|
|
- An X11 unmanaged window -> surface is NULL, nothing to do
|
|
|
|
- An X11 unmanaged window, but we got the wayland event first ->
|
|
|
|
just clear the resource pointer
|
|
|
|
- A wayland surface without window (destroyed before set_toplevel) ->
|
|
|
|
need to free the surface itself
|
|
|
|
- A wayland window -> need to unmanage
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (surface)
|
|
|
|
{
|
|
|
|
surface->resource = NULL;
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
meta_wayland_surface_free (surface);
|
|
|
|
}
|
2012-01-07 17:21:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-06-15 20:49:03 -04:00
|
|
|
typedef struct _MetaWaylandGrab
|
|
|
|
{
|
|
|
|
MetaWaylandPointerGrab grab;
|
|
|
|
MetaWaylandShellSurface *shell_surface;
|
|
|
|
struct wl_listener shell_surface_destroy_listener;
|
|
|
|
MetaWaylandPointer *pointer;
|
|
|
|
} MetaWaylandGrab;
|
|
|
|
|
|
|
|
typedef struct _MetaWaylandMoveGrab
|
|
|
|
{
|
|
|
|
MetaWaylandGrab base;
|
|
|
|
wl_fixed_t dx, dy;
|
|
|
|
} MetaWaylandMoveGrab;
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroy_shell_surface_grab_listener (struct wl_listener *listener,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
MetaWaylandGrab *grab = wl_container_of (listener, grab,
|
|
|
|
shell_surface_destroy_listener);
|
|
|
|
grab->shell_surface = NULL;
|
|
|
|
|
|
|
|
/* XXX: Could we perhaps just stop the grab here so we don't have
|
|
|
|
* to consider grab->shell_surface becoming NULL in grab interface
|
|
|
|
* callbacks? */
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef enum _GrabCursor
|
|
|
|
{
|
|
|
|
GRAB_CURSOR_MOVE,
|
|
|
|
} GrabCursor;
|
|
|
|
|
|
|
|
static void
|
|
|
|
grab_pointer (MetaWaylandGrab *grab,
|
|
|
|
const MetaWaylandPointerGrabInterface *interface,
|
|
|
|
MetaWaylandShellSurface *shell_surface,
|
|
|
|
MetaWaylandPointer *pointer,
|
|
|
|
GrabCursor cursor)
|
|
|
|
{
|
|
|
|
/* TODO: popup_grab_end (pointer); */
|
|
|
|
|
|
|
|
grab->grab.interface = interface;
|
|
|
|
grab->shell_surface = shell_surface;
|
|
|
|
grab->shell_surface_destroy_listener.notify =
|
|
|
|
destroy_shell_surface_grab_listener;
|
|
|
|
wl_resource_add_destroy_listener (shell_surface->resource,
|
|
|
|
&grab->shell_surface_destroy_listener);
|
|
|
|
|
|
|
|
grab->pointer = pointer;
|
|
|
|
grab->grab.focus = shell_surface->surface;
|
|
|
|
|
|
|
|
meta_wayland_pointer_start_grab (pointer, &grab->grab);
|
|
|
|
|
|
|
|
/* TODO: send_grab_cursor (cursor); */
|
|
|
|
|
|
|
|
/* XXX: In Weston there is a desktop shell protocol which has
|
|
|
|
* a set_grab_surface request that's used to specify the surface
|
|
|
|
* that's focused here.
|
|
|
|
*
|
|
|
|
* TODO: understand why.
|
|
|
|
*
|
|
|
|
* XXX: For now we just focus the surface directly associated with
|
|
|
|
* the grab.
|
|
|
|
*/
|
|
|
|
meta_wayland_pointer_set_focus (pointer,
|
|
|
|
grab->shell_surface->surface,
|
|
|
|
wl_fixed_from_int (0),
|
|
|
|
wl_fixed_from_int (0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
release_pointer (MetaWaylandGrab *grab)
|
|
|
|
{
|
|
|
|
if (grab->shell_surface)
|
|
|
|
wl_list_remove (&grab->shell_surface_destroy_listener.link);
|
|
|
|
|
|
|
|
meta_wayland_pointer_end_grab (grab->pointer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
noop_grab_focus (MetaWaylandPointerGrab *grab,
|
|
|
|
MetaWaylandSurface *surface,
|
|
|
|
wl_fixed_t x,
|
|
|
|
wl_fixed_t y)
|
|
|
|
{
|
|
|
|
grab->focus = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
move_grab_motion (MetaWaylandPointerGrab *grab,
|
|
|
|
uint32_t time,
|
|
|
|
wl_fixed_t x,
|
|
|
|
wl_fixed_t y)
|
|
|
|
{
|
|
|
|
MetaWaylandMoveGrab *move = (MetaWaylandMoveGrab *)grab;
|
|
|
|
MetaWaylandPointer *pointer = move->base.pointer;
|
|
|
|
MetaWaylandShellSurface *shell_surface = move->base.shell_surface;
|
|
|
|
|
|
|
|
if (!shell_surface)
|
|
|
|
return;
|
|
|
|
|
|
|
|
meta_window_move (shell_surface->surface->window,
|
|
|
|
TRUE,
|
|
|
|
wl_fixed_to_int (pointer->x + move->dx),
|
|
|
|
wl_fixed_to_int (pointer->y + move->dy));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
move_grab_button (MetaWaylandPointerGrab *pointer_grab,
|
|
|
|
uint32_t time,
|
|
|
|
uint32_t button,
|
|
|
|
uint32_t state_w)
|
|
|
|
{
|
|
|
|
MetaWaylandGrab *grab =
|
|
|
|
wl_container_of (pointer_grab, grab, grab);
|
|
|
|
MetaWaylandMoveGrab *move = (MetaWaylandMoveGrab *)grab;
|
|
|
|
MetaWaylandPointer *pointer = grab->pointer;
|
|
|
|
enum wl_pointer_button_state state = state_w;
|
|
|
|
|
|
|
|
if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED)
|
|
|
|
{
|
|
|
|
release_pointer (grab);
|
|
|
|
g_slice_free (MetaWaylandMoveGrab, move);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MetaWaylandPointerGrabInterface move_grab_interface = {
|
|
|
|
noop_grab_focus,
|
|
|
|
move_grab_motion,
|
|
|
|
move_grab_button,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
start_surface_move (MetaWaylandShellSurface *shell_surface,
|
|
|
|
MetaWaylandSeat *seat)
|
|
|
|
{
|
|
|
|
MetaWaylandMoveGrab *move;
|
|
|
|
MetaRectangle rect;
|
|
|
|
|
|
|
|
g_return_if_fail (shell_surface != NULL);
|
|
|
|
|
|
|
|
/* TODO: check if the surface is fullscreen when we support fullscreen */
|
|
|
|
|
|
|
|
move = g_slice_new (MetaWaylandMoveGrab);
|
|
|
|
|
2013-08-12 12:13:45 -04:00
|
|
|
meta_window_get_input_rect (shell_surface->surface->window,
|
|
|
|
&rect);
|
2013-06-15 20:49:03 -04:00
|
|
|
|
|
|
|
move->dx = wl_fixed_from_int (rect.x) - seat->pointer.grab_x;
|
|
|
|
move->dy = wl_fixed_from_int (rect.y) - seat->pointer.grab_y;
|
|
|
|
|
|
|
|
grab_pointer (&move->base, &move_grab_interface, shell_surface,
|
|
|
|
&seat->pointer, GRAB_CURSOR_MOVE);
|
|
|
|
}
|
|
|
|
|
2012-01-07 17:21:32 -05:00
|
|
|
static void
|
|
|
|
shell_surface_move (struct wl_client *client,
|
|
|
|
struct wl_resource *resource,
|
2013-06-15 20:49:03 -04:00
|
|
|
struct wl_resource *seat_resource,
|
2012-01-07 17:21:32 -05:00
|
|
|
guint32 serial)
|
|
|
|
{
|
2013-06-15 20:49:03 -04:00
|
|
|
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
|
|
|
|
MetaWaylandShellSurface *shell_surface = wl_resource_get_user_data (resource);
|
|
|
|
|
|
|
|
if (seat->pointer.button_count == 0 ||
|
|
|
|
seat->pointer.grab_serial != serial ||
|
|
|
|
seat->pointer.focus != shell_surface->surface)
|
|
|
|
return;
|
|
|
|
|
|
|
|
start_surface_move (shell_surface, seat);
|
2012-01-07 17:21:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
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
|
|
|
|
stage_destroy_cb (void)
|
|
|
|
{
|
|
|
|
meta_quit (META_EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2013-05-03 13:51:22 -04:00
|
|
|
#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
|
2013-08-12 12:04:34 -04:00
|
|
|
integrated into Mutter's event handling and Mutter won't give them
|
2013-05-03 13:51:22 -04:00
|
|
|
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;
|
|
|
|
|
2013-08-12 12:04:34 -04:00
|
|
|
if (surface && surface->window &&
|
|
|
|
surface->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
|
2013-05-03 13:51:22 -04:00
|
|
|
{
|
2013-08-12 12:04:34 -04:00
|
|
|
MetaDisplay *display = meta_get_display ();
|
|
|
|
guint32 timestamp = meta_display_get_current_time_roundtrip (display);
|
|
|
|
|
|
|
|
meta_window_focus (surface->window, timestamp);
|
2013-05-03 13:51:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-13 06:57:41 -04:00
|
|
|
if (seat->cursor_tracker)
|
|
|
|
{
|
|
|
|
meta_cursor_tracker_update_position (seat->cursor_tracker,
|
|
|
|
wl_fixed_to_int (pointer->x),
|
|
|
|
wl_fixed_to_int (pointer->y));
|
|
|
|
|
|
|
|
if (pointer->current == NULL)
|
|
|
|
meta_cursor_tracker_revert_root (seat->cursor_tracker);
|
2012-01-18 18:03:23 -05:00
|
|
|
|
2013-08-13 06:57:41 -04:00
|
|
|
meta_cursor_tracker_queue_redraw (seat->cursor_tracker, stage);
|
|
|
|
}
|
2012-01-18 18:03:23 -05:00
|
|
|
|
2013-05-03 13:51:22 -04:00
|
|
|
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 */;
|
|
|
|
}
|
|
|
|
|
2012-01-07 17:21:32 -05:00
|
|
|
void
|
|
|
|
meta_wayland_init (void)
|
|
|
|
{
|
|
|
|
MetaWaylandCompositor *compositor = &_meta_wayland_compositor;
|
2013-05-03 13:51:22 -04:00
|
|
|
guint event_signal;
|
2012-01-07 17:21:32 -05:00
|
|
|
|
|
|
|
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");
|
|
|
|
|
2012-01-18 18:03:23 -05:00
|
|
|
compositor->stage = meta_wayland_stage_new ();
|
2012-01-07 17:21:32 -05:00
|
|
|
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);
|
|
|
|
|
2013-05-03 13:51:22 -04:00
|
|
|
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 */);
|
|
|
|
|
2012-01-07 17:21:32 -05:00
|
|
|
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");
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_wayland_finalize (void)
|
|
|
|
{
|
|
|
|
meta_xwayland_stop (meta_wayland_compositor_get_default ());
|
|
|
|
}
|