mutter/clutter/wayland/clutter-backend-wayland.c

444 lines
13 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
* Authors:
* Matthew Allum
* Robert Bragg
* Kristian Høgsberg
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#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 <wayland-client.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <cogl/cogl.h>
#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
output_handle_mode (void *data,
struct wl_output *wl_output,
uint32_t flags,
int width,
int height,
int refresh)
{
ClutterBackendWayland *backend_wayland = data;
if (flags & WL_OUTPUT_MODE_CURRENT)
{
backend_wayland->output_width = width;
backend_wayland->output_height = height;
}
}
static void
output_handle_geometry (void *data,
struct wl_output *wl_output,
int x,
int y,
int physical_width,
int physical_height,
int subpixel,
const char *make,
const char *model)
{
}
static const struct wl_output_listener wayland_output_listener = {
output_handle_geometry,
output_handle_mode,
};
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);
}
else if (strcmp (interface, "wl_output") == 0)
{
/* FIXME: Support multiple outputs */
backend_wayland->wayland_output =
wl_display_bind (display, id, &wl_output_interface);
wl_output_add_listener (backend_wayland->wayland_output,
&wayland_output_listener,
backend_wayland);
}
}
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_ARGB8888);
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)
{
}