From cb5582c4ab1207738a2b5dc4c94fb4551e696c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Tue, 28 Sep 2010 10:17:46 -0400 Subject: [PATCH] Add wayland backend This adds a clutter backend for running under the wayland window system. Initial cogl framebuffer integration by Robert Bragg. --- clutter/Makefile.am | 19 + clutter/cogl/cogl/Makefile.am | 4 + clutter/cogl/cogl/winsys/cogl-egl.c | 10 +- clutter/wayland/clutter-backend-wayland.c | 552 ++++++++++++++++++ clutter/wayland/clutter-backend-wayland.h | 94 +++ clutter/wayland/clutter-event-wayland.c | 148 +++++ .../wayland/clutter-input-device-wayland.c | 340 +++++++++++ clutter/wayland/clutter-stage-wayland.c | 368 ++++++++++++ clutter/wayland/clutter-stage-wayland.h | 94 +++ clutter/wayland/clutter-wayland.h | 60 ++ configure.ac | 61 +- 11 files changed, 1738 insertions(+), 12 deletions(-) create mode 100644 clutter/wayland/clutter-backend-wayland.c create mode 100644 clutter/wayland/clutter-backend-wayland.h create mode 100644 clutter/wayland/clutter-event-wayland.c create mode 100644 clutter/wayland/clutter-input-device-wayland.c create mode 100644 clutter/wayland/clutter-stage-wayland.c create mode 100644 clutter/wayland/clutter-stage-wayland.h create mode 100644 clutter/wayland/clutter-wayland.h diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 642bb1d6b..b26df5222 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -495,6 +495,25 @@ clutterfruity_includedir = $(clutter_includedir)/fruity clutterfruity_include_HEADERS = $(srcdir)/fruity/clutter-fruity.h endif # SUPPORT_FRUITY +# Wayland backend rules +if SUPPORT_WAYLAND +backend_source_h += \ + $(srcdir)/wayland/clutter-wayland.h + +backend_source_h_priv += \ + $(srcdir)/wayland/clutter-backend-wayland.h \ + $(srcdir)/wayland/clutter-stage-wayland.h + +backend_source_c += \ + $(srcdir)/wayland/clutter-backend-wayland.c \ + $(srcdir)/wayland/clutter-stage-wayland.c \ + $(srcdir)/wayland/clutter-event-wayland.c \ + $(srcdir)/wayland/clutter-input-device-wayland.c + +clutterwayland_includedir = $(clutter_includedir)/wayland +clutterwayland_include_HEADERS = $(wayland_source_h) +endif # SUPPORT_WAYLAND + # cally cally_sources_h = \ $(srcdir)/cally/cally-actor.h \ diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am index f7fb8b172..d2de76a54 100644 --- a/clutter/cogl/cogl/Makefile.am +++ b/clutter/cogl/cogl/Makefile.am @@ -294,6 +294,10 @@ if SUPPORT_EGL_PLATFORM_FRUITY cogl_sources_c += \ $(srcdir)/winsys/cogl-fruity.c endif +if SUPPORT_EGL_PLATFORM_DRM_SURFACELESS +cogl_sources_c += \ + $(srcdir)/winsys/cogl-egl.c +endif if SUPPORT_WIN32 cogl_sources_c += \ $(srcdir)/winsys/cogl-win32.c diff --git a/clutter/cogl/cogl/winsys/cogl-egl.c b/clutter/cogl/cogl/winsys/cogl-egl.c index 1849ea27f..bafafc0ac 100644 --- a/clutter/cogl/cogl/winsys/cogl-egl.c +++ b/clutter/cogl/cogl/winsys/cogl-egl.c @@ -27,11 +27,15 @@ #include "cogl.h" -#ifdef HAVE_COGL_GLES2 +#ifdef HAVE_STANDALONE_EGL #include -#else /* HAVE_COGL_GLES2 */ +#include +#define NativeDisplayType EGLNativeDisplayType +#define NativeWindowType EGLNativeWindowType +#else #include -#endif /* HAVE_COGL_GLES2 */ +#include +#endif CoglFuncPtr _cogl_winsys_get_proc_address (const char *name) diff --git a/clutter/wayland/clutter-backend-wayland.c b/clutter/wayland/clutter-backend-wayland.c new file mode 100644 index 000000000..e2bb706ad --- /dev/null +++ b/clutter/wayland/clutter-backend-wayland.c @@ -0,0 +1,552 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 +#include + +#include "../clutter-private.h" +#include "../clutter-main.h" +#include "../clutter-debug.h" +#include "../clutter-version.h" + +#include "clutter-backend-wayland.h" +#include "clutter-stage-wayland.h" +#include "clutter-wayland.h" + +static ClutterBackendWayland *backend_singleton = NULL; + +G_DEFINE_TYPE (ClutterBackendWayland, _clutter_backend_wayland, CLUTTER_TYPE_BACKEND); + +static void +clutter_backend_at_exit (void) +{ + if (backend_singleton) + g_object_run_dispose (G_OBJECT (backend_singleton)); +} + +static gboolean +clutter_backend_wayland_pre_parse (ClutterBackend *backend, + GError **error) +{ + return TRUE; +} + +static void +drm_handle_device (void *data, struct wl_drm *drm, const char *device) +{ + ClutterBackendWayland *backend_wayland = data; + backend_wayland->device_name = g_strdup (device); +} + +static void +drm_handle_authenticated (void *data, struct wl_drm *drm) +{ + ClutterBackendWayland *backend_wayland = data; + backend_wayland->authenticated = 1; +} + +static const struct wl_drm_listener drm_listener = +{ + drm_handle_device, + drm_handle_authenticated +}; + +static void +display_handle_geometry (void *data, + struct wl_output *output, + int32_t width, int32_t height) +{ + ClutterBackendWayland *backend_wayland = data; + + backend_wayland->screen_allocation.x = 0; + backend_wayland->screen_allocation.y = 0; + backend_wayland->screen_allocation.width = width; + backend_wayland->screen_allocation.height = height; +} + +static const struct wl_output_listener output_listener = +{ + display_handle_geometry, +}; + + +static void +handle_configure (void *data, struct wl_shell *shell, + uint32_t timestamp, uint32_t edges, + struct wl_surface *surface, + int32_t x, int32_t y, int32_t width, int32_t height) +{ + ClutterStageWayland *stage_wayland; + + stage_wayland = wl_surface_get_user_data (surface); + + stage_wayland->pending_allocation.x = x; + stage_wayland->pending_allocation.y = y; + stage_wayland->allocation = stage_wayland->pending_allocation; + + clutter_actor_set_size (CLUTTER_ACTOR (stage_wayland->wrapper), + width, height); + + /* the resize process is complete, so we can ask the stage + * to set up the GL viewport with the new size + */ + clutter_stage_ensure_viewport (stage_wayland->wrapper); +} + +static const struct wl_shell_listener shell_listener = { + handle_configure, +}; + +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, "compositor") == 0) + { + backend_wayland->wayland_compositor = wl_compositor_create (display, id); + } + else if (strcmp (interface, "output") == 0) + { + backend_wayland->wayland_output = wl_output_create (display, id); + wl_output_add_listener (backend_wayland->wayland_output, + &output_listener, backend_wayland); + } + else if (strcmp (interface, "input_device") == 0) + { + _clutter_backend_add_input_device (backend_wayland, id); + } + else if (strcmp (interface, "shell") == 0) + { + backend_wayland->wayland_shell = wl_shell_create (display, id); + wl_shell_add_listener (backend_wayland->wayland_shell, + &shell_listener, backend_wayland); + } + else if (strcmp (interface, "drm") == 0) + { + backend_wayland->wayland_drm = wl_drm_create (display, id); + wl_drm_add_listener (backend_wayland->wayland_drm, + &drm_listener, backend_wayland); + } +} + +static gboolean +clutter_backend_wayland_post_parse (ClutterBackend *backend, + GError **error) +{ + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + EGLBoolean status; + drm_magic_t magic; + + g_atexit (clutter_backend_at_exit); + + /* TODO: expose environment variable/commandline option for this... */ + backend_wayland->wayland_display = + wl_display_create ("\0wayland", sizeof ("\0wayland")); + 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); + + /* Set up listener so we'll catch all events. */ + wl_display_add_global_listener (backend_wayland->wayland_display, + display_handle_global, + backend_wayland); + + /* Process connection events. */ + wl_display_iterate (backend_wayland->wayland_display, WL_DISPLAY_READABLE); + + backend_wayland->drm_fd = open (backend_wayland->device_name, O_RDWR); + if (backend_wayland->drm_fd < 0) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Failed to open drm device"); + return FALSE; + } + + if (drmGetMagic (backend_wayland->drm_fd, &magic)) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Failed to get drm magic"); + return FALSE; + } + + wl_drm_authenticate (backend_wayland->wayland_drm, magic); + wl_display_iterate (backend_wayland->wayland_display, WL_DISPLAY_WRITABLE); + while (!backend_wayland->authenticated) + wl_display_iterate (backend_wayland->wayland_display, WL_DISPLAY_READABLE); + + backend_wayland->get_drm_display = + (PFNEGLGETDRMDISPLAYMESA) eglGetProcAddress ("eglGetDRMDisplayMESA"); + backend_wayland->create_drm_image = + (PFNEGLCREATEDRMIMAGEMESA) eglGetProcAddress ("eglCreateDRMImageMESA"); + backend_wayland->destroy_image = + (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress ("eglDestroyImageKHR"); + backend_wayland->export_drm_image = + (PFNEGLEXPORTDRMIMAGEMESA) eglGetProcAddress ("eglExportDRMImageMESA"); + backend_wayland->image_target_texture_2d = + (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) eglGetProcAddress ("glEGLImageTargetTexture2DOES"); + + if (backend_wayland->get_drm_display == NULL || + backend_wayland->create_drm_image == NULL || + backend_wayland->destroy_image == NULL || + backend_wayland->export_drm_image == NULL || + backend_wayland->image_target_texture_2d == NULL) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Missing EGL extensions"); + return FALSE; + } + + backend_wayland->edpy = + backend_wayland->get_drm_display (backend_wayland->drm_fd); + + status = eglInitialize (backend_wayland->edpy, + &backend_wayland->egl_version_major, + &backend_wayland->egl_version_minor); + if (status != EGL_TRUE) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Unable to Initialize EGL"); + return FALSE; + } + + CLUTTER_NOTE (BACKEND, "EGL Reports version %i.%i", + backend_wayland->egl_version_major, + backend_wayland->egl_version_minor); + + return TRUE; +} + +static gboolean +try_create_context (ClutterBackend *backend, + int retry_cookie, + gboolean *try_fallback, + GError **error) +{ + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + EGLDisplay edpy; + const char *error_message; + + edpy = clutter_egl_display (); + + eglBindAPI (EGL_OPENGL_API); + + if (backend_wayland->egl_context == EGL_NO_CONTEXT) + { +#if defined (HAVE_COGL_GLES2) + static const EGLint attribs[] = + { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; +#else + static const EGLint *attribs = NULL; +#endif + + backend_wayland->egl_context = eglCreateContext (edpy, + NULL, + EGL_NO_CONTEXT, + attribs); + if (backend_wayland->egl_context == EGL_NO_CONTEXT) + { + error_message = "Unable to create a suitable EGL context"; + goto fail; + } + + CLUTTER_NOTE (GL, "Created EGL Context"); + } + + if (!eglMakeCurrent (edpy, NULL, NULL, backend_wayland->egl_context)) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Unable to MakeCurrent with NULL drawables"); + return FALSE; + } + + return TRUE; + +fail: + { + *try_fallback = FALSE; + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "%s", error_message); + return FALSE; + } +} + +static gboolean +clutter_backend_wayland_create_context (ClutterBackend *backend, + GError **error) +{ + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + EGLDisplay edpy; + const gchar *egl_extensions = NULL; + gboolean status; + int retry_cookie; + gboolean try_fallback; + GError *try_error = NULL; + + if (backend_wayland->egl_context != EGL_NO_CONTEXT) + return TRUE; + + edpy = clutter_egl_display (); + + egl_extensions = eglQueryString (edpy, EGL_EXTENSIONS); + + if (!_cogl_check_extension ("EGL_KHR_surfaceless_opengl", egl_extensions)) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Wayland clients require the " + "EGL_KHR_surfaceless_opengl extension"); + return FALSE; + } + + retry_cookie = 0; + while (!(status = try_create_context (backend, + retry_cookie, + &try_fallback, + &try_error)) && + try_fallback) + { + g_warning ("Failed to create context: %s\nWill try fallback...", + try_error->message); + g_error_free (try_error); + try_error = NULL; + retry_cookie++; + } + if (!status) + g_propagate_error (error, try_error); + + return status; +} + +static void +clutter_backend_wayland_ensure_context (ClutterBackend *backend, + ClutterStage *stage) +{ +} + +static void +clutter_backend_wayland_redraw (ClutterBackend *backend, + ClutterStage *stage) +{ + ClutterStageWindow *impl; + + impl = _clutter_stage_get_window (stage); + if (!impl) + return; + + g_assert (CLUTTER_IS_STAGE_WAYLAND (impl)); + + _clutter_stage_wayland_redraw (CLUTTER_STAGE_WAYLAND (impl), stage); +} + +static void +clutter_backend_wayland_init_events (ClutterBackend *backend) +{ +} + +static void +clutter_backend_wayland_finalize (GObject *gobject) +{ + if (backend_singleton) + backend_singleton = NULL; + + G_OBJECT_CLASS (_clutter_backend_wayland_parent_class)->finalize (gobject); +} + +static void +clutter_backend_wayland_dispose (GObject *gobject) +{ + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (gobject); + + /* We chain up before disposing our own resources so that + ClutterBackend will destroy all of the stages before we destroy + the egl context. Otherwise the actors may try to make GL calls + during destruction which causes a crash */ + G_OBJECT_CLASS (_clutter_backend_wayland_parent_class)->dispose (gobject); + + if (backend_wayland->egl_context) + { + eglDestroyContext (backend_wayland->edpy, backend_wayland->egl_context); + backend_wayland->egl_context = NULL; + } + + if (backend_wayland->edpy) + { + eglTerminate (backend_wayland->edpy); + backend_wayland->edpy = 0; + } + + if (backend_wayland->drm_fd != -1) + { + close (backend_wayland->drm_fd); + backend_wayland->drm_fd = -1; + } +} + +static GObject * +clutter_backend_wayland_constructor (GType gtype, + guint n_params, + GObjectConstructParam *params) +{ + GObjectClass *parent_class; + GObject *retval; + + if (!backend_singleton) + { + parent_class = G_OBJECT_CLASS (_clutter_backend_wayland_parent_class); + retval = parent_class->constructor (gtype, n_params, params); + + backend_singleton = CLUTTER_BACKEND_WAYLAND (retval); + + return retval; + } + + g_warning ("Attempting to create a new backend object. This should " + "never happen, so we return the singleton instance."); + + return g_object_ref (backend_singleton); +} + +static ClutterFeatureFlags +clutter_backend_wayland_get_features (ClutterBackend *backend) +{ + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + ClutterFeatureFlags flags = 0; + + g_assert (backend_wayland->egl_context != NULL); + + flags |= + CLUTTER_FEATURE_STAGE_MULTIPLE | + CLUTTER_FEATURE_SWAP_EVENTS | + CLUTTER_FEATURE_SYNC_TO_VBLANK; + + CLUTTER_NOTE (BACKEND, "Checking features\n" + "GL_VENDOR: %s\n" + "GL_RENDERER: %s\n" + "GL_VERSION: %s\n" + "EGL_VENDOR: %s\n" + "EGL_VERSION: %s\n" + "EGL_EXTENSIONS: %s\n", + glGetString (GL_VENDOR), + glGetString (GL_RENDERER), + glGetString (GL_VERSION), + eglQueryString (backend_wayland->edpy, EGL_VENDOR), + eglQueryString (backend_wayland->edpy, EGL_VERSION), + eglQueryString (backend_wayland->edpy, EGL_EXTENSIONS)); + + return flags; +} + +static ClutterStageWindow * +clutter_backend_wayland_create_stage (ClutterBackend *backend, + ClutterStage *wrapper, + GError **error) +{ + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + ClutterStageWindow *stage; + ClutterStageWayland *stage_wayland; + + CLUTTER_NOTE (BACKEND, "Creating stage of type '%s'", + g_type_name (CLUTTER_STAGE_TYPE)); + + stage = g_object_new (CLUTTER_TYPE_STAGE_WAYLAND, NULL); + + stage_wayland = CLUTTER_STAGE_WAYLAND (stage); + stage_wayland->backend = backend_wayland; + stage_wayland->wrapper = wrapper; + + return stage; +} + +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->constructor = clutter_backend_wayland_constructor; + gobject_class->dispose = clutter_backend_wayland_dispose; + gobject_class->finalize = clutter_backend_wayland_finalize; + + backend_class->pre_parse = clutter_backend_wayland_pre_parse; + backend_class->post_parse = clutter_backend_wayland_post_parse; + backend_class->get_features = clutter_backend_wayland_get_features; + backend_class->init_events = clutter_backend_wayland_init_events; + backend_class->create_stage = clutter_backend_wayland_create_stage; + backend_class->create_context = clutter_backend_wayland_create_context; + backend_class->ensure_context = clutter_backend_wayland_ensure_context; + backend_class->redraw = clutter_backend_wayland_redraw; +} + +static void +_clutter_backend_wayland_init (ClutterBackendWayland *backend_wayland) +{ + backend_wayland->egl_context = EGL_NO_CONTEXT; + + backend_wayland->drm_fd = -1; +} + +GType +_clutter_backend_impl_get_type (void) +{ + return _clutter_backend_wayland_get_type (); +} + +EGLDisplay +clutter_egl_display (void) +{ + return backend_singleton->edpy; +} diff --git a/clutter/wayland/clutter-backend-wayland.h b/clutter/wayland/clutter-backend-wayland.h new file mode 100644 index 000000000..54a8d5260 --- /dev/null +++ b/clutter/wayland/clutter-backend-wayland.h @@ -0,0 +1,94 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 + */ + +#ifndef __CLUTTER_BACKEND_WAYLAND_H__ +#define __CLUTTER_BACKEND_WAYLAND_H__ + +#include +#include +#include + +#include +#include + +#include "clutter-wayland.h" + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BACKEND_WAYLAND (_clutter_backend_wayland_get_type ()) +#define CLUTTER_BACKEND_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_WAYLAND, ClutterBackendWayland)) +#define CLUTTER_IS_BACKEND_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_WAYLAND)) +#define CLUTTER_BACKEND_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_WAYLAND, ClutterBackendWaylandClass)) +#define CLUTTER_IS_BACKEND_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_WAYLAND)) +#define CLUTTER_BACKEND_WAYLAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_WAYLAND, ClutterBackendWaylandClass)) + +typedef struct _ClutterBackendWayland ClutterBackendWayland; +typedef struct _ClutterBackendWaylandClass ClutterBackendWaylandClass; + +struct _ClutterBackendWayland +{ + ClutterBackend parent_instance; + + /* EGL Specific */ + EGLDisplay edpy; + EGLContext egl_context; + EGLConfig egl_config; + + gint egl_version_major; + gint egl_version_minor; + + struct wl_display *wayland_display; + GSource *wayland_source; + struct wl_compositor *wayland_compositor; + struct wl_shell *wayland_shell; + struct wl_drm *wayland_drm; + char *device_name; + int authenticated; + struct wl_output *wayland_output; + ClutterGeometry screen_allocation; + int drm_fd; + + PFNEGLGETDRMDISPLAYMESA get_drm_display; + PFNEGLCREATEDRMIMAGEMESA create_drm_image; + PFNEGLDESTROYIMAGEKHRPROC destroy_image; + PFNEGLEXPORTDRMIMAGEMESA export_drm_image; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; +}; + +struct _ClutterBackendWaylandClass +{ + ClutterBackendClass parent_class; +}; + +GType _clutter_backend_wayland_get_type (void) G_GNUC_CONST; + +GSource *_clutter_event_source_wayland_new(struct wl_display *display); +void _clutter_backend_add_input_device (ClutterBackendWayland *backend_wayland, + uint32_t id); + +G_END_DECLS + +#endif /* __CLUTTER_BACKEND_WAYLAND_H__ */ diff --git a/clutter/wayland/clutter-event-wayland.c b/clutter/wayland/clutter-event-wayland.c new file mode 100644 index 000000000..23d9be41b --- /dev/null +++ b/clutter/wayland/clutter-event-wayland.c @@ -0,0 +1,148 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 "../clutter-event.h" +#include "../clutter-main.h" + +typedef struct _ClutterEventSourceWayland +{ + GSource source; + GPollFD pfd; + uint32_t mask; + struct wl_display *display; +} ClutterEventSourceWayland; + +static gboolean +clutter_event_source_wayland_prepare (GSource *base, gint *timeout) +{ + ClutterEventSourceWayland *source = (ClutterEventSourceWayland *) base; + gboolean retval; + + clutter_threads_enter (); + + *timeout = -1; + + /* We have to add/remove the GPollFD if we want to update our + * poll event mask dynamically. Instead, let's just flush all + * write on idle instead, which is what this amounts to. */ + + while (source->mask & WL_DISPLAY_WRITABLE) + wl_display_iterate (source->display, WL_DISPLAY_WRITABLE); + + retval = clutter_events_pending (); + + clutter_threads_leave (); + + return retval; +} + +static gboolean +clutter_event_source_wayland_check (GSource *base) +{ + ClutterEventSourceWayland *source = (ClutterEventSourceWayland *) base; + gboolean retval; + + clutter_threads_enter (); + + retval = clutter_events_pending () || source->pfd.revents; + + clutter_threads_leave (); + + return retval; +} + +static gboolean +clutter_event_source_wayland_dispatch (GSource *base, + GSourceFunc callback, + gpointer data) +{ + ClutterEventSourceWayland *source = (ClutterEventSourceWayland *) base; + ClutterEvent *event; + + clutter_threads_enter (); + + if (source->pfd.revents) + { + wl_display_iterate (source->display, WL_DISPLAY_READABLE); + source->pfd.revents = 0; + } + + event = clutter_event_get (); + + if (event) + { + /* forward the event into clutter for emission etc. */ + clutter_do_event (event); + clutter_event_free (event); + } + + clutter_threads_leave (); + + return TRUE; +} + +static GSourceFuncs clutter_event_source_wayland_funcs = { + clutter_event_source_wayland_prepare, + clutter_event_source_wayland_check, + clutter_event_source_wayland_dispatch, + NULL +}; + +static int +clutter_event_source_wayland_update (uint32_t mask, void *data) +{ + ClutterEventSourceWayland *source = data; + + source->mask = mask; + + return 0; +} + +GSource * +_clutter_event_source_wayland_new (struct wl_display *display) +{ + ClutterEventSourceWayland *source; + + source = (ClutterEventSourceWayland *) + g_source_new (&clutter_event_source_wayland_funcs, + sizeof (ClutterEventSourceWayland)); + source->display = display; + source->pfd.fd = + wl_display_get_fd (display, + clutter_event_source_wayland_update, source); + source->pfd.events = G_IO_IN | G_IO_ERR; + g_source_add_poll (&source->source, &source->pfd); + + return &source->source; +} diff --git a/clutter/wayland/clutter-input-device-wayland.c b/clutter/wayland/clutter-input-device-wayland.c new file mode 100644 index 000000000..e59ccb328 --- /dev/null +++ b/clutter/wayland/clutter-input-device-wayland.c @@ -0,0 +1,340 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 "../clutter-debug.h" +#include "../clutter-private.h" +#include "../clutter-keysyms.h" + +#include "clutter-stage-wayland.h" + +#define CLUTTER_TYPE_INPUT_DEVICE_WAYLAND (clutter_input_device_wayland_get_type ()) +#define CLUTTER_INPUT_DEVICE_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_WAYLAND, ClutterInputDeviceWayland)) +#define CLUTTER_IS_INPUT_DEVICE_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_WAYLAND)) + +typedef struct _ClutterInputDeviceWayland ClutterInputDeviceWayland; + +GType clutter_input_device_wayland_get_type (void) G_GNUC_CONST; + +typedef struct _ClutterInputDeviceClass ClutterInputDeviceWaylandClass; + +struct _ClutterInputDeviceWayland +{ + ClutterInputDevice device; + struct wl_input_device *input_device; + ClutterStageWayland *pointer_focus; + ClutterStageWayland *keyboard_focus; + uint32_t modifier_state; + int32_t x, y, surface_x, surface_y; + struct xkb_desc *xkb; +}; + +G_DEFINE_TYPE (ClutterInputDeviceWayland, + clutter_input_device_wayland, + CLUTTER_TYPE_INPUT_DEVICE); + +static void +clutter_backend_wayland_handle_motion (void *data, + struct wl_input_device *input_device, + uint32_t _time, + int32_t x, int32_t y, + int32_t sx, int32_t sy) +{ + ClutterInputDeviceWayland *device = data; + ClutterStageWayland *stage_wayland = device->pointer_focus; + ClutterMainContext *clutter_context; + ClutterEvent *event; + + event = clutter_event_new (CLUTTER_MOTION); + event->motion.stage = stage_wayland->wrapper; + event->motion.device = CLUTTER_INPUT_DEVICE (device); + event->motion.time = _time; + event->motion.modifier_state = 0; + event->motion.x = sx; + event->motion.y = sy; + + device->surface_x = sx; + device->surface_y = sy; + device->x = x; + device->y = y; + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); +} + +static void +clutter_backend_wayland_handle_button (void *data, + struct wl_input_device *input_device, + uint32_t _time, + uint32_t button, uint32_t state) +{ + ClutterInputDeviceWayland *device = data; + ClutterStageWayland *stage_wayland = device->pointer_focus; + ClutterMainContext *clutter_context; + ClutterEvent *event; + ClutterEventType type; + + if (state) + type = CLUTTER_BUTTON_PRESS; + else + type = CLUTTER_BUTTON_RELEASE; + + event = clutter_event_new (type); + event->button.stage = stage_wayland->wrapper; + event->button.device = CLUTTER_INPUT_DEVICE (device); + event->button.time = _time; + event->button.x = device->surface_x; + event->button.y = device->surface_y; + event->button.modifier_state = device->modifier_state; + + /* evdev button codes */ + switch (button) { + case 272: + event->button.button = 1; + break; + case 273: + event->button.button = 3; + break; + case 274: + event->button.button = 2; + break; + } + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); +} + +static void +clutter_backend_wayland_handle_key (void *data, + struct wl_input_device *input_device, + uint32_t _time, + uint32_t key, uint32_t state) +{ + ClutterInputDeviceWayland *device = data; + ClutterStageWayland *stage_wayland = device->keyboard_focus; + ClutterMainContext *clutter_context; + ClutterEvent *event; + uint32_t code, sym, level; + + if (state) + event = clutter_event_new (CLUTTER_KEY_PRESS); + else + event = clutter_event_new (CLUTTER_KEY_RELEASE); + + code = key + device->xkb->min_key_code; + level = 0; + + if (device->modifier_state & CLUTTER_SHIFT_MASK && + XkbKeyGroupWidth (device->xkb, code, 0) > 1) + level = 1; + + sym = XkbKeySymEntry (device->xkb, code, level, 0); + if (state) + device->modifier_state |= device->xkb->map->modmap[code]; + else + device->modifier_state &= ~device->xkb->map->modmap[code]; + + event->key.device = CLUTTER_INPUT_DEVICE (device); + event->key.stage = stage_wayland->wrapper; + event->key.time = _time; + event->key.modifier_state = device->modifier_state; + event->key.hardware_keycode = key; + event->key.keyval = sym; + event->key.unicode_value = sym; + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); +} + +static void +clutter_backend_wayland_handle_pointer_focus (void *data, + struct wl_input_device *input_device, + uint32_t _time, + struct wl_surface *surface, + int32_t x, int32_t y, int32_t sx, int32_t sy) +{ + ClutterInputDeviceWayland *device = data; + ClutterStageWayland *stage_wayland; + ClutterMainContext *clutter_context; + ClutterEvent *event; + + if (device->pointer_focus) + { + stage_wayland = device->pointer_focus; + + event = clutter_event_new (CLUTTER_LEAVE); + event->crossing.stage = stage_wayland->wrapper; + event->crossing.time = _time; + event->crossing.x = sx; + event->crossing.y = sy; + event->crossing.source = CLUTTER_ACTOR (stage_wayland->wrapper); + event->crossing.device = CLUTTER_INPUT_DEVICE (device); + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); + + device->pointer_focus = NULL; + _clutter_input_device_set_stage (CLUTTER_INPUT_DEVICE (device), NULL); + } + + if (surface) + { + stage_wayland = wl_surface_get_user_data (surface); + + device->pointer_focus = stage_wayland; + _clutter_input_device_set_stage (CLUTTER_INPUT_DEVICE (device), + stage_wayland->wrapper); + + event = clutter_event_new (CLUTTER_MOTION); + event->motion.time = _time; + event->motion.x = sx; + event->motion.y = sy; + event->motion.modifier_state = device->modifier_state; + event->motion.source = CLUTTER_ACTOR (stage_wayland->wrapper); + event->motion.device = CLUTTER_INPUT_DEVICE (device); + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); + + device->surface_x = sx; + device->surface_y = sy; + device->x = x; + device->y = y; + + /* Revert back to default pointer for now. */ + wl_input_device_attach (input_device, _time, NULL, 0, 0); + } +} + +static void +clutter_backend_wayland_handle_keyboard_focus (void *data, + struct wl_input_device *input_device, + uint32_t _time, + struct wl_surface *surface, + struct wl_array *keys) +{ + ClutterInputDeviceWayland *device = data; + ClutterStageWayland *stage_wayland; + ClutterMainContext *clutter_context; + ClutterEvent *event; + uint32_t *k, *end; + + if (device->keyboard_focus) + { + stage_wayland = device->keyboard_focus; + device->keyboard_focus = NULL; + + event = clutter_event_new (CLUTTER_STAGE_STATE); + event->stage_state.time = _time; + event->stage_state.stage = stage_wayland->wrapper; + event->stage_state.stage = stage_wayland->wrapper; + event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; + event->stage_state.new_state = 0; + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); + } + + if (surface) + { + stage_wayland = wl_surface_get_user_data (surface); + device->keyboard_focus = stage_wayland; + + event = clutter_event_new (CLUTTER_STAGE_STATE); + event->stage_state.stage = stage_wayland->wrapper; + event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; + event->stage_state.new_state = CLUTTER_STAGE_STATE_ACTIVATED; + + end = keys->data + keys->size; + device->modifier_state = 0; + for (k = keys->data; k < end; k++) + device->modifier_state |= device->xkb->map->modmap[*k]; + + clutter_context = _clutter_context_get_default (); + g_queue_push_head (clutter_context->events_queue, event); + } +} + +static const struct wl_input_device_listener input_device_listener = { + clutter_backend_wayland_handle_motion, + clutter_backend_wayland_handle_button, + clutter_backend_wayland_handle_key, + clutter_backend_wayland_handle_pointer_focus, + clutter_backend_wayland_handle_keyboard_focus, +}; + +static void +clutter_input_device_wayland_class_init (ClutterInputDeviceWaylandClass *klass) +{ +} + +static void +clutter_input_device_wayland_init (ClutterInputDeviceWayland *self) +{ +} + +const char *option_xkb_layout = "us"; +const char *option_xkb_variant = ""; +const char *option_xkb_options = ""; + +void +_clutter_backend_add_input_device (ClutterBackendWayland *backend_wayland, + uint32_t id) +{ + ClutterInputDeviceWayland *device; + struct xkb_rule_names names; + + device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_WAYLAND, + "id", id, + "device-type", CLUTTER_POINTER_DEVICE, + "name", "wayland device", + NULL); + + device->input_device = + wl_input_device_create (backend_wayland->wayland_display, id); + wl_input_device_add_listener (device->input_device, + &input_device_listener, device); + wl_input_device_set_user_data (device->input_device, device); + + names.rules = "evdev"; + names.model = "pc105"; + names.layout = option_xkb_layout; + names.variant = option_xkb_variant; + names.options = option_xkb_options; + + device->xkb = xkb_compile_keymap_from_rules (&names); + if (!device->xkb) + CLUTTER_NOTE (BACKEND, "Failed to compile keymap"); +} diff --git a/clutter/wayland/clutter-stage-wayland.c b/clutter/wayland/clutter-stage-wayland.c new file mode 100644 index 000000000..c8e49c027 --- /dev/null +++ b/clutter/wayland/clutter-stage-wayland.c @@ -0,0 +1,368 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 "../clutter-main.h" +#include "../clutter-feature.h" +#include "../clutter-color.h" +#include "../clutter-util.h" +#include "../clutter-event.h" +#include "../clutter-enum-types.h" +#include "../clutter-private.h" +#include "../clutter-debug.h" +#include "../clutter-units.h" +#include "../clutter-stage.h" +#include "../clutter-stage-window.h" + +#include "clutter-stage-wayland.h" +#include "clutter-wayland.h" +#include "clutter-backend-wayland.h" + +#include "cogl/cogl-framebuffer-private.h" + +static void +wayland_swap_buffers (ClutterStageWayland *stage_wayland); + +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ClutterStageWayland, + _clutter_stage_wayland, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, + clutter_stage_window_iface_init)); + +static ClutterStageWaylandWaylandBuffer * +wayland_create_buffer (ClutterStageWayland *stage_wayland, + ClutterGeometry *geom) +{ + ClutterBackend *backend = clutter_get_default_backend (); + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + EGLDisplay edpy = clutter_egl_display (); + ClutterStageWaylandWaylandBuffer *buffer; + EGLint image_attribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA, + EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SCANOUT_MESA, + EGL_NONE + }; + CoglHandle tex; + + buffer = g_slice_new (ClutterStageWaylandWaylandBuffer); + + image_attribs[1] = geom->width; + image_attribs[3] = geom->height; + buffer->drm_image = backend_wayland->create_drm_image (edpy, image_attribs); + glGenTextures (1, &buffer->texture); + glBindTexture (GL_TEXTURE_2D, buffer->texture); + backend_wayland->image_target_texture_2d (GL_TEXTURE_2D, buffer->drm_image); + + tex = cogl_texture_new_from_foreign (buffer->texture, + GL_TEXTURE_2D, + geom->width, + geom->height, + 0, + 0, + COGL_PIXEL_FORMAT_ARGB_8888); + buffer->offscreen = cogl_offscreen_new_to_texture (tex); + cogl_handle_unref (tex); + buffer->wayland_buffer = NULL; + + return buffer; +} + +static void +wayland_free_buffer (ClutterStageWaylandWaylandBuffer *buffer) +{ + ClutterBackend *backend = clutter_get_default_backend (); + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + EGLDisplay edpy = clutter_egl_display (); + + if (buffer->wayland_buffer) + wl_buffer_destroy (buffer->wayland_buffer); + + cogl_handle_unref (buffer->offscreen); + glDeleteTextures (1, &buffer->texture); + backend_wayland->destroy_image (edpy, buffer->drm_image); + g_slice_free (ClutterStageWaylandWaylandBuffer, buffer); +} + +static void +clutter_stage_wayland_unrealize (ClutterStageWindow *stage_window) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + + if (stage_wayland->front_buffer) + { + wayland_free_buffer (stage_wayland->front_buffer); + stage_wayland->front_buffer = NULL; + } + + if (stage_wayland->back_buffer) + { + wayland_free_buffer (stage_wayland->back_buffer); + stage_wayland->back_buffer = NULL; + } + + wayland_free_buffer (stage_wayland->pick_buffer); +} + +static gboolean +clutter_stage_wayland_realize (ClutterStageWindow *stage_window) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + ClutterBackend *backend = clutter_get_default_backend (); + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + gfloat width, height; + + clutter_actor_get_size (CLUTTER_ACTOR (stage_wayland->wrapper), + &width, &height); + stage_wayland->pending_allocation.width = (gint)width; + stage_wayland->pending_allocation.height = (gint)height; + stage_wayland->allocation = stage_wayland->pending_allocation; + + stage_wayland->wayland_surface = + wl_compositor_create_surface (backend_wayland->wayland_compositor); + wl_surface_set_user_data (stage_wayland->wayland_surface, stage_wayland); + + stage_wayland->pick_buffer = + wayland_create_buffer (stage_wayland, &stage_wayland->allocation); + + return TRUE; +} + +static int +clutter_stage_wayland_get_pending_swaps (ClutterStageWindow *stage_window) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + + return stage_wayland->pending_swaps; +} + +static void +clutter_stage_wayland_set_fullscreen (ClutterStageWindow *stage_window, + gboolean fullscreen) +{ + g_warning ("Stage of type '%s' do not support ClutterStage::set_fullscreen", + G_OBJECT_TYPE_NAME (stage_window)); +} + +static void +clutter_stage_wayland_set_title (ClutterStageWindow *stage_window, + const gchar *title) +{ + g_warning ("Stage of type '%s' do not support ClutterStage::set_title", + G_OBJECT_TYPE_NAME (stage_window)); +} + +static void +clutter_stage_wayland_set_cursor_visible (ClutterStageWindow *stage_window, + gboolean cursor_visible) +{ + g_warning ("Stage of type '%s' do not support ClutterStage::set_cursor_visible", + G_OBJECT_TYPE_NAME (stage_window)); +} + +static ClutterActor * +clutter_stage_wayland_get_wrapper (ClutterStageWindow *stage_window) +{ + return CLUTTER_ACTOR (CLUTTER_STAGE_WAYLAND (stage_window)->wrapper); +} + +static void +clutter_stage_wayland_show (ClutterStageWindow *stage_window, + gboolean do_raise) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + + clutter_actor_map (CLUTTER_ACTOR (stage_wayland->wrapper)); +} + +static void +clutter_stage_wayland_hide (ClutterStageWindow *stage_window) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + + clutter_actor_unmap (CLUTTER_ACTOR (stage_wayland->wrapper)); +} + +static void +clutter_stage_wayland_get_geometry (ClutterStageWindow *stage_window, + ClutterGeometry *geometry) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + + if (geometry) + { + *geometry = stage_wayland->allocation; + } +} + +static void +clutter_stage_wayland_resize (ClutterStageWindow *stage_window, + gint width, + gint height) +{ + ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); + + stage_wayland->pending_allocation.width = width; + stage_wayland->pending_allocation.height = height; +} + +static void +clutter_stage_window_iface_init (ClutterStageWindowIface *iface) +{ + iface->realize = clutter_stage_wayland_realize; + iface->unrealize = clutter_stage_wayland_unrealize; + iface->get_pending_swaps = clutter_stage_wayland_get_pending_swaps; + iface->set_fullscreen = clutter_stage_wayland_set_fullscreen; + iface->set_title = clutter_stage_wayland_set_title; + iface->set_cursor_visible = clutter_stage_wayland_set_cursor_visible; + iface->get_wrapper = clutter_stage_wayland_get_wrapper; + iface->get_geometry = clutter_stage_wayland_get_geometry; + iface->resize = clutter_stage_wayland_resize; + iface->show = clutter_stage_wayland_show; + iface->hide = clutter_stage_wayland_hide; +} + +static void +_clutter_stage_wayland_class_init (ClutterStageWaylandClass *klass) +{ +} + +static void +_clutter_stage_wayland_init (ClutterStageWayland *stage_wayland) +{ + stage_wayland->allocation.x = 0; + stage_wayland->allocation.y = 0; + stage_wayland->allocation.width = 640; + stage_wayland->allocation.height = 480; + stage_wayland->save_allocation = stage_wayland->allocation; +} + +static void +wayland_free_front_buffer (void *data) +{ + ClutterStageWayland *stage_wayland = data; + + if (stage_wayland->front_buffer) + wayland_free_buffer (stage_wayland->front_buffer); + stage_wayland->front_buffer = stage_wayland->pending_buffer; + stage_wayland->pending_buffer = NULL; + + if (stage_wayland->back_buffer) + wayland_swap_buffers (stage_wayland); +} + +static void +wayland_frame_callback (void *data, uint32_t _time) +{ + ClutterStageWayland *stage_wayland = data; + + stage_wayland->pending_swaps--; +} + +static void +wayland_swap_buffers (ClutterStageWayland *stage_wayland) +{ + ClutterBackend *backend = clutter_get_default_backend (); + ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); + EGLDisplay edpy = clutter_egl_display (); + EGLint name; + EGLint stride; + struct wl_visual *visual; + + if (stage_wayland->pending_buffer) + return; + + stage_wayland->pending_buffer = stage_wayland->back_buffer; + stage_wayland->back_buffer = NULL; + + backend_wayland->export_drm_image (edpy, + stage_wayland->pending_buffer->drm_image, + &name, NULL, &stride); + visual = + wl_display_get_premultiplied_argb_visual (backend_wayland->wayland_display); + stage_wayland->pending_buffer->wayland_buffer = + wl_drm_create_buffer (backend_wayland->wayland_drm, + name, + stage_wayland->allocation.width, + stage_wayland->allocation.height, + stride, visual); + wl_surface_attach (stage_wayland->wayland_surface, + stage_wayland->pending_buffer->wayland_buffer); + wl_surface_map (stage_wayland->wayland_surface, + stage_wayland->allocation.x, + stage_wayland->allocation.y, + stage_wayland->allocation.width, + stage_wayland->allocation.height); + wl_display_sync_callback (backend_wayland->wayland_display, + wayland_free_front_buffer, + stage_wayland); + + stage_wayland->pending_swaps++; + wl_display_frame_callback (backend_wayland->wayland_display, + wayland_frame_callback, + stage_wayland); +} + +void +_clutter_stage_wayland_redraw (ClutterStageWayland *stage_wayland, + ClutterStage *stage) +{ + ClutterActor *wrapper = CLUTTER_ACTOR (stage_wayland->wrapper); + + stage_wayland->allocation = stage_wayland->pending_allocation; + + if (stage_wayland->back_buffer) + { + wayland_free_buffer (stage_wayland->back_buffer); + stage_wayland->back_buffer = NULL; + } + + stage_wayland->back_buffer = wayland_create_buffer (stage_wayland, + &stage_wayland->allocation); + + cogl_set_framebuffer (stage_wayland->back_buffer->offscreen); + _clutter_stage_maybe_setup_viewport (stage_wayland->wrapper); + + clutter_actor_paint (wrapper); + cogl_flush (); + glFlush (); + + cogl_set_framebuffer (stage_wayland->pick_buffer->offscreen); + _clutter_stage_maybe_setup_viewport (stage_wayland->wrapper); + + wayland_swap_buffers (stage_wayland); +} diff --git a/clutter/wayland/clutter-stage-wayland.h b/clutter/wayland/clutter-stage-wayland.h new file mode 100644 index 000000000..c250ba3a3 --- /dev/null +++ b/clutter/wayland/clutter-stage-wayland.h @@ -0,0 +1,94 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 + */ + +#ifndef __CLUTTER_STAGE_WAYLAND_H__ +#define __CLUTTER_STAGE_WAYLAND_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define MESA_EGL_NO_X11_HEADERS +#include +#include + +#include "clutter-backend-wayland.h" + + +#define CLUTTER_TYPE_STAGE_WAYLAND (_clutter_stage_wayland_get_type ()) +#define CLUTTER_STAGE_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_WAYLAND, ClutterStageWayland)) +#define CLUTTER_IS_STAGE_WAYLAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_WAYLAND)) +#define CLUTTER_STAGE_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_WAYLAND, ClutterStageWaylandClass)) +#define CLUTTER_IS_STAGE_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_WAYLAND)) +#define CLUTTER_STAGE_WAYLAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_WAYLAND, ClutterStageWaylandClass)) + +typedef struct _ClutterStageWayland ClutterStageWayland; +typedef struct _ClutterStageWaylandClass ClutterStageWaylandClass; + +typedef struct _ClutterStageWaylandWaylandBuffer +{ + EGLImageKHR drm_image; + GLuint texture; + CoglHandle offscreen; + struct wl_buffer *wayland_buffer; +} ClutterStageWaylandWaylandBuffer; + +struct _ClutterStageWayland +{ + GObject parent_instance; + + /* the stage wrapper */ + ClutterStage *wrapper; + + /* back pointer to the backend */ + ClutterBackendWayland *backend; + + ClutterGeometry allocation; + ClutterGeometry save_allocation; + ClutterGeometry pending_allocation; + struct wl_surface *wayland_surface; + int pending_swaps; + + ClutterStageWaylandWaylandBuffer *front_buffer; + ClutterStageWaylandWaylandBuffer *back_buffer; + ClutterStageWaylandWaylandBuffer *pending_buffer; + ClutterStageWaylandWaylandBuffer *pick_buffer; +}; + +struct _ClutterStageWaylandClass +{ + GObjectClass parent_class; +}; + +GType _clutter_stage_wayland_get_type (void) G_GNUC_CONST; + +void _clutter_stage_wayland_redraw (ClutterStageWayland *stage_wayland, + ClutterStage *stage); + +#endif /* __CLUTTER_STAGE_WAYLAND_H__ */ diff --git a/clutter/wayland/clutter-wayland.h b/clutter/wayland/clutter-wayland.h new file mode 100644 index 000000000..ac2d7133c --- /dev/null +++ b/clutter/wayland/clutter-wayland.h @@ -0,0 +1,60 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2010 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 + */ + +/** + * SECTION:clutter-wayland + * @short_description: Wayland specific API + * + * The Wayland backend for Clutter provides some Wayland specific API + * + * You need to include + * <clutter/egl/clutter-wayland.h> + * to have access to the functions documented here. + */ + +#ifndef __CLUTTER_WAYLAND_H__ +#define __CLUTTER_WAYLAND_H__ + +#include + +#include +#include + +G_BEGIN_DECLS + +/** + * clutter_egl_display: + * + * Retrieves the EGLDisplay used by Clutter + * + * Return value: the EGL display + */ +EGLDisplay +clutter_egl_display (void); + +G_END_DECLS + +#endif /* __CLUTTER_WAYLAND_H__ */ diff --git a/configure.ac b/configure.ac index 05f4c3d10..b096e32ad 100644 --- a/configure.ac +++ b/configure.ac @@ -152,7 +152,7 @@ experimental_backend=no experimental_image=no AC_ARG_WITH([flavour], - [AC_HELP_STRING([--with-flavour=@<:@glx/opengl-egl-xlib/eglx/eglnative/osx/win32/fruity/cex100@:>@], + [AC_HELP_STRING([--with-flavour=@<:@glx/opengl-egl-xlib/wayland/eglx/eglnative/osx/win32/fruity/cex100@:>@], [Select the Clutter window system backend])], [CLUTTER_FLAVOUR=$with_flavour]) @@ -228,6 +228,26 @@ AS_CASE([$CLUTTER_FLAVOUR], PKG_CHECK_EXISTS([egl], [BACKEND_PC_FILES="$BACKEND_PC_FILES egl"], []) ], + [wayland], + [ + experimental_backend="yes" + cogl_gl_headers="GL/gl.h" + CLUTTER_STAGE_TYPE="CLUTTER_TYPE_STAGE_WAYLAND" + + SUPPORT_WAYLAND=1 + SUPPORT_EGL_PLATFORM_DRM_SURFACELESS=1 + + COGL_DRIVER="gl" + + CLUTTER_WINSYS=wayland + CLUTTER_SONAME_INFIX=wayland + + PKG_CHECK_EXISTS([gl], [BACKEND_PC_FILES="$BACKEND_PC_FILES gl"], []) + PKG_CHECK_EXISTS([egl], [BACKEND_PC_FILES="$BACKEND_PC_FILES egl"], []) + PKG_CHECK_EXISTS([wayland-client xkbcommon], + [BACKEND_PC_FILES="$BACKEND_PC_FILES wayland-client xkbcommon"], []) + ], + [eglx], [ # the GL header is defined in the COGL checks above @@ -383,6 +403,11 @@ AS_IF([test "x$SUPPORT_GLX" = "x1"], [AC_MSG_ERROR([Required GLX library not found])]) ]) +AS_IF([test "x$SUPPORT_WAYLAND" = "x1"], + [ + AC_DEFINE([HAVE_CLUTTER_WAYLAND], [1], [Have the Wayland backend]) + ]) + AS_IF([test "x$SUPPORT_EGL" = "x1"], [ AC_DEFINE([HAVE_CLUTTER_EGL], [1], [Have the EGL backend]) @@ -395,24 +420,29 @@ AS_IF([test "x$CLUTTER_EGL_BACKEND" = "xgeneric"], AS_IF([test "x$CLUTTER_EGL_BACKEND" = "xcex100"], AC_DEFINE([CLUTTER_EGL_BACKEND_CEX100], [1], [Use CEX100 EGL backend])) -AS_IF([test "x$SUPPORT_EGL_POWERVR_X11" = "x1"], +AS_IF([test "x$SUPPORT_EGL_PLATFORM_POWERVR_X11" = "x1"], [ AC_DEFINE([COGL_HAS_EGL_PLATFORM_POWERVR_X11_SUPPORT], [1], - [Cogl supports OpenGLES using the EGL API with PowerVR X11 platform typedefs]) + [Cogl supports OpenGL[ES] using the EGL API with PowerVR X11 platform typedefs]) ]) -AS_IF([test "x$SUPPORT_EGL_POWERVR_NULL" = "x1"], +AS_IF([test "x$SUPPORT_EGL_PLATFORM_POWERVR_NULL" = "x1"], [ AC_DEFINE([COGL_HAS_EGL_PLATFORM_POWERVR_NULL_SUPPORT], [1], - [Cogl supports OpenGLES using the EGL API with PowerVR NULL platform typedefs]) + [Cogl supports OpenGL[ES] using the EGL API with PowerVR NULL platform typedefs]) ]) -AS_IF([test "x$SUPPORT_EGL_POWERVR_GDL" = "x1"], +AS_IF([test "x$SUPPORT_EGL_PLATFORM_POWERVR_GDL" = "x1"], [ AC_DEFINE([COGL_HAS_EGL_PLATFORM_POWERVR_GDL_SUPPORT], [1], - [Cogl supports OpenGLES using the EGL API with PowerVR GDL platform typedefs]) + [Cogl supports OpenGL[ES] using the EGL API with the GDL API]) ]) +AS_IF([test "x$SUPPORT_EGL_PLATFORM_DRM_SURFACELESS" = "x1"], + [ + AC_DEFINE([COGL_HAS_EGL_PLATFORM_DRM_SURFACELESS_SUPPORT], [1], + [Cogl supports OpenGL[ES] using the EGL API with EGL_MESA_drm_display]) + ]) # winsys conditionals for use in automake files... AM_CONDITIONAL(SUPPORT_GLX, [test "x$SUPPORT_GLX" = "x1"]) @@ -423,15 +453,20 @@ AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_POWERVR_X11, [test "x$SUPPORT_EGL_PLATFORM_P AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_POWERVR_NULL, [test "x$SUPPORT_EGL_PLATFORM_POWERVR_NULL" = "x1"]) AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_POWERVR_GDL, [test "x$SUPPORT_EGL_PLATFORM_POWERVR_GDL" = "x1"]) AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_FRUITY, [test "x$CLUTTER_WINSYS" = "xfruity"]) +AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_DRM_SURFACELESS, [test "x$SUPPORT_EGL_PLATFORM_DRM_SURFACELESS" = "x1"]) +AM_CONDITIONAL(SUPPORT_EGL_PLATFORM_FRUITY, [test "x$CLUTTER_WINSYS" = "xfruity"]) AM_CONDITIONAL(SUPPORT_OSX, [test "x$CLUTTER_WINSYS" = "xosx"]) AM_CONDITIONAL(SUPPORT_FRUITY, [test "x$CLUTTER_WINSYS" = "xfruity"]) AM_CONDITIONAL(SUPPORT_WIN32, [test "x$CLUTTER_WINSYS" = "xwin32"]) AM_CONDITIONAL(SUPPORT_CEX100, [test "x$SUPPORT_EGL_PLATFORM_POWERVR_GDL" = "x1"]) +AM_CONDITIONAL(SUPPORT_WAYLAND, [test "x$CLUTTER_WINSYS" = "xwayland"]) dnl === COGL driver backend ===================================================== AS_IF([test "x$COGL_DRIVER" = "xgl"], - [ AC_DEFINE([HAVE_COGL_GL], [1], [Have GL for rendering]) ]) + [ AC_DEFINE([HAVE_COGL_GL], [1], [Have GL for rendering]) + NEED_SEPARATE_EGL=yes + ]) cogl_gles_version_define="" @@ -478,7 +513,9 @@ AS_IF([test "x$COGL_DRIVER" = "xgles"], AS_IF([test "x$NEED_SEPARATE_EGL" = "xyes"], [ PKG_CHECK_EXISTS([egl], - [BACKEND_PC_FILES="$BACKEND_PC_FILES egl"], + [BACKEND_PC_FILES="$BACKEND_PC_FILES egl" + NEED_SEPARATE_EGL=yes + ], [ AC_CHECK_HEADERS([GLES/egl.h], [], @@ -524,6 +561,8 @@ AS_IF([test "x$COGL_DRIVER" = "xgles"], FLAVOUR_LIBS="$FLAVOUR_LIBS -lEGL" ] ) + + NEED_SEPARATE_EGL=yes ], [fruity], @@ -561,6 +600,10 @@ if test "x$SUPPORT_XLIB" = "x1"; then COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_XLIB" fi; +if test "x$NEED_SEPARATE_EGL" = "xyes"; then + AC_DEFINE([HAVE_STANDALONE_EGL], 1, [Have standalone EGL library]) +fi + AS_CASE([$COGL_DRIVER], [gl], [COGL_DEFINES_SYMBOLS="$COGL_DEFINES_SYMBOLS COGL_HAS_GL"