diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 41ff69cdd..cbd76fdc7 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -699,6 +699,33 @@ clutter-wayland-compositor-$(CLUTTER_API_VERSION).pc: clutter-$(CLUTTER_API_VERS pc_files += clutter-wayland-compositor-$(CLUTTER_API_VERSION).pc endif +# Mir backend rules +if SUPPORT_MIR +backend_source_h_priv += \ + mir/clutter-backend-mir.h \ + mir/clutter-backend-mir-priv.h \ + mir/clutter-stage-mir.h \ + mir/clutter-event-mir.h \ + mir/clutter-input-device-mir.h \ + mir/clutter-device-manager-mir.h + +backend_source_c += \ + mir/clutter-backend-mir.c \ + mir/clutter-stage-mir.c \ + mir/clutter-event-mir.c \ + mir/clutter-input-device-mir.c \ + mir/clutter-device-manager-mir.c + + +cluttermir_includedir = $(clutter_includedir)/mir +cluttermir_include_HEADERS = $(srcdir)/mir/clutter-mir.h + +clutter-mir-$(CLUTTER_API_VERSION).pc: clutter-$(CLUTTER_API_VERSION).pc + $(QUIET_GEN)cp -f $< $(@F) + +pc_files += clutter-mir-$(CLUTTER_API_VERSION).pc +endif # SUPPORT_MIR + if SUPPORT_EGL backend_source_h += $(egl_source_h) backend_source_c += $(egl_source_c) @@ -898,7 +925,7 @@ clutter.vsenums_h: echo 'perl %1\bin\glib-mkenums --template ../../clutter/clutter-enum-types.h.in ' >vsenums_h.temp1 for F in `echo $(source_h) $(backend_source_h) $(srcdir)/win32/clutter-win32.h`; do \ case $$F in \ - *-x11*.h|*-wayland*.h|*-gdk*.h|*-glx*.h|*-cex*.h|*-egl*.h|*-osx*.h) ;; \ + *-x11*.h|*-wayland*.h|*-gdk*.h|*-glx*.h|*-cex*.h|*-egl*.h|*-osx*.h|*-mir*.h) ;; \ *.h) echo '../../clutter'$$F' ' \ ;; \ esac; \ @@ -913,7 +940,7 @@ clutter.vsenums_c: echo 'perl %1\bin\glib-mkenums --template ../../clutter/clutter-enum-types.c.in ' >vsenums_c.temp1 for F in `echo $(source_h) $(backend_source_h) $(srcdir)/win32/clutter-win32.h`; do \ case $$F in \ - *-x11*.h|*-wayland*.h|*-gdk*.h|*-glx*.h|*-cex*.h|*-egl*.h|*-osx*.h) ;; \ + *-x11*.h|*-wayland*.h|*-gdk*.h|*-glx*.h|*-cex*.h|*-egl*.h|*-osx*.h|*-mir*.h) ;; \ *.h) echo '../../clutter'$$F' ' \ ;; \ esac; \ diff --git a/clutter/clutter-backend.c b/clutter/clutter-backend.c index 4b1795602..ac00d2708 100644 --- a/clutter/clutter-backend.c +++ b/clutter/clutter-backend.c @@ -89,6 +89,12 @@ #ifdef CLUTTER_INPUT_WAYLAND #include "wayland/clutter-device-manager-wayland.h" #endif +#ifdef CLUTTER_WINDOWING_MIR +#include "mir/clutter-backend-mir.h" +#endif +#ifdef CLUTTER_INPUT_MIR +#include "mir/clutter-device-manager-mir.h" +#endif #ifdef HAVE_CLUTTER_WAYLAND_COMPOSITOR #include @@ -510,6 +516,11 @@ _clutter_create_backend (void) if (backend == NULL || backend == I_(CLUTTER_WINDOWING_EGL)) retval = g_object_new (CLUTTER_TYPE_BACKEND_EGL_NATIVE, NULL); else +#endif +#ifdef CLUTTER_WINDOWING_MIR + if (backend == NULL || backend == I_(CLUTTER_WINDOWING_MIR)) + retval = g_object_new (CLUTTER_TYPE_BACKEND_MIR, NULL); + else #endif if (backend == NULL) g_error ("No default Clutter backend found."); @@ -589,6 +600,14 @@ clutter_backend_real_init_events (ClutterBackend *backend) _clutter_events_wayland_init (backend); } else +#endif +#ifdef CLUTTER_INPUT_MIR + if (clutter_check_windowing_backend (CLUTTER_WINDOWING_MIR) && + (input_backend == NULL || input_backend == I_(CLUTTER_INPUT_MIR))) + { + _clutter_events_mir_init (backend); + } + else #endif if (input_backend != NULL) { diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index c21f897d2..5b84062a3 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -89,6 +89,9 @@ #ifdef CLUTTER_WINDOWING_WAYLAND #include "wayland/clutter-backend-wayland.h" #endif +#ifdef CLUTTER_WINDOWING_MIR +#include "mir/clutter-backend-mir.h" +#endif #include #include @@ -3781,6 +3784,12 @@ clutter_check_windowing_backend (const char *backend_type) return TRUE; else #endif +#ifdef CLUTTER_WINDOWING_MIR + if (backend_type == I_(CLUTTER_WINDOWING_MIR) && + CLUTTER_IS_BACKEND_MIR (context->backend)) + return TRUE; + else +#endif #ifdef CLUTTER_WINDOWING_GDK if (backend_type == I_(CLUTTER_WINDOWING_GDK) && CLUTTER_IS_BACKEND_GDK (context->backend)) diff --git a/clutter/mir/clutter-backend-mir-priv.h b/clutter/mir/clutter-backend-mir-priv.h new file mode 100644 index 000000000..11ee162f4 --- /dev/null +++ b/clutter/mir/clutter-backend-mir-priv.h @@ -0,0 +1,46 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifndef __CLUTTER_BACKEND_MIR_PRIV_H__ +#define __CLUTTER_BACKEND_MIR_PRIV_H__ + +#include +#include +#include + +#include "clutter-backend-private.h" + +G_BEGIN_DECLS + +struct _ClutterBackendMir +{ + ClutterBackend parent_instance; + + MirConnection *mir_connection; + GSource *mir_source; +}; + +G_END_DECLS + +#endif /* __CLUTTER_BACKEND_MIR_PRIV_H__ */ diff --git a/clutter/mir/clutter-backend-mir.c b/clutter/mir/clutter-backend-mir.c new file mode 100644 index 000000000..d537483da --- /dev/null +++ b/clutter/mir/clutter-backend-mir.c @@ -0,0 +1,231 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-debug.h" +#include "clutter-main.h" +#include "clutter-stage-private.h" + +#include "mir/clutter-backend-mir-priv.h" +#include "mir/clutter-backend-mir.h" +#include "mir/clutter-device-manager-mir.h" +#include "mir/clutter-event-mir.h" +#include "mir/clutter-stage-mir.h" +#include "mir/clutter-mir.h" + +#define clutter_backend_mir_get_type _clutter_backend_mir_get_type + +G_DEFINE_TYPE (ClutterBackendMir, clutter_backend_mir, CLUTTER_TYPE_BACKEND); + +static MirConnection *_foreign_connection = NULL; +static gboolean _no_event_dispatch = FALSE; + +static gboolean +clutter_backend_mir_post_parse (ClutterBackend *backend, + GError **error) +{ + ClutterBackendMir *backend_mir = CLUTTER_BACKEND_MIR (backend); + + backend_mir->mir_connection = _foreign_connection; + if (backend_mir->mir_connection == NULL) + backend_mir->mir_connection = mir_connect_sync (NULL, "Clutter"); + + if (!mir_connection_is_valid (backend_mir->mir_connection)) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Failed to open Mir display socket %s", + mir_connection_get_error_message (backend_mir->mir_connection)); + mir_connection_release (backend_mir->mir_connection); + return FALSE; + } + + g_object_set (clutter_settings_get_default (), "font-dpi", 96 * 1024, NULL); + + return TRUE; +} + +static CoglRenderer * +clutter_backend_mir_get_renderer (ClutterBackend *backend, + GError **error) +{ + ClutterBackendMir *backend_mir = CLUTTER_BACKEND_MIR (backend); + CoglRenderer *renderer; + + CLUTTER_NOTE (BACKEND, "Creating a new Mir renderer"); + + renderer = cogl_renderer_new (); + + cogl_renderer_set_winsys_id (renderer, COGL_WINSYS_ID_EGL_MIR); + cogl_mir_renderer_set_foreign_connection (renderer, + backend_mir->mir_connection); + + return renderer; +} + +static CoglDisplay * +clutter_backend_mir_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); + + 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 +on_mir_event_cb (CoglMirEvent *mir_event, + void *data) +{ + ClutterBackend *backend = data; + _clutter_mir_handle_event (backend, mir_event->surface, mir_event->event); +} + +void +_clutter_events_mir_init (ClutterBackend *backend) +{ + ClutterBackendMir *backend_mir = CLUTTER_BACKEND_MIR (backend); + CoglRenderer *cogl_renderer = backend->cogl_renderer; + + backend->device_manager = _clutter_device_manager_mir_new (backend); + + if (_no_event_dispatch) + return; + + cogl_mir_renderer_add_event_listener (cogl_renderer, on_mir_event_cb, backend); + backend_mir->mir_source = _clutter_event_source_mir_new (); +} + + +static void +clutter_backend_mir_init (ClutterBackendMir *backend_mir) +{ +} + +static void +clutter_backend_mir_dispose (GObject *gobject) +{ + ClutterBackend *backend = CLUTTER_BACKEND (gobject); + ClutterBackendMir *backend_mir = CLUTTER_BACKEND_MIR (backend); + CoglRenderer *cogl_renderer = backend->cogl_renderer; + + g_clear_object (&backend->device_manager); + g_clear_pointer (&backend_mir->mir_source, g_source_unref); + cogl_mir_renderer_remove_event_listener (cogl_renderer, on_mir_event_cb, + backend); + + G_OBJECT_CLASS (clutter_backend_mir_parent_class)->dispose (gobject); +} + +static void +clutter_backend_mir_class_init (ClutterBackendMirClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); + + gobject_class->dispose = clutter_backend_mir_dispose; + + backend_class->stage_window_type = CLUTTER_TYPE_STAGE_MIR; + + backend_class->post_parse = clutter_backend_mir_post_parse; + backend_class->get_renderer = clutter_backend_mir_get_renderer; + backend_class->get_display = clutter_backend_mir_get_display; +} + +/** + * clutter_mir_set_connection + * @connection: pointer to a mir connection + * + * Sets the display connection Clutter should use; must be called + * before clutter_init(), clutter_init_with_args() or other functions + * pertaining Clutter's initialization process. + * + * If you are parsing the command line arguments by retrieving Clutter's + * #GOptionGroup with clutter_get_option_group() and calling + * g_option_context_parse() yourself, you should also call + * clutter_mir_set_connection() before g_option_context_parse(). + * + * Since: 1.22 + */ +void +clutter_mir_set_connection (MirConnection *connection) +{ + g_return_if_fail (mir_connection_is_valid (connection)); + + if (_clutter_context_is_initialized ()) + { + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); + return; + } + + _foreign_connection = connection; +} + +/** + * clutter_mir_disable_event_retrieval: + * + * Disables the dispatch of the events in the main loop. + * + * This is useful for integrating Clutter with another library that will do the + * event dispatch; + * + * This function can only be called before calling clutter_init(). + * + * This function should not be normally used by applications. + * + * Since: 1.22 + */ +void +clutter_mir_disable_event_retrieval (void) +{ + if (_clutter_context_is_initialized ()) + { + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); + return; + } + + _no_event_dispatch = TRUE; +} diff --git a/clutter/mir/clutter-backend-mir.h b/clutter/mir/clutter-backend-mir.h new file mode 100644 index 000000000..beecf8a5d --- /dev/null +++ b/clutter/mir/clutter-backend-mir.h @@ -0,0 +1,55 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifndef __CLUTTER_BACKEND_MIR_H__ +#define __CLUTTER_BACKEND_MIR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BACKEND_MIR (_clutter_backend_mir_get_type ()) +#define CLUTTER_BACKEND_MIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_MIR, ClutterBackendMir)) +#define CLUTTER_IS_BACKEND_MIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_MIR)) +#define CLUTTER_BACKEND_MIR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_MIR, ClutterBackendMirClass)) +#define CLUTTER_IS_BACKEND_MIR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_MIR)) +#define CLUTTER_BACKEND_MIR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_MIR, ClutterBackendMirClass)) + +typedef struct _ClutterBackendMir ClutterBackendMir; +typedef struct _ClutterBackendMirClass ClutterBackendMirClass; + +struct _ClutterBackendMirClass +{ + ClutterBackendClass parent_class; +}; + +GType _clutter_backend_mir_get_type (void) G_GNUC_CONST; + +void +_clutter_events_mir_init (ClutterBackend *backend); + +G_END_DECLS + +#endif /* __CLUTTER_BACKEND_MIR_H__ */ diff --git a/clutter/mir/clutter-device-manager-mir.c b/clutter/mir/clutter-device-manager-mir.c new file mode 100644 index 000000000..8fb87c5bd --- /dev/null +++ b/clutter/mir/clutter-device-manager-mir.c @@ -0,0 +1,192 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-device-manager-private.h" +#include "clutter-input-device-mir.h" +#include "clutter-device-manager-mir.h" + +static guint device_counter; + +G_DEFINE_TYPE (ClutterDeviceManagerMir, _clutter_device_manager_mir, CLUTTER_TYPE_DEVICE_MANAGER); + +static void +clutter_device_manager_mir_add_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + ClutterDeviceManagerMir *manager_mir = CLUTTER_DEVICE_MANAGER_MIR (manager); + manager_mir->devices = g_slist_prepend (manager_mir->devices, device); +} + +static void +clutter_device_manager_mir_remove_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + ClutterDeviceManagerMir *manager_mir = CLUTTER_DEVICE_MANAGER_MIR (manager); + manager_mir->devices = g_slist_remove (manager_mir->devices, device); +} + +static const GSList * +clutter_device_manager_mir_get_devices (ClutterDeviceManager *manager) +{ + return CLUTTER_DEVICE_MANAGER_MIR (manager)->devices; +} + +static ClutterInputDevice * +clutter_device_manager_mir_get_core_device (ClutterDeviceManager *manager, + ClutterInputDeviceType type) +{ + ClutterDeviceManagerMir *manager_mir; + + manager_mir = CLUTTER_DEVICE_MANAGER_MIR (manager); + + switch (type) + { + case CLUTTER_POINTER_DEVICE: + return manager_mir->core_pointer; + + case CLUTTER_KEYBOARD_DEVICE: + return manager_mir->core_keyboard; + + case CLUTTER_EXTENSION_DEVICE: + default: + return NULL; + } + + return NULL; +} + +static ClutterInputDevice * +clutter_device_manager_mir_get_device (ClutterDeviceManager *manager, + gint id) +{ + ClutterDeviceManagerMir *manager_mir = + CLUTTER_DEVICE_MANAGER_MIR (manager); + GSList *l; + + for (l = manager_mir->devices; l != NULL; l = l->next) + { + ClutterInputDevice *device = l->data; + + if (clutter_input_device_get_device_id (device) == id) + return device; + } + + return NULL; +} + +static void +clutter_device_manager_mir_constructed (GObject *gobject) +{ + ClutterBackend *backend; + ClutterDeviceManager *manager; + ClutterDeviceManagerMir *manager_mir; + ClutterInputDevice *device; + + manager = CLUTTER_DEVICE_MANAGER (gobject); + manager_mir = CLUTTER_DEVICE_MANAGER_MIR (manager); + + g_object_get (manager, "backend", &backend, NULL); + + device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_MIR, + "id", device_counter++, + "backend", backend, + "device-manager", manager, + "device-type", CLUTTER_POINTER_DEVICE, + "device-mode", CLUTTER_INPUT_MODE_MASTER, + "name", "Mir pointer", + "enabled", TRUE, + "has-cursor", TRUE, + NULL); + + manager_mir->core_pointer = device; + _clutter_device_manager_add_device (manager, CLUTTER_INPUT_DEVICE (device)); + + device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_MIR, + "id", device_counter++, + "backend", backend, + "device-manager", manager, + "device-type", CLUTTER_KEYBOARD_DEVICE, + "device-mode", CLUTTER_INPUT_MODE_MASTER, + "name", "Mir keyboard", + "enabled", TRUE, + "has-cursor", FALSE, + NULL); + + manager_mir->core_keyboard = device; + _clutter_device_manager_add_device (manager, CLUTTER_INPUT_DEVICE (device)); + + _clutter_input_device_set_associated_device (manager_mir->core_pointer, + manager_mir->core_keyboard); + _clutter_input_device_set_associated_device (manager_mir->core_keyboard, + manager_mir->core_pointer); + + if (G_OBJECT_CLASS (_clutter_device_manager_mir_parent_class)->constructed) + G_OBJECT_CLASS (_clutter_device_manager_mir_parent_class)->constructed (gobject); +} + +static void +clutter_device_manager_mir_finalize (GObject *gobject) +{ + ClutterDeviceManagerMir *manager_mir; + + manager_mir = CLUTTER_DEVICE_MANAGER_MIR (gobject); + g_slist_free_full (manager_mir->devices, g_object_unref); + + G_OBJECT_CLASS (_clutter_device_manager_mir_parent_class)->finalize (gobject); +} + +static void +_clutter_device_manager_mir_class_init (ClutterDeviceManagerMirClass *klass) +{ + ClutterDeviceManagerClass *manager_class; + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->constructed = clutter_device_manager_mir_constructed; + gobject_class->finalize = clutter_device_manager_mir_finalize; + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_mir_add_device; + manager_class->remove_device = clutter_device_manager_mir_remove_device; + manager_class->get_devices = clutter_device_manager_mir_get_devices; + manager_class->get_core_device = clutter_device_manager_mir_get_core_device; + manager_class->get_device = clutter_device_manager_mir_get_device; +} + +static void +_clutter_device_manager_mir_init (ClutterDeviceManagerMir *self) +{ +} + +ClutterDeviceManager * +_clutter_device_manager_mir_new (ClutterBackend *backend) +{ + return g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_MIR, + "backend", backend, + NULL); +} diff --git a/clutter/mir/clutter-device-manager-mir.h b/clutter/mir/clutter-device-manager-mir.h new file mode 100644 index 000000000..f675bff5b --- /dev/null +++ b/clutter/mir/clutter-device-manager-mir.h @@ -0,0 +1,64 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifndef __CLUTTER_DEVICE_MANAGER_MIR_H__ +#define __CLUTTER_DEVICE_MANAGER_MIR_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_DEVICE_MANAGER_MIR (_clutter_device_manager_mir_get_type ()) +#define CLUTTER_DEVICE_MANAGER_MIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEVICE_MANAGER_MIR, ClutterDeviceManagerMir)) +#define CLUTTER_IS_DEVICE_MANAGER_MIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEVICE_MANAGER_MIR)) +#define CLUTTER_DEVICE_MANAGER_MIR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEVICE_MANAGER_MIR, ClutterDeviceManagerMirClass)) +#define CLUTTER_IS_DEVICE_MANAGER_MIR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEVICE_MANAGER_MIR)) +#define CLUTTER_DEVICE_MANAGER_MIR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEVICE_MANAGER_MIR, ClutterDeviceManagerMirClass)) + +typedef struct _ClutterDeviceManagerMir ClutterDeviceManagerMir; +typedef struct _ClutterDeviceManagerMirClass ClutterDeviceManagerMirClass; + +struct _ClutterDeviceManagerMir +{ + ClutterDeviceManager parent_instance; + + GSList *devices; + ClutterInputDevice *core_pointer; + ClutterInputDevice *core_keyboard; +}; + +struct _ClutterDeviceManagerMirClass +{ + ClutterDeviceManagerClass parent_class; +}; + +GType _clutter_device_manager_mir_get_type (void) G_GNUC_CONST; + +ClutterDeviceManager * +_clutter_device_manager_mir_new (ClutterBackend *backend); + +G_END_DECLS + +#endif /* __CLUTTER_DEVICE_MANAGER_MIR_H__ */ diff --git a/clutter/mir/clutter-event-mir.c b/clutter/mir/clutter-event-mir.c new file mode 100644 index 000000000..296430bb9 --- /dev/null +++ b/clutter/mir/clutter-event-mir.c @@ -0,0 +1,395 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-mir.h" +#include "clutter-private.h" +#include "clutter-event-private.h" +#include "clutter-stage-mir.h" +#include "clutter-stage-private.h" +#include "clutter-backend-mir-priv.h" +#include "clutter-device-manager-private.h" +#include "evdev/clutter-xkb-utils.h" + +#include "clutter-event-mir.h" + +#define NANO_TO_MILLI(x) ((x) / 1000000) + +/* Using the clutter threads lock would cause a dead-lock when resizing */ +static GMutex mir_event_lock; + +static gboolean +clutter_event_source_mir_check (GSource *source) +{ + gboolean retval; + + g_mutex_lock (&mir_event_lock); + + retval = clutter_events_pending (); + + g_mutex_unlock (&mir_event_lock); + + return retval; +} + +static gboolean +clutter_event_source_mir_prepare (GSource *source, gint *timeout) +{ + *timeout = -1; + return clutter_event_source_mir_check (source); +} + +static gboolean +clutter_event_source_mir_dispatch (GSource *source, + GSourceFunc callback, + gpointer data) +{ + ClutterEvent *event; + + g_mutex_lock (&mir_event_lock); + _clutter_threads_acquire_lock (); + + event = clutter_event_get (); + + if (event) + { + /* forward the event into clutter for emission etc. */ + _clutter_stage_queue_event (event->any.stage, event, FALSE); + } + + _clutter_threads_release_lock (); + g_mutex_unlock (&mir_event_lock); + + return TRUE; +} + +static void +clutter_event_source_mir_finalize (GSource *source) +{ + g_mutex_clear (&mir_event_lock); +} + +static GSourceFuncs clutter_event_source_mir_funcs = { + clutter_event_source_mir_prepare, + clutter_event_source_mir_check, + clutter_event_source_mir_dispatch, + clutter_event_source_mir_finalize +}; + +GSource * +_clutter_event_source_mir_new (void) +{ + GSource *source; + + source = g_source_new (&clutter_event_source_mir_funcs, sizeof (GSource)); + + g_mutex_init (&mir_event_lock); + g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); + g_source_attach (source, NULL); + + return source; +} + + +static ClutterModifierType +translate_mir_modifier (unsigned int key_modifiers, MirMotionButton button_state) +{ + ClutterModifierType clutter_modifiers = 0; + + if (key_modifiers == mir_key_modifier_none && button_state == 0) + return clutter_modifiers; + + if (key_modifiers & mir_key_modifier_alt) + clutter_modifiers |= CLUTTER_MOD1_MASK; + + if (key_modifiers & mir_key_modifier_shift) + clutter_modifiers |= CLUTTER_SHIFT_MASK; + + if (key_modifiers & mir_key_modifier_ctrl) + clutter_modifiers |= CLUTTER_CONTROL_MASK; + + if (key_modifiers & mir_key_modifier_meta) + clutter_modifiers |= CLUTTER_META_MASK; + + if (key_modifiers & mir_key_modifier_caps_lock) + clutter_modifiers |= CLUTTER_LOCK_MASK; + + if (button_state & mir_motion_button_primary) + clutter_modifiers |= CLUTTER_BUTTON1_MASK; + + if (button_state & mir_motion_button_secondary) + clutter_modifiers |= CLUTTER_BUTTON3_MASK; + + if (button_state & mir_motion_button_tertiary) + clutter_modifiers |= CLUTTER_BUTTON2_MASK; + + return clutter_modifiers; +} + +static gunichar +get_unicode_value (int32_t key_code) +{ + gunichar unicode = '\0'; + char text[8]; + int size; + + size = xkb_keysym_to_utf8 (key_code, text, sizeof (text)); + + if (size > 0) + { + unicode = g_utf8_get_char_validated (text, size); + + if (unicode == -1 || unicode == -2) + unicode = '\0'; + } + + return unicode; +} + +void +_clutter_mir_handle_event (ClutterBackend *backend, + MirSurface *surface, + MirEvent *mir_event) +{ + ClutterStageManager *stage_manager; + ClutterInputDevice *device = NULL; + ClutterStage *stage = NULL; + ClutterEvent *event = NULL; + ClutterModifierType modifiers; + MirMotionButton button_state; + MirMotionPointer *pointer; + const GSList *l; + + stage_manager = clutter_stage_manager_get_default (); + + for (l = clutter_stage_manager_peek_stages (stage_manager); l; l = l->next) + { + ClutterStage* tmp_stage = l->data; + + if (CLUTTER_IS_STAGE (tmp_stage) && + clutter_mir_stage_get_mir_surface (tmp_stage) == surface) + { + stage = tmp_stage; + break; + } + } + + if (!stage) + return; + + g_mutex_lock (&mir_event_lock); + + button_state = CLUTTER_STAGE_MIR (stage)->button_state; + + switch (mir_event->type) + { + case mir_event_type_key: + if (mir_event->key.action == mir_key_action_multiple) + break; + + device = clutter_device_manager_get_core_device (backend->device_manager, + CLUTTER_KEYBOARD_DEVICE); + + event = clutter_event_new (mir_event->key.action == mir_key_action_down ? + CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE); + + modifiers = translate_mir_modifier (mir_event->key.modifiers, button_state); + event->key.time = NANO_TO_MILLI (mir_event->key.event_time); + event->key.modifier_state = modifiers; + event->key.keyval = mir_event->key.key_code; + event->key.hardware_keycode = mir_event->key.scan_code + 8; + event->key.unicode_value = get_unicode_value (mir_event->key.key_code); + break; + + case mir_event_type_motion: + pointer = mir_event->motion.pointer_coordinates; + device = clutter_device_manager_get_core_device (backend->device_manager, + CLUTTER_POINTER_DEVICE); + + /* We need to send an ENTER event again if the stage is not focused anymore */ + if (mir_event->motion.action != mir_motion_action_hover_enter && + mir_event->motion.action != mir_motion_action_hover_exit && + !_clutter_input_device_get_stage (device)) + { + ClutterEvent *new_event = clutter_event_new (CLUTTER_ENTER); + modifiers = translate_mir_modifier (mir_event->motion.modifiers, + button_state); + + clutter_event_set_time (new_event, NANO_TO_MILLI (mir_event->motion.event_time)); + clutter_event_set_state (new_event, modifiers); + clutter_event_set_coords (new_event, pointer->x, pointer->y); + + _clutter_input_device_set_stage (device, stage); + + clutter_event_set_stage (new_event, stage); + clutter_event_set_device (new_event, device); + clutter_event_set_source_device (new_event, device); + + _clutter_event_push (new_event, FALSE); + } + + switch (mir_event->motion.action) + { + case mir_motion_action_down: + case mir_motion_action_pointer_down: + case mir_motion_action_up: + case mir_motion_action_pointer_up: + event = clutter_event_new ((mir_event->motion.action == + mir_motion_action_down || + mir_event->motion.action == + mir_motion_action_pointer_down) ? + CLUTTER_BUTTON_PRESS : + CLUTTER_BUTTON_RELEASE); + + event->button.button = 1; + event->button.click_count = 1; + + button_state ^= mir_event->motion.button_state; + + if (button_state == 0 || (button_state & mir_motion_button_primary)) + event->button.button = 1; + else if (button_state & mir_motion_button_secondary) + event->button.button = 3; + else if (button_state & mir_motion_button_tertiary) + event->button.button = 2; + else if (button_state & mir_motion_button_back) + event->button.button = 8; + else if (button_state & mir_motion_button_forward) + event->button.button = 9; + + button_state = mir_event->motion.button_state; + CLUTTER_STAGE_MIR (stage)->button_state = button_state; + + break; + case mir_motion_action_scroll: + event = clutter_event_new (CLUTTER_SCROLL); + if (ABS (pointer->hscroll) == 1 && pointer->vscroll == 0) + { + clutter_event_set_scroll_direction (event, pointer->hscroll < 0 ? + CLUTTER_SCROLL_LEFT : + CLUTTER_SCROLL_RIGHT); + } + else if (ABS (pointer->vscroll) == 1 && pointer->hscroll == 0) + { + clutter_event_set_scroll_direction (event, pointer->vscroll < 0 ? + CLUTTER_SCROLL_DOWN : + CLUTTER_SCROLL_UP); + } + else + { + clutter_event_set_scroll_delta (event, -pointer->hscroll, -pointer->vscroll); + } + break; + + case mir_motion_action_move: + case mir_motion_action_hover_move: + event = clutter_event_new (CLUTTER_MOTION); + break; + + case mir_motion_action_hover_enter: + event = clutter_event_new (CLUTTER_ENTER); + _clutter_input_device_set_stage (device, stage); + break; + + case mir_motion_action_hover_exit: + event = clutter_event_new (CLUTTER_LEAVE); + _clutter_input_device_set_stage (device, NULL); + break; + } + + if (event) + { + modifiers = translate_mir_modifier (mir_event->motion.modifiers, + button_state); + + clutter_event_set_time (event, NANO_TO_MILLI (mir_event->motion.event_time)); + clutter_event_set_state (event, modifiers); + clutter_event_set_coords (event, pointer->x, pointer->y); + } + + break; + + case mir_event_type_surface: + switch (mir_event->surface.attrib) + { + case mir_surface_attrib_state: + if (mir_event->surface.value == mir_surface_state_fullscreen) + { + _clutter_stage_update_state (stage, + 0, + CLUTTER_STAGE_STATE_FULLSCREEN); + } + else + { + _clutter_stage_update_state (stage, + CLUTTER_STAGE_STATE_FULLSCREEN, + 0); + } + break; + + case mir_surface_attrib_focus: + if (mir_event->surface.value == mir_surface_focused) + { + _clutter_stage_update_state (stage, + 0, + CLUTTER_STAGE_STATE_ACTIVATED); + } + else /* if (mir_event->surface.value == mir_surface_unfocused) */ + { + _clutter_stage_update_state (stage, + CLUTTER_STAGE_STATE_ACTIVATED, + 0); + } + break; + + default: + break; + } + break; + + case mir_event_type_close_surface: + event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); + break; + + default: + break; + } + + if (event) + { + clutter_event_set_stage (event, stage); + clutter_event_set_device (event, device); + clutter_event_set_source_device (event, device); + + _clutter_event_push (event, FALSE); + } + + g_mutex_unlock (&mir_event_lock); + + if (event) + g_main_context_wakeup (NULL); +} diff --git a/clutter/mir/clutter-event-mir.h b/clutter/mir/clutter-event-mir.h new file mode 100644 index 000000000..f537dbbf6 --- /dev/null +++ b/clutter/mir/clutter-event-mir.h @@ -0,0 +1,40 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifndef __CLUTTER_EVENT_MIR_H__ +#define __CLUTTER_EVENT_MIR_H__ + +#include +#include + +#include + +GSource * +_clutter_event_source_mir_new (void); + +void +_clutter_mir_handle_event (ClutterBackend *backend, MirSurface *surface, MirEvent *event); + +#endif /* __CLUTTER_EVENT_MIR_H__ */ diff --git a/clutter/mir/clutter-input-device-mir.c b/clutter/mir/clutter-input-device-mir.c new file mode 100644 index 000000000..3e5f8fa39 --- /dev/null +++ b/clutter/mir/clutter-input-device-mir.c @@ -0,0 +1,55 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-device-manager-private.h" +#include "clutter-input-device-mir.h" + +typedef struct _ClutterInputDeviceClass ClutterInputDeviceMirClass; + +#define clutter_input_device_mir_get_type _clutter_input_device_mir_get_type +G_DEFINE_TYPE (ClutterInputDeviceMir, clutter_input_device_mir, CLUTTER_TYPE_INPUT_DEVICE); + +static gboolean +clutter_input_device_mir_keycode_to_evdev (ClutterInputDevice *device, + guint hardware_keycode, + guint *evdev_keycode) +{ + *evdev_keycode = hardware_keycode - 8; + return TRUE; +} + +static void +clutter_input_device_mir_class_init (ClutterInputDeviceMirClass *klass) +{ + klass->keycode_to_evdev = clutter_input_device_mir_keycode_to_evdev; +} + +static void +clutter_input_device_mir_init (ClutterInputDeviceMir *self) +{ +} diff --git a/clutter/mir/clutter-input-device-mir.h b/clutter/mir/clutter-input-device-mir.h new file mode 100644 index 000000000..a8ee66228 --- /dev/null +++ b/clutter/mir/clutter-input-device-mir.h @@ -0,0 +1,45 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifndef __CLUTTER_INPUT_DEVICE_MIR_H__ +#define __CLUTTER_INPUT_DEVICE_MIR_H__ + +#include +#include + +#define CLUTTER_TYPE_INPUT_DEVICE_MIR (_clutter_input_device_mir_get_type ()) +#define CLUTTER_INPUT_DEVICE_MIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_MIR, ClutterInputDeviceMir)) +#define CLUTTER_IS_INPUT_DEVICE_MIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_MIR)) + +typedef struct _ClutterInputDeviceMir ClutterInputDeviceMir; + +struct _ClutterInputDeviceMir +{ + ClutterInputDevice parent_device; +}; + +GType _clutter_input_device_mir_get_type (void) G_GNUC_CONST; + +#endif /* __CLUTTER_INPUT_DEVICE_MIR_H__ */ diff --git a/clutter/mir/clutter-mir.h b/clutter/mir/clutter-mir.h new file mode 100644 index 000000000..cecc3e31a --- /dev/null +++ b/clutter/mir/clutter-mir.h @@ -0,0 +1,57 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +/** + * SECTION:clutter-mir + * @short_description: Mir specific API + * + * The Mir backend for Clutter provides some specific API, allowing + * integration with the Mir client API for acessing the underlying data + * structures + * + * The Clutter Mir API is available since Clutter 1.22 + */ + +#ifndef __CLUTTER_MIR_H__ +#define __CLUTTER_MIR_H__ + +#include +#include +#include +G_BEGIN_DECLS + +CLUTTER_AVAILABLE_IN_1_22 +MirSurface *clutter_mir_stage_get_mir_surface (ClutterStage *stage); + +CLUTTER_AVAILABLE_IN_1_22 +void clutter_mir_stage_set_mir_surface (ClutterStage *stage, MirSurface *surface); + +CLUTTER_AVAILABLE_IN_1_22 +void clutter_mir_set_connection (MirConnection *connection); + +CLUTTER_AVAILABLE_IN_1_22 +void clutter_mir_disable_event_retrieval (void); + +G_END_DECLS +#endif /* __CLUTTER_MIR_H__ */ diff --git a/clutter/mir/clutter-stage-mir.c b/clutter/mir/clutter-stage-mir.c new file mode 100644 index 000000000..9ca022517 --- /dev/null +++ b/clutter/mir/clutter-stage-mir.c @@ -0,0 +1,293 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-mir.h" +#include "clutter-stage-mir.h" +#include "clutter-backend-mir-priv.h" +#include "clutter-stage-private.h" +#include "clutter-mir.h" +#include + +#define clutter_stage_mir_get_type _clutter_stage_mir_get_type + +static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL; + +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); +static void clutter_stage_mir_set_fullscreen (ClutterStageWindow *stage_window, + gboolean fullscreen); +static void clutter_stage_mir_set_cursor_visible (ClutterStageWindow *stage_window, + gboolean cursor_visible); + +G_DEFINE_TYPE_WITH_CODE (ClutterStageMir, + clutter_stage_mir, + CLUTTER_TYPE_STAGE_COGL, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, + clutter_stage_window_iface_init)); + +static void +on_stage_resized (CoglOnscreen *onscreen, + int width, + int height, + void *user_data) +{ + clutter_actor_set_size (CLUTTER_ACTOR (user_data), width, height); +} + +static gboolean +clutter_stage_mir_realize (ClutterStageWindow *stage_window) +{ + ClutterStageMir *stage_mir = CLUTTER_STAGE_MIR (stage_window); + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + MirSurface *mir_surface; + + if (!clutter_stage_window_parent_iface->realize (stage_window)) + return FALSE; + + cogl_framebuffer_allocate (COGL_FRAMEBUFFER (stage_cogl->onscreen), NULL); + mir_surface = cogl_mir_onscreen_get_surface (stage_cogl->onscreen); + + if (!mir_surface_is_valid (mir_surface)) + { + g_warning ("Realized Mir surface not valid"); + return FALSE; + } + + if (!stage_mir->foreign_mir_surface) + { + cogl_onscreen_add_resize_callback (stage_cogl->onscreen, on_stage_resized, + stage_cogl->wrapper, NULL); + } + + if (stage_mir->surface_state == mir_surface_state_fullscreen) + { + clutter_stage_mir_set_fullscreen (stage_window, TRUE); + stage_mir->surface_state = mir_surface_state_unknown; + } + + if (!stage_mir->cursor_visible) + { + clutter_stage_mir_set_cursor_visible (stage_window, FALSE); + } + + return TRUE; +} + +static void +clutter_stage_mir_show (ClutterStageWindow *stage_window, + gboolean do_raise) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + + cogl_onscreen_show (stage_cogl->onscreen); + clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_cogl->wrapper)); +} + +static void +clutter_stage_mir_hide (ClutterStageWindow *stage_window) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + + cogl_onscreen_hide (stage_cogl->onscreen); + clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper)); + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_cogl->wrapper)); +} + +static void +clutter_stage_mir_set_cursor_visible (ClutterStageWindow *stage_window, + gboolean cursor_visible) +{ + ClutterStageMir *stage_mir = CLUTTER_STAGE_MIR (stage_window); + ClutterActor *actor = _clutter_stage_window_get_wrapper (stage_window); + MirSurface *surface = clutter_mir_stage_get_mir_surface ((ClutterStage *) actor); + MirCursorConfiguration *cursor_conf; + + if (mir_surface_is_valid (surface)) + { + cursor_conf = mir_cursor_configuration_from_name (cursor_visible ? + mir_default_cursor_name : + mir_disabled_cursor_name); + mir_surface_configure_cursor (surface, cursor_conf); + mir_cursor_configuration_destroy (cursor_conf); + } + + stage_mir->cursor_visible = cursor_visible; +} + +static void +clutter_stage_mir_set_fullscreen (ClutterStageWindow *stage_window, + gboolean fullscreen) +{ + ClutterStageMir *stage_mir = CLUTTER_STAGE_MIR (stage_window); + ClutterActor *actor = _clutter_stage_window_get_wrapper (stage_window); + MirSurface *surface = clutter_mir_stage_get_mir_surface ((ClutterStage *) actor); + + if (!mir_surface_is_valid (surface)) + { + stage_mir->surface_state = fullscreen ? + mir_surface_state_fullscreen : + mir_surface_state_unknown; + } + else + { + if (fullscreen) + { + stage_mir->surface_state = mir_surface_get_state (surface); + + if (stage_mir->surface_state != mir_surface_state_fullscreen) + mir_wait_for (mir_surface_set_state (surface, + mir_surface_state_fullscreen)); + } + else if (mir_surface_get_state (surface) == mir_surface_state_fullscreen) + { + mir_wait_for (mir_surface_set_state (surface, stage_mir->surface_state)); + } + } +} + +static void +clutter_stage_mir_resize (ClutterStageWindow *stage_window, + gint width, + gint height) +{ + ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); + + if (stage_cogl->onscreen) + { + cogl_mir_onscreen_resize (stage_cogl->onscreen, width, height); + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_cogl->wrapper)); + } +} + +static gboolean +clutter_stage_mir_can_clip_redraws (ClutterStageWindow *stage_window) +{ + return TRUE; +} + +static void +clutter_stage_mir_init (ClutterStageMir *stage_mir) +{ + stage_mir->cursor_visible = TRUE; + stage_mir->surface_state = mir_surface_state_unknown; +} + +static void +clutter_stage_window_iface_init (ClutterStageWindowIface *iface) +{ + clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface); + + iface->realize = clutter_stage_mir_realize; + iface->show = clutter_stage_mir_show; + iface->hide = clutter_stage_mir_hide; + iface->set_fullscreen = clutter_stage_mir_set_fullscreen; + iface->set_cursor_visible = clutter_stage_mir_set_cursor_visible; + iface->resize = clutter_stage_mir_resize; + iface->can_clip_redraws = clutter_stage_mir_can_clip_redraws; +} + +static void +clutter_stage_mir_class_init (ClutterStageMirClass *klass) +{ +} + +/** + * clutter_mir_stage_get_mir_surface: (skip) + * @stage: a #ClutterStage + * + * Access the underlying data structure representing the surface that is + * backing the #ClutterStage + * + * Note: this function can only be called when running on the Mir + * platform. Calling this function at any other time will return %NULL. + * + * Returns: (transfer none): the Mir surface associated with @stage + * + * Since: 1.22 + */ +MirSurface * +clutter_mir_stage_get_mir_surface (ClutterStage *stage) +{ + ClutterStageWindow *stage_window = _clutter_stage_get_window (stage); + ClutterStageCogl *stage_cogl; + + if (!CLUTTER_IS_STAGE_COGL (stage_window)) + return NULL; + + stage_cogl = CLUTTER_STAGE_COGL (stage_window); + + if (!cogl_is_onscreen (stage_cogl->onscreen)) + return NULL; + + return cogl_mir_onscreen_get_surface (stage_cogl->onscreen); +} + +/** + * clutter_mir_stage_set_mir_surface: + * @stage: a #ClutterStage + * @surface: A Mir surface to associate with the @stage. + * + * Allows you to explicitly provide an existing Mir surface to associate + * with @stage, preventing Cogl from allocating a surface and shell surface for + * the stage automatically. + * + * This function must be called before @stage is shown. + * + * Note: this function can only be called when running on the Mir + * platform. Calling this function at any other time has no effect. + * + * Since: 1.22 + */ +void +clutter_mir_stage_set_mir_surface (ClutterStage *stage, + MirSurface *surface) +{ + ClutterStageWindow *stage_window = _clutter_stage_get_window (stage); + ClutterStageCogl *stage_cogl; + + if (!CLUTTER_IS_STAGE_MIR (stage_window)) + return; + + g_return_if_fail (mir_surface_is_valid (surface)); + + stage_cogl = CLUTTER_STAGE_COGL (stage_window); + + if (stage_cogl->onscreen == NULL) + { + ClutterBackend *backend = clutter_get_default_backend (); + + /* Use the same default dimensions as clutter_stage_cogl_realize() */ + stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context, + 800, 600); + + cogl_mir_onscreen_set_foreign_surface (stage_cogl->onscreen, surface); + CLUTTER_STAGE_MIR (stage_window)->foreign_mir_surface = TRUE; + } + else + g_warning (G_STRLOC ": cannot set foreign surface for stage"); +} diff --git a/clutter/mir/clutter-stage-mir.h b/clutter/mir/clutter-stage-mir.h new file mode 100644 index 000000000..187322b65 --- /dev/null +++ b/clutter/mir/clutter-stage-mir.h @@ -0,0 +1,64 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2014 Canonical Ltd. + * + * 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: + * Marco Trevisan + */ + +#ifndef __CLUTTER_STAGE_MIR_H__ +#define __CLUTTER_STAGE_MIR_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "cogl/clutter-stage-cogl.h" + +#define CLUTTER_TYPE_STAGE_MIR (_clutter_stage_mir_get_type ()) +#define CLUTTER_STAGE_MIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_MIR, ClutterStageMir)) +#define CLUTTER_IS_STAGE_MIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_MIR)) +#define CLUTTER_STAGE_MIR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_MIR, ClutterStageMirClass)) +#define CLUTTER_IS_STAGE_MIR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_MIR)) +#define CLUTTER_STAGE_MIR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_MIR, ClutterStageMirClass)) + +typedef struct _ClutterStageMir ClutterStageMir; +typedef struct _ClutterStageMirClass ClutterStageMirClass; + +struct _ClutterStageMir +{ + ClutterStageCogl parent_instance; + + MirSurfaceState surface_state; + MirMotionButton button_state; + + gboolean foreign_mir_surface; + gboolean cursor_visible; +}; + +struct _ClutterStageMirClass +{ + ClutterStageCoglClass parent_class; +}; + +GType _clutter_stage_mir_get_type (void) G_GNUC_CONST; + +#endif /* __CLUTTER_STAGE_MIR_H__ */ diff --git a/configure.ac b/configure.ac index e8c7262ee..f8ca4e3e4 100644 --- a/configure.ac +++ b/configure.ac @@ -274,6 +274,10 @@ AC_ARG_ENABLE([egl-backend], [AS_HELP_STRING([--enable-egl-backend=@<:@yes/no@:>@], [Enable the EGL framebuffer backend (default=no)])], [enable_egl=$enableval], [enable_egl=no]) +AC_ARG_ENABLE([mir-backend], + [AS_HELP_STRING([--enable-mir-backend=@<:@yes/no@:>@], [Enable the Mir client backend (default=no)])], + [enable_mir=$enableval], + [enable_mir=no]) AC_ARG_ENABLE([cex100-backend], [AS_HELP_STRING([--enable-cex100-backend=@<:@yes/no@:>@], [Enable the CEx100 backend (default=no)])], [enable_cex100=$enableval], @@ -465,6 +469,45 @@ AS_IF([test "x$enable_win32" = "xyes"], SUPPORT_WIN32=1 ]) +AS_IF([test "x$enable_mir" = "xyes"], + [ + CLUTTER_BACKENDS="$CLUTTER_BACKENDS mir" + CLUTTER_INPUT_BACKENDS="$CLUTTER_INPUT_BACKENDS mir" + + SUPPORT_MIR=1 + SUPPORT_COGL=1 + + have_cogl_mir=no + SAVED_CFLAGS="${CFLAGS}" + CFLAGS="`$PKG_CONFIG --cflags $CLUTTER_BASE_PC_FILES`" + + # Manually check whether cogl has Mir support, as we can't rely on + # versioning yet. + AC_MSG_CHECKING([for Mir Cogl backend]) + AC_TRY_COMPILE([#include ], + [ + #ifndef COGL_HAS_EGL_PLATFORM_MIR_SUPPORT + #error "No Mir support in Cogl" + #endif + int main (void) { return 0; } + ], + AC_MSG_RESULT(yes) + have_cogl_mir=yes, + AC_MSG_RESULT(no) + have_cogl_mir=no) + + CFLAGS="${SAVED_CFLAGS}" + + AS_IF([test "x$have_cogl_mir" = "xno"], + [AC_MSG_ERROR([COGL_HAS_EGL_PLATFORM_MIR_SUPPORT not defined but the Mir backend has been explicitly enabled])]) + + PKG_CHECK_EXISTS([mirclient], + [BACKEND_PC_FILES="$BACKEND_PC_FILES mirclient"], + []) + + AC_DEFINE([HAVE_CLUTTER_MIR], [1], [Have the mir client backend]) + ]) + AS_IF([test "x$CLUTTER_BACKENDS" = "x"], [ AC_MSG_ERROR([No backend enabled. You need to enable at least one backend.]) @@ -519,6 +562,7 @@ AM_CONDITIONAL(SUPPORT_OSX, [test "x$SUPPORT_OSX" = "x1"]) AM_CONDITIONAL(SUPPORT_WIN32, [test "x$SUPPORT_WIN32" = "x1"]) AM_CONDITIONAL(SUPPORT_CEX100, [test "x$SUPPORT_CEX100" = "x1"]) AM_CONDITIONAL(SUPPORT_WAYLAND, [test "x$SUPPORT_WAYLAND" = "x1"]) +AM_CONDITIONAL(SUPPORT_MIR, [test "x$SUPPORT_MIR" = "x1"]) AM_CONDITIONAL(USE_COGL, [test "x$SUPPORT_COGL" = "x1"]) AM_CONDITIONAL(USE_TSLIB, [test "x$have_tslib" = "xyes"]) @@ -563,6 +607,10 @@ AS_IF([test "x$SUPPORT_EGL" = "x1"], AS_IF([test "x$SUPPORT_WAYLAND" = "x1"], [CLUTTER_CONFIG_DEFINES="$CLUTTER_CONFIG_DEFINES #define CLUTTER_WINDOWING_WAYLAND \"wayland\""]) +AS_IF([test "x$SUPPORT_MIR" = "x1"], + [CLUTTER_CONFIG_DEFINES="$CLUTTER_CONFIG_DEFINES +#define CLUTTER_WINDOWING_MIR \"mir\" +#define CLUTTER_INPUT_MIR \"mir\""]) AS_IF([test "x$SUPPORT_OSX" = "x1"], [CLUTTER_CONFIG_DEFINES="$CLUTTER_CONFIG_DEFINES #define CLUTTER_WINDOWING_OSX \"osx\"