mutter/tests/interactive/test-wayland-surface.c
Robert Bragg e5bde0b074 tests: Adds a simple wayland compositor example
This adds an extremely minimal wayland compositor to tests/interactive
to test the ClutterWaylandSurface actor. Currently this minimal
compositor doesn't support any input, it simply paints client surfaces
fixed at the top-left of the stage.

Reviewed-by: Emmanuele Bassi <ebassi@linux.intel.com>
2011-12-08 16:13:37 +00:00

568 lines
15 KiB
C

#define COGL_ENABLE_EXPERIMENTAL_2_0_API
#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 <wayland-server.h>
typedef struct _TWSCompositor TWSCompositor;
typedef struct
{
struct wl_buffer *wayland_buffer;
GList *surfaces_attached_to;
} TWSBuffer;
typedef struct
{
TWSCompositor *compositor;
struct wl_surface wayland_surface;
int x;
int y;
TWSBuffer *buffer;
ClutterActor *actor;
} TWSSurface;
typedef struct
{
guint32 flags;
int width;
int height;
int refresh;
} TWSMode;
typedef struct
{
struct wl_object wayland_output;
int x;
int y;
int width_mm;
int height_mm;
/* XXX: with sliced stages we'd reference a CoglFramebuffer here. */
GList *modes;
} TWSOutput;
typedef struct
{
GSource source;
GPollFD pfd;
struct wl_event_loop *loop;
} WaylandEventSource;
typedef struct
{
struct wl_resource resource;
} TWSFrameCallback;
struct _TWSCompositor
{
struct wl_display *wayland_display;
struct wl_shm *wayland_shm;
struct wl_event_loop *wayland_loop;
ClutterActor *stage;
GList *outputs;
GSource *wayland_event_source;
GList *surfaces;
GArray *frame_callbacks;
};
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)
{
*timeout = -1;
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;
wl_event_loop_dispatch (source->loop, 0);
return TRUE;
}
static GSourceFuncs wayland_event_source_funcs =
{
wayland_event_source_prepare,
wayland_event_source_check,
wayland_event_source_dispatch,
NULL
};
GSource *
wayland_event_source_new (struct wl_event_loop *loop)
{
WaylandEventSource *source;
source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
sizeof (WaylandEventSource));
source->loop = loop;
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 TWSBuffer *
tws_buffer_new (struct wl_buffer *wayland_buffer)
{
TWSBuffer *buffer = g_slice_new (TWSBuffer);
buffer->wayland_buffer = wayland_buffer;
buffer->surfaces_attached_to = NULL;
return buffer;
}
static void
tws_buffer_free (TWSBuffer *buffer)
{
GList *l;
buffer->wayland_buffer->user_data = NULL;
for (l = buffer->surfaces_attached_to; l; l = l->next)
{
TWSSurface *surface = l->data;
surface->buffer = NULL;
}
g_list_free (buffer->surfaces_attached_to);
g_slice_free (TWSBuffer, buffer);
}
static void
shm_buffer_created (struct wl_buffer *wayland_buffer)
{
wayland_buffer->user_data = tws_buffer_new (wayland_buffer);
}
static void
shm_buffer_damaged (struct wl_buffer *wayland_buffer,
gint32 x,
gint32 y,
gint32 width,
gint32 height)
{
TWSBuffer *buffer = wayland_buffer->user_data;
GList *l;
for (l = buffer->surfaces_attached_to; l; l = l->next)
{
TWSSurface *surface = l->data;
ClutterWaylandSurface *surface_actor =
CLUTTER_WAYLAND_SURFACE (surface->actor);
clutter_wayland_surface_damage_buffer (surface_actor,
wayland_buffer,
x, y, width, height);
}
}
static void
shm_buffer_destroyed (struct wl_buffer *wayland_buffer)
{
if (wayland_buffer->user_data)
tws_buffer_free ((TWSBuffer *)wayland_buffer->user_data);
}
const static struct wl_shm_callbacks shm_callbacks = {
shm_buffer_created,
shm_buffer_damaged,
shm_buffer_destroyed
};
static void
tws_surface_destroy (struct wl_client *wayland_client,
struct wl_resource *wayland_resource)
{
wl_resource_destroy (wayland_resource, get_time ());
}
static void
tws_surface_detach_buffer (TWSSurface *surface)
{
TWSBuffer *buffer = surface->buffer;
if (buffer)
{
buffer->surfaces_attached_to =
g_list_remove (buffer->surfaces_attached_to, surface);
if (buffer->surfaces_attached_to == NULL)
tws_buffer_free (buffer);
surface->buffer = NULL;
}
}
static void
tws_surface_attach_buffer (struct wl_client *wayland_client,
struct wl_resource *wayland_surface_resource,
struct wl_resource *wayland_buffer_resource,
gint32 dx, gint32 dy)
{
struct wl_buffer *wayland_buffer = wayland_buffer_resource->data;
TWSBuffer *buffer = wayland_buffer->user_data;
TWSSurface *surface = wayland_surface_resource->data;
TWSCompositor *compositor = surface->compositor;
ClutterWaylandSurface *surface_actor;
/* XXX: in the case where we are reattaching the same buffer we can
* simply bail out. Note this is important because if we don't bail
* out then the _detach_buffer will actually end up destroying the
* buffer we're trying to attach. */
if (buffer && surface->buffer == buffer)
return;
tws_surface_detach_buffer (surface);
/* XXX: we will have been notified of shm buffers already via the
* callbacks, but this will be the first we know of drm buffers */
if (!buffer)
{
buffer = tws_buffer_new (wayland_buffer);
wayland_buffer->user_data = buffer;
}
g_return_if_fail (g_list_find (buffer->surfaces_attached_to, surface) == NULL);
buffer->surfaces_attached_to = g_list_prepend (buffer->surfaces_attached_to,
surface);
if (!surface->actor)
{
surface->actor = clutter_wayland_surface_new (&surface->wayland_surface);
clutter_container_add_actor (CLUTTER_CONTAINER (compositor->stage),
surface->actor);
}
surface_actor = CLUTTER_WAYLAND_SURFACE (surface->actor);
if (!clutter_wayland_surface_attach_buffer (surface_actor, wayland_buffer,
NULL))
g_warning ("Failed to attach buffer to ClutterWaylandSurface");
surface->buffer = buffer;
}
static void
tws_surface_damage (struct wl_client *client,
struct wl_resource *resource,
gint32 x,
gint32 y,
gint32 width,
gint32 height)
{
}
static void
destroy_frame_callback (struct wl_resource *callback_resource)
{
TWSFrameCallback *callback = callback_resource->data;
g_slice_free (TWSFrameCallback, callback);
}
static void
tws_surface_frame (struct wl_client *client,
struct wl_resource *surface_resource,
guint32 callback_id)
{
TWSFrameCallback *callback;
TWSSurface *surface = surface_resource->data;
callback = g_slice_new0 (TWSFrameCallback);
callback->resource.object.interface = &wl_callback_interface;
callback->resource.object.id = callback_id;
callback->resource.destroy = destroy_frame_callback;
callback->resource.data = callback;
wl_client_add_resource (client, &callback->resource);
g_array_append_val (surface->compositor->frame_callbacks, callback);
}
const struct wl_surface_interface tws_surface_interface = {
tws_surface_destroy,
tws_surface_attach_buffer,
tws_surface_damage,
tws_surface_frame
};
static void
tws_surface_free (TWSSurface *surface)
{
TWSCompositor *compositor = surface->compositor;
compositor->surfaces = g_list_remove (compositor->surfaces, surface);
tws_surface_detach_buffer (surface);
clutter_actor_destroy (surface->actor);
g_slice_free (TWSSurface, surface);
}
static void
tws_surface_resource_destroy_cb (struct wl_resource *wayland_surface_resource)
{
TWSSurface *surface = wayland_surface_resource->data;
tws_surface_free (surface);
}
static void
tws_compositor_create_surface (struct wl_client *wayland_client,
struct wl_resource *wayland_compositor_resource,
guint32 id)
{
TWSCompositor *compositor = wayland_compositor_resource->data;
TWSSurface *surface = g_slice_new0 (TWSSurface);
surface->compositor = compositor;
surface->wayland_surface.resource.destroy =
tws_surface_resource_destroy_cb;
surface->wayland_surface.resource.object.id = id;
surface->wayland_surface.resource.object.interface = &wl_surface_interface;
surface->wayland_surface.resource.object.implementation =
(void (**)(void)) &tws_surface_interface;
surface->wayland_surface.resource.data = surface;
wl_client_add_resource (wayland_client, &surface->wayland_surface.resource);
compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
}
static void
bind_output (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
TWSOutput *output = data;
struct wl_resource *resource =
wl_client_add_object (client, &wl_output_interface, NULL, id, data);
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)
{
TWSMode *mode = l->data;
wl_resource_post_event (resource,
WL_OUTPUT_MODE,
mode->flags,
mode->width,
mode->height,
mode->refresh);
}
}
static void
tws_compositor_create_output (TWSCompositor *compositor,
int x,
int y,
int width_mm,
int height_mm)
{
TWSOutput *output = g_slice_new0 (TWSOutput);
output->wayland_output.interface = &wl_output_interface;
output->x = x;
output->y = y;
output->width_mm = width_mm;
output->height_mm = height_mm;
wl_display_add_global (compositor->wayland_display,
&wl_output_interface,
output,
bind_output);
/* 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_mm, height_mm);
compositor->outputs = g_list_prepend (compositor->outputs, output);
}
const static struct wl_compositor_interface tws_compositor_interface = {
tws_compositor_create_surface,
};
static void
paint_finished_cb (ClutterActor *self, void *user_data)
{
TWSCompositor *compositor = user_data;
int i;
for (i = 0; i < compositor->frame_callbacks->len; i++)
{
TWSFrameCallback *callback =
g_array_index (compositor->frame_callbacks, TWSFrameCallback *, i);
wl_resource_post_event (&callback->resource,
WL_CALLBACK_DONE, get_time ());
wl_resource_destroy (&callback->resource, 0);
}
g_array_set_size (compositor->frame_callbacks, 0);
}
static void
compositor_bind (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
TWSCompositor *compositor = data;
wl_client_add_object (client, &wl_compositor_interface,
&tws_compositor_interface, id, compositor);
}
static void
shell_move(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource,
struct wl_resource *input_resource,
guint32 time)
{
}
static void
shell_resize (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource,
struct wl_resource *input_resource,
guint32 time,
guint32 edges)
{
}
static void
shell_set_toplevel (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource)
{
}
static void
shell_set_transient (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource,
struct wl_resource *parent_resource,
int x, int y, uint32_t flags)
{
}
static void
shell_set_fullscreen (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *surface_resource)
{
}
static const struct wl_shell_interface tws_shell_interface =
{
shell_move,
shell_resize,
shell_set_toplevel,
shell_set_transient,
shell_set_fullscreen
};
static void
bind_shell (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
wl_client_add_object (client, &wl_shell_interface,
&tws_shell_interface, id, data);
}
G_MODULE_EXPORT int
test_wayland_surface_main (int argc, char **argv)
{
TWSCompositor compositor;
GMainLoop *loop;
memset (&compositor, 0, sizeof (compositor));
compositor.wayland_display = wl_display_create ();
if (compositor.wayland_display == NULL)
g_error ("failed to create wayland display");
compositor.frame_callbacks = g_array_new (FALSE, FALSE, sizeof (void *));
if (!wl_display_add_global (compositor.wayland_display,
&wl_compositor_interface,
&compositor,
compositor_bind))
g_error ("Failed to register wayland compositor object");
compositor.wayland_shm = wl_shm_init (compositor.wayland_display,
&shm_callbacks);
if (!compositor.wayland_shm)
g_error ("Failed to allocate setup wayland shm callbacks");
loop = g_main_loop_new (NULL, FALSE);
compositor.wayland_loop =
wl_display_get_event_loop (compositor.wayland_display);
compositor.wayland_event_source =
wayland_event_source_new (compositor.wayland_loop);
g_source_attach (compositor.wayland_event_source, NULL);
clutter_wayland_set_compositor_display (compositor.wayland_display);
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
compositor.stage = clutter_stage_get_default ();
clutter_stage_set_user_resizable (CLUTTER_STAGE (compositor.stage), FALSE);
g_signal_connect_after (compositor.stage, "paint",
G_CALLBACK (paint_finished_cb), &compositor);
tws_compositor_create_output (&compositor, 0, 0, 800, 600);
if (wl_display_add_global (compositor.wayland_display, &wl_shell_interface,
&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");
g_main_loop_run (loop);
return 0;
}