#define COGL_ENABLE_EXPERIMENTAL_2_0_API #include #include #include #include #include #include #include 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; }