diff --git a/examples/Makefile.am b/examples/Makefile.am index a96ac6b9b..29a762d03 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -28,3 +28,9 @@ noinst_PROGRAMS += x11-foreign x11_foreign_SOURCES = x11-foreign.c x11_foreign_LDADD = $(common_ldadd) endif + +if SUPPORT_WAYLAND_EGL_SERVER +noinst_PROGRAMS += cogland +cogland_SOURCES = cogland.c +cogland_LDADD = $(common_ldadd) +endif diff --git a/examples/cogland.c b/examples/cogland.c new file mode 100644 index 000000000..551addc98 --- /dev/null +++ b/examples/cogland.c @@ -0,0 +1,543 @@ +#include +#include +#include +#include +#include + +#include + +CoglColor black; + +typedef struct _CoglandCompositor CoglandCompositor; + +typedef struct +{ + struct wl_buffer *wayland_buffer; + CoglTexture2D *texture; + GList *surfaces_attached_to; +} CoglandBuffer; + +typedef struct +{ + CoglandCompositor *compositor; + + struct wl_surface wayland_surface; + int x; + int y; + CoglandBuffer *buffer; +} CoglandSurface; + +typedef struct +{ + struct wl_object wayland_output; + + int x; + int y; + int width; + int height; + CoglOnscreen *onscreen; +} CoglandOutput; + +typedef struct +{ + GSource source; + GPollFD pfd; + struct wl_event_loop *loop; +} WaylandEventSource; + +struct _CoglandCompositor +{ + struct wl_display *wayland_display; + struct wl_compositor wayland_compositor; + struct wl_shm *wayland_shm; + struct wl_event_loop *wayland_loop; + + CoglDisplay *cogl_display; + CoglContext *cogl_context; + + int virtual_width; + int virtual_height; + GList *outputs; + + CoglPrimitive *triangle; + + GSource *wayland_event_source; + + GList *surfaces; +}; + +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 CoglandBuffer * +cogland_buffer_new (struct wl_buffer *wayland_buffer) +{ + CoglandBuffer *buffer = g_slice_new (CoglandBuffer); + + buffer->wayland_buffer = wayland_buffer; + buffer->texture = NULL; + buffer->surfaces_attached_to = NULL; + + return buffer; +} + +static void +cogland_buffer_free (CoglandBuffer *buffer) +{ + GList *l; + + buffer->wayland_buffer->user_data = NULL; + + for (l = buffer->surfaces_attached_to; l; l = l->next) + { + CoglandSurface *surface = l->data; + surface->buffer = NULL; + } + + if (buffer->texture) + cogl_object_unref (buffer->texture); + + g_list_free (buffer->surfaces_attached_to); + g_slice_free (CoglandBuffer, buffer); +} + +static void +shm_buffer_created (struct wl_buffer *wayland_buffer) +{ + wayland_buffer->user_data = cogland_buffer_new (wayland_buffer); +} + +static CoglPixelFormat +get_buffer_format (struct wl_buffer *wayland_buffer) +{ + struct wl_compositor *compositor = wayland_buffer->compositor; + struct wl_visual *visual = wayland_buffer->visual; +#if G_BYTE_ORDER == G_BIG_ENDIAN + if (visual == &compositor->premultiplied_argb_visual) + return COGL_PIXEL_FORMAT_ARGB_8888_PRE; + else if (visual == &compositor->argb_visual) + return COGL_PIXEL_FORMAT_ARGB_8888; + else if (visual == &compositor->rgb_visual) + return COGL_PIXEL_FORMAT_RGB_888; +#elif G_BYTE_ORDER == G_LITTLE_ENDIAN + if (visual == &compositor->premultiplied_argb_visual) + return COGL_PIXEL_FORMAT_BGRA_8888_PRE; + else if (visual == &compositor->argb_visual) + return COGL_PIXEL_FORMAT_BGRA_8888; + else if (visual == &compositor->rgb_visual) + return COGL_PIXEL_FORMAT_BGR_888; +#endif + else + g_return_val_if_reached (COGL_PIXEL_FORMAT_ANY); +} + +static void +shm_buffer_damaged (struct wl_buffer *wayland_buffer, + gint32 x, + gint32 y, + gint32 width, + gint32 height) +{ + CoglandBuffer *buffer = wayland_buffer->user_data; + + if (buffer->texture) + { + cogl_texture_set_region (buffer->texture, + x, y, + x, y, + width, height, + width, height, + get_buffer_format (wayland_buffer), + wl_shm_buffer_get_stride (wayland_buffer), + wl_shm_buffer_get_data (wayland_buffer)); + } +} + +static void +shm_buffer_destroyed (struct wl_buffer *wayland_buffer) +{ + if (wayland_buffer->user_data) + cogland_buffer_free ((CoglandBuffer *)wayland_buffer->user_data); +} + +const static struct wl_shm_callbacks shm_callbacks = { + shm_buffer_created, + shm_buffer_damaged, + shm_buffer_destroyed +}; + +static void +cogland_surface_destroy (struct wl_client *wayland_client, + struct wl_surface *wayland_surface) +{ + wl_resource_destroy (&wayland_surface->resource, wayland_client, get_time ()); +} + +static void +cogland_surface_detach_buffer (CoglandSurface *surface) +{ + CoglandBuffer *buffer = surface->buffer; + + if (buffer) + { + buffer->surfaces_attached_to = + g_list_remove (buffer->surfaces_attached_to, surface); + if (buffer->surfaces_attached_to == NULL) + cogland_buffer_free (buffer); + surface->buffer = NULL; + } +} + +static void +cogland_surface_attach_buffer (struct wl_client *wayland_client, + struct wl_surface *wayland_surface, + struct wl_buffer *wayland_buffer, + gint32 dx, gint32 dy) +{ + CoglandBuffer *buffer = wayland_buffer->user_data; + CoglandSurface *surface = container_of (wayland_surface, + CoglandSurface, + wayland_surface); + CoglandCompositor *compositor = surface->compositor; + + cogland_surface_detach_buffer (surface); + + /* XXX: it seems like for shm buffers we will have been notified of + * the buffer already via the callbacks, but for drm buffers I guess + * this will be the first we know of them? */ + if (!buffer) + { + buffer = cogland_buffer_new (wayland_buffer); + wayland_buffer->user_data = buffer; + } + + /* wayland-drm.c: drm_create_buffer doesn't fill this in for us...*/ + if (!wayland_buffer->compositor) + wayland_buffer->compositor = &compositor->wayland_compositor; + + 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 (!buffer->texture) + { + GError *error = NULL; + + buffer->texture = + cogl_wayland_texture_2d_new_from_buffer (compositor->cogl_context, + wayland_buffer, + &error); + if (!buffer->texture) + g_error ("Failed to create texture_2d from wayland buffer: %s", + error->message); + } + + surface->buffer = buffer; +} + +static void +cogland_surface_map_toplevel (struct wl_client *client, + struct wl_surface *surface) +{ +} + +static void +cogland_surface_map_transient (struct wl_client *client, + struct wl_surface *surface, + struct wl_surface *parent, + gint32 dx, + gint32 dy, + guint32 flags) +{ +} + +static void +cogland_surface_map_fullscreen (struct wl_client *client, + struct wl_surface *surface) +{ +} + +static void +cogland_surface_damage (struct wl_client *client, + struct wl_surface *surface, + gint32 x, + gint32 y, + gint32 width, + gint32 height) +{ +} + +const struct wl_surface_interface cogland_surface_interface = { + cogland_surface_destroy, + cogland_surface_attach_buffer, + cogland_surface_map_toplevel, + cogland_surface_map_transient, + cogland_surface_map_fullscreen, + cogland_surface_damage +}; + +static void +cogland_surface_free (CoglandSurface *surface) +{ + CoglandCompositor *compositor = surface->compositor; + compositor->surfaces = g_list_remove (compositor->surfaces, surface); + cogland_surface_detach_buffer (surface); + g_slice_free (CoglandSurface, surface); +} + +static void +cogland_surface_resource_destroy_cb (struct wl_resource *wayland_resource, + struct wl_client *wayland_client) +{ + CoglandSurface *surface = + container_of (wayland_resource, CoglandSurface, wayland_surface.resource); + cogland_surface_free (surface); +} + +static void +cogland_compositor_create_surface (struct wl_client *wayland_client, + struct wl_compositor *wayland_compositor, + guint32 wayland_id) +{ + CoglandCompositor *compositor = container_of (wayland_compositor, + CoglandCompositor, + wayland_compositor); + CoglandSurface *surface = g_slice_new0 (CoglandSurface); + surface->compositor = compositor; + + surface->wayland_surface.resource.destroy = + cogland_surface_resource_destroy_cb; + + surface->wayland_surface.resource.object.id = wayland_id; + surface->wayland_surface.resource.object.interface = &wl_surface_interface; + surface->wayland_surface.resource.object.implementation = + (void (**)(void)) &cogland_surface_interface; + surface->wayland_surface.client = wayland_client; + + wl_client_add_resource (wayland_client, &surface->wayland_surface.resource); + + compositor->surfaces = g_list_prepend (compositor->surfaces, + surface); +} + +const static struct wl_compositor_interface cogland_compositor_interface = +{ + cogland_compositor_create_surface, +}; + +static void +cogland_output_post_geometry (struct wl_client *wayland_client, + struct wl_object *wayland_output, + guint32 version) +{ + CoglandOutput *output = + container_of (wayland_output, CoglandOutput, wayland_output); + + wl_client_post_event (wayland_client, + wayland_output, + WL_OUTPUT_GEOMETRY, + output->x, output->y, + output->width, output->height); +} + +static void +cogland_compositor_create_output (CoglandCompositor *compositor, + int x, + int y, + int width, + int height) +{ + CoglandOutput *output = g_slice_new0 (CoglandOutput); + CoglFramebuffer *fb; + GError *error = NULL; + + output->wayland_output.interface = &wl_output_interface; + + wl_display_add_object (compositor->wayland_display, &output->wayland_output); + wl_display_add_global (compositor->wayland_display, &output->wayland_output, + cogland_output_post_geometry); + + output->onscreen = cogl_onscreen_new (compositor->cogl_context, + width, height); + /* Eventually there will be an implicit allocate on first use so this + * will become optional... */ + fb = COGL_FRAMEBUFFER (output->onscreen); + if (!cogl_framebuffer_allocate (fb, &error)) + g_error ("Failed to allocate framebuffer: %s\n", error->message); + + cogl_onscreen_show (output->onscreen); +#if 0 + cogl_framebuffer_set_viewport (fb, x, y, width, height); +#else + cogl_push_framebuffer (fb); + cogl_set_viewport (-x, -y, + compositor->virtual_width, + compositor->virtual_height); + cogl_pop_framebuffer (); +#endif + + compositor->outputs = g_list_prepend (compositor->outputs, output); +} + +static gboolean +paint_cb (void *user_data) +{ + CoglandCompositor *compositor = user_data; + GList *l; + + for (l = compositor->outputs; l; l = l->next) + { + CoglandOutput *output = l->data; + CoglFramebuffer *fb = COGL_FRAMEBUFFER (output->onscreen); + GList *l2; + + cogl_push_framebuffer (fb); + +#if 0 + cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR); +#else + cogl_clear (&black, COGL_BUFFER_BIT_COLOR); +#endif + cogl_primitive_draw (compositor->triangle); + + for (l2 = compositor->surfaces; l2; l2 = l2->next) + { + CoglandSurface *surface = l2->data; + + if (surface->buffer) + { + CoglTexture2D *texture = surface->buffer->texture; + cogl_set_source_texture (texture); + cogl_rectangle (-1, 1, 1, -1); + } + wl_display_post_frame (compositor->wayland_display, + &surface->wayland_surface, get_time ()); + } + cogl_framebuffer_swap_buffers (fb); + + + cogl_pop_framebuffer (); + + } + + return TRUE; +} + +int +main (int argc, char **argv) +{ + CoglandCompositor compositor; + GMainLoop *loop; + GError *error = NULL; + CoglVertexP2C4 triangle_vertices[] = { + {0, 0.7, 0xff, 0x00, 0x00, 0x80}, + {-0.7, -0.7, 0x00, 0xff, 0x00, 0xff}, + {0.7, -0.7, 0x00, 0x00, 0xff, 0xff} + }; + + memset (&compositor, 0, sizeof (compositor)); + + compositor.wayland_display = wl_display_create (); + if (compositor.wayland_display == NULL) + g_error ("failed to create wayland display"); + + if (wl_compositor_init (&compositor.wayland_compositor, + &cogland_compositor_interface, + compositor.wayland_display) < 0) + g_error ("Failed to init wayland compositor"); + + 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); + + compositor.cogl_display = cogl_display_new (NULL, NULL); + cogl_wayland_display_set_compositor_display (compositor.cogl_display, + compositor.wayland_display); + + compositor.cogl_context = cogl_context_new (compositor.cogl_display, &error); + if (!compositor.cogl_context) + g_error ("Failed to create a Cogl context: %s\n", error->message); + + compositor.virtual_width = 640; + compositor.virtual_height = 480; + + /* Emulate compositing with multiple monitors... */ + cogland_compositor_create_output (&compositor, 0, 0, 320, 240); + cogland_compositor_create_output (&compositor, 320, 0, 320, 240); + cogland_compositor_create_output (&compositor, 0, 240, 320, 240); + cogland_compositor_create_output (&compositor, 320, 240, 320, 240); + + if (wl_display_add_socket (compositor.wayland_display, "wayland-0")) + g_error ("Failed to create socket"); + + compositor.triangle = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES, + 3, triangle_vertices); + + g_timeout_add (16, paint_cb, &compositor); + + g_main_loop_run (loop); + + return 0; +}