/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010, 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * Authors: * Matthew Allum * Robert Bragg * Kristian Høgsberg */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "clutter-debug.h" #include "clutter-private.h" #include "clutter-main.h" #include "clutter-stage-private.h" #include "wayland/clutter-backend-wayland.h" #include "wayland/clutter-device-manager-wayland.h" #include "wayland/clutter-event-wayland.h" #include "wayland/clutter-stage-wayland.h" #include "cogl/clutter-stage-cogl.h" #include #include #include #define clutter_backend_wayland_get_type _clutter_backend_wayland_get_type G_DEFINE_TYPE (ClutterBackendWayland, clutter_backend_wayland, CLUTTER_TYPE_BACKEND); static void clutter_backend_wayland_load_cursor (ClutterBackendWayland *backend_wayland); static void clutter_backend_wayland_dispose (GObject *gobject) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (gobject); if (backend_wayland->device_manager) { g_object_unref (backend_wayland->device_manager); backend_wayland->device_manager = NULL; } if (backend_wayland->cursor_buffer) { wl_buffer_destroy (backend_wayland->cursor_buffer); backend_wayland->cursor_buffer = NULL; } G_OBJECT_CLASS (clutter_backend_wayland_parent_class)->dispose (gobject); } static void display_handle_global (struct wl_display *display, uint32_t id, const char *interface, uint32_t version, void *data) { ClutterBackendWayland *backend_wayland = data; if (strcmp (interface, "wl_compositor") == 0) backend_wayland->wayland_compositor = wl_display_bind (display, id, &wl_compositor_interface); else if (strcmp (interface, "wl_input_device") == 0) { ClutterDeviceManager *device_manager = backend_wayland->device_manager; _clutter_device_manager_wayland_add_input_group (device_manager, id); } else if (strcmp (interface, "wl_shell") == 0) { backend_wayland->wayland_shell = wl_display_bind (display, id, &wl_shell_interface); } else if (strcmp (interface, "wl_shm") == 0) backend_wayland->wayland_shm = wl_display_bind (display, id, &wl_shm_interface); } static gboolean clutter_backend_wayland_post_parse (ClutterBackend *backend, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); /* TODO: expose environment variable/commandline option for this... */ backend_wayland->wayland_display = wl_display_connect (NULL); if (!backend_wayland->wayland_display) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Failed to open Wayland display socket"); return FALSE; } backend_wayland->wayland_source = _clutter_event_source_wayland_new (backend_wayland->wayland_display); g_source_attach (backend_wayland->wayland_source, NULL); /* XXX: We require the device manager to exist as soon as we connect to the * compositor and setup an event handler because we will immediately be * notified of the available input devices which need to be associated with * the device-manager. * * FIXME: At some point we could perhaps just collapse the * _clutter_backend_post_parse(), and _clutter_backend_init_events() * functions into one called something like _clutter_backend_init() which * would allow the real backend to manage the precise order of * initialization. */ backend_wayland->device_manager = _clutter_device_manager_wayland_new (backend); /* Set up listener so we'll catch all events. */ wl_display_add_global_listener (backend_wayland->wayland_display, display_handle_global, backend_wayland); /* Wait until we have been notified about the compositor and shell objects */ while (!(backend_wayland->wayland_compositor && backend_wayland->wayland_shell)) wl_display_roundtrip (backend_wayland->wayland_display); /* We need the shm object before we can create the cursor */ clutter_backend_wayland_load_cursor (backend_wayland); return TRUE; } static CoglRenderer * clutter_backend_wayland_get_renderer (ClutterBackend *backend, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); CoglRenderer *renderer; CLUTTER_NOTE (BACKEND, "Creating a new wayland renderer"); renderer = cogl_renderer_new (); cogl_wayland_renderer_set_foreign_display (renderer, backend_wayland->wayland_display); cogl_wayland_renderer_set_foreign_compositor (renderer, backend_wayland->wayland_compositor); cogl_wayland_renderer_set_foreign_shell (renderer, backend_wayland->wayland_shell); return renderer; } static CoglDisplay * clutter_backend_wayland_get_display (ClutterBackend *backend, CoglRenderer *renderer, CoglSwapChain *swap_chain, GError **error) { CoglOnscreenTemplate *onscreen_template = NULL; CoglDisplay *display; onscreen_template = cogl_onscreen_template_new (swap_chain); /* XXX: I have some doubts that this is a good design. * Conceptually should we be able to check an onscreen_template * without more details about the CoglDisplay configuration? */ if (!cogl_renderer_check_onscreen_template (renderer, onscreen_template, error)) goto error; display = cogl_display_new (renderer, onscreen_template); return display; error: if (onscreen_template) cogl_object_unref (onscreen_template); return NULL; } static void clutter_backend_wayland_class_init (ClutterBackendWaylandClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); gobject_class->dispose = clutter_backend_wayland_dispose; backend_class->stage_window_type = CLUTTER_TYPE_STAGE_WAYLAND; backend_class->post_parse = clutter_backend_wayland_post_parse; backend_class->get_renderer = clutter_backend_wayland_get_renderer; backend_class->get_display = clutter_backend_wayland_get_display; } /* * clutter_backend_wayland_load_cursor and the two functions below were copied * from GTK+ and adapted for clutter */ static void set_pixbuf (GdkPixbuf *pixbuf, unsigned char *map, int width, int height) { int stride, i, n_channels; unsigned char *pixels, *end, *argb_pixels, *s, *d; stride = gdk_pixbuf_get_rowstride (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); argb_pixels = map; #define MULT(_d,c,a,t) \ do { t = c * a + 0x7f; _d = ((t >> 8) + t) >> 8; } while (0) if (n_channels == 4) { for (i = 0; i < height; i++) { s = pixels + i * stride; end = s + width * 4; d = argb_pixels + i * width * 4; while (s < end) { unsigned int t; MULT(d[0], s[2], s[3], t); MULT(d[1], s[1], s[3], t); MULT(d[2], s[0], s[3], t); d[3] = s[3]; s += 4; d += 4; } } } else if (n_channels == 3) { for (i = 0; i < height; i++) { s = pixels + i * stride; end = s + width * 3; d = argb_pixels + i * width * 4; while (s < end) { d[0] = s[2]; d[1] = s[1]; d[2] = s[0]; d[3] = 0xff; s += 3; d += 4; } } } } static struct wl_buffer * create_cursor (ClutterBackendWayland *backend_wayland, GdkPixbuf *pixbuf) { int stride, fd; char *filename; GError *error = NULL; struct wl_buffer *buffer; gint width, height; gsize size; unsigned char *map; width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); stride = width * 4; size = stride * height; fd = g_file_open_tmp ("wayland-shm-XXXXXX", &filename, &error); if (fd < 0) { g_critical (G_STRLOC ": Opening temporary file failed: %s", error->message); g_error_free (error); return NULL; } unlink (filename); g_free (filename); if (ftruncate (fd, size) < 0) { g_critical (G_STRLOC ": Setting the size of temporary file failed: %s", g_strerror (errno)); close (fd); return NULL; } map = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { g_critical (G_STRLOC ": Memory mapping file failed: %s", g_strerror (errno)); close (fd); return NULL; } set_pixbuf (pixbuf, map, width, height); buffer = wl_shm_create_buffer (backend_wayland->wayland_shm, fd, width, height, stride, WL_SHM_FORMAT_ARGB32); close(fd); munmap (map, size); return buffer; } static void clutter_backend_wayland_load_cursor (ClutterBackendWayland *backend_wayland) { const gchar * const *directories; gint j; GdkPixbuf *pixbuf = NULL; GError *error = NULL; directories = g_get_system_data_dirs(); for (j = 0; directories[j] != NULL; j++) { gchar *filename; filename = g_build_filename (directories[j], "weston", "left_ptr.png", NULL); if (g_file_test (filename, G_FILE_TEST_EXISTS)) { pixbuf = gdk_pixbuf_new_from_file (filename, &error); if (error != NULL) { g_warning ("Failed to load cursor: %s: %s", filename, error->message); g_error_free (error); return; } break; } } if (!pixbuf) return; backend_wayland->cursor_buffer = create_cursor (backend_wayland, pixbuf); if (backend_wayland->cursor_buffer) { backend_wayland->cursor_x = 15; backend_wayland->cursor_y = 15; } g_object_unref (pixbuf); } static void clutter_backend_wayland_init (ClutterBackendWayland *backend_wayland) { }