diff --git a/clutter/Makefile.am b/clutter/Makefile.am index b89349b7b..d28e7e969 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -416,6 +416,34 @@ backend_source_c += $(cogl_source_c) $(glx_source_c) backend_source_h_priv += $(cogl_source_h_priv) endif # SUPPORT_GLX +# GDK backend rules +gdk_source_c = \ + $(srcdir)/gdk/clutter-backend-gdk.c \ + $(srcdir)/gdk/clutter-device-manager-gdk.c \ + $(srcdir)/gdk/clutter-input-device-gdk.c \ + $(srcdir)/gdk/clutter-event-gdk.c \ + $(srcdir)/gdk/clutter-stage-gdk.c \ + $(NULL) + +gdk_source_h = \ + $(srcdir)/gdk/clutter-gdk.h \ + $(NULL) + +gdk_source_h_priv = \ + $(srcdir)/gdk/clutter-settings-gdk.h \ + $(srcdir)/gdk/clutter-backend-gdk.h \ + $(srcdir)/gdk/clutter-device-manager-gdk.h \ + $(srcdir)/gdk/clutter-input-device-gdk.h \ + $(srcdir)/gdk/clutter-event-gdk.h \ + $(srcdir)/gdk/clutter-stage-gdk.h \ + $(NULL) + +if SUPPORT_GDK +backend_source_h += $(cogl_source_h) $(gdk_source_h) +backend_source_c += $(cogl_source_c) $(gdk_source_c) +backend_source_h_priv += $(cogl_source_h_priv) $(gdk_source_h_priv) +endif # SUPPORT_GDK + # Windows backend rules win32_source_c = \ $(srcdir)/win32/clutter-backend-win32.c \ diff --git a/clutter/cogl/clutter-backend-cogl.c b/clutter/cogl/clutter-backend-cogl.c index f37ca197e..6741bdd87 100644 --- a/clutter/cogl/clutter-backend-cogl.c +++ b/clutter/cogl/clutter-backend-cogl.c @@ -36,6 +36,17 @@ #include +#include "clutter-config.h" + +#ifdef CLUTTER_WINDOWING_GDK +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#endif + #include "clutter-backend-cogl.h" #include "clutter-stage-cogl.h" @@ -67,8 +78,10 @@ static gdl_plane_id_t gdl_plane = GDL_PLANE_ID_UPP_C; static guint gdl_n_buffers = CLUTTER_CEX100_TRIPLE_BUFFERING; #endif -#ifdef COGL_HAS_X11_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 G_DEFINE_TYPE (ClutterBackendCogl, _clutter_backend_cogl, CLUTTER_TYPE_BACKEND_X11); +#elif defined(CLUTTER_WINDOWING_GDK) +G_DEFINE_TYPE (ClutterBackendCogl, _clutter_backend_cogl, CLUTTER_TYPE_BACKEND_GDK); #else G_DEFINE_TYPE (ClutterBackendCogl, _clutter_backend_cogl, CLUTTER_TYPE_BACKEND); #endif @@ -87,7 +100,7 @@ clutter_backend_cogl_pre_parse (ClutterBackend *backend, GError **error) { const gchar *env_string; -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK) ClutterBackendClass *parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_cogl_parent_class); @@ -109,7 +122,7 @@ static gboolean clutter_backend_cogl_post_parse (ClutterBackend *backend, GError **error) { -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK) ClutterBackendClass *parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_cogl_parent_class); @@ -122,7 +135,7 @@ clutter_backend_cogl_post_parse (ClutterBackend *backend, return TRUE; } -#ifndef COGL_HAS_XLIB_SUPPORT +#if !(defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK)) static ClutterDeviceManager * clutter_backend_cogl_get_device_manager (ClutterBackend *backend) { @@ -140,7 +153,7 @@ clutter_backend_cogl_get_device_manager (ClutterBackend *backend) return backend_cogl->device_manager; } -#endif +#endif /* !(X11 || GDK) */ static void clutter_backend_cogl_init_events (ClutterBackend *backend) @@ -152,8 +165,8 @@ clutter_backend_cogl_init_events (ClutterBackend *backend) #ifdef HAVE_EVDEV _clutter_events_evdev_init (CLUTTER_BACKEND (backend)); #endif -#ifdef COGL_HAS_X11_SUPPORT - /* Chain up to the X11 backend */ +#if defined (CLUTTER_WINDOWING_X11) || defined (CLUTTER_WINDOWING_GDK) + /* Chain up to the X11 or GDK backend */ CLUTTER_BACKEND_CLASS (_clutter_backend_cogl_parent_class)-> init_events (backend); #endif @@ -227,15 +240,15 @@ static ClutterFeatureFlags clutter_backend_cogl_get_features (ClutterBackend *backend) { ClutterBackendCogl *backend_cogl = CLUTTER_BACKEND_COGL (backend); -#ifdef COGL_HAS_XLIB_SUPPORT - ClutterBackendClass *parent_class; -#endif ClutterFeatureFlags flags = 0; -#ifdef COGL_HAS_XLIB_SUPPORT - parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_cogl_parent_class); +#if defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK) + { + ClutterBackendClass *parent_class; + parent_class = CLUTTER_BACKEND_CLASS (_clutter_backend_cogl_parent_class); - flags = parent_class->get_features (backend); + flags = parent_class->get_features (backend); + } #endif if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN)) @@ -272,7 +285,7 @@ clutter_backend_cogl_get_features (ClutterBackend *backend) return flags; } -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 static XVisualInfo * clutter_backend_cogl_get_visual_info (ClutterBackendX11 *backend_x11) { @@ -284,8 +297,10 @@ static gboolean clutter_backend_cogl_create_context (ClutterBackend *backend, GError **error) { -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); +#elif CLUTTER_WINDOWING_GDK + ClutterBackendGdk *backend_gdk = CLUTTER_BACKEND_GDK (backend); #endif CoglSwapChain *swap_chain = NULL; CoglOnscreenTemplate *onscreen_template = NULL; @@ -295,17 +310,34 @@ clutter_backend_cogl_create_context (ClutterBackend *backend, return TRUE; backend->cogl_renderer = cogl_renderer_new (); -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 cogl_xlib_renderer_set_foreign_display (backend->cogl_renderer, backend_x11->xdpy); +#elif defined(CLUTTER_WINDOWING_GDK) +#if defined(COGL_HAS_XLIB_SUPPORT) && defined(GDK_WINDOWING_X11) + if (GDK_IS_X11_DISPLAY (backend_gdk->display)) + { + cogl_xlib_renderer_set_foreign_display (backend->cogl_renderer, + gdk_x11_display_get_xdisplay (backend_gdk->display)); + } + else #endif + { + g_warning ("Unsupported GdkDisplay type %s", G_OBJECT_TYPE_NAME (backend_gdk->display)); + goto error; + } +#endif /* GDK */ + if (!cogl_renderer_connect (backend->cogl_renderer, error)) goto error; swap_chain = cogl_swap_chain_new (); -#ifdef COGL_HAS_XLIB_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) cogl_swap_chain_set_has_alpha (swap_chain, clutter_x11_get_use_argb_visual ()); +#elif defined(CLUTTER_WINDOWING_GDK) + cogl_swap_chain_set_has_alpha (swap_chain, + gdk_screen_get_rgba_visual (backend_gdk->screen) != NULL); #endif #ifdef COGL_HAS_EGL_PLATFORM_GDL_SUPPORT @@ -387,7 +419,7 @@ clutter_backend_cogl_create_stage (ClutterBackend *backend, ClutterStage *wrapper, GError **error) { -#ifdef COGL_HAS_XLIB_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); ClutterEventTranslator *translator; ClutterStageWindow *stage; @@ -408,7 +440,14 @@ clutter_backend_cogl_create_stage (ClutterBackend *backend, backend_x11->xscreen_num, (unsigned int) backend_x11->xwin_root); -#else /* COGL_HAS_XLIB_SUPPORT */ + return stage; + +#elif defined(CLUTTER_WINDOWING_GDK) + return g_object_new (CLUTTER_TYPE_STAGE_COGL, + "wrapper", wrapper, + "backend", backend, + NULL); +#else ClutterBackendCogl *backend_cogl = CLUTTER_BACKEND_COGL (backend); ClutterStageWindow *stage; @@ -431,9 +470,8 @@ clutter_backend_cogl_create_stage (ClutterBackend *backend, backend_cogl->stage = stage; -#endif /* COGL_HAS_XLIB_SUPPORT */ - return stage; +#endif /* CLUTTER_WINDOWING_X11 || CLUTTER_WINDOWING_GDK */ } static void @@ -457,7 +495,7 @@ _clutter_backend_cogl_class_init (ClutterBackendCoglClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); -#ifdef COGL_HAS_X11_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 ClutterBackendX11Class *backendx11_class = CLUTTER_BACKEND_X11_CLASS (klass); #endif @@ -468,7 +506,7 @@ _clutter_backend_cogl_class_init (ClutterBackendCoglClass *klass) backend_class->pre_parse = clutter_backend_cogl_pre_parse; backend_class->post_parse = clutter_backend_cogl_post_parse; backend_class->get_features = clutter_backend_cogl_get_features; -#ifndef COGL_HAS_XLIB_SUPPORT +#if !(defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK)) backend_class->get_device_manager = clutter_backend_cogl_get_device_manager; #endif backend_class->init_events = clutter_backend_cogl_init_events; @@ -476,7 +514,7 @@ _clutter_backend_cogl_class_init (ClutterBackendCoglClass *klass) backend_class->create_context = clutter_backend_cogl_create_context; backend_class->ensure_context = clutter_backend_cogl_ensure_context; -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 backendx11_class->get_visual_info = clutter_backend_cogl_get_visual_info; #endif } diff --git a/clutter/cogl/clutter-backend-cogl.h b/clutter/cogl/clutter-backend-cogl.h index c1563d390..68065591e 100644 --- a/clutter/cogl/clutter-backend-cogl.h +++ b/clutter/cogl/clutter-backend-cogl.h @@ -37,9 +37,12 @@ #include "clutter-backend-private.h" -#ifdef COGL_HAS_X11_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 #include "../x11/clutter-backend-x11.h" #endif +#ifdef CLUTTER_WINDOWING_GDK +#include "../gdk/clutter-backend-gdk.h" +#endif G_BEGIN_DECLS @@ -55,10 +58,13 @@ typedef struct _ClutterBackendCoglClass ClutterBackendCoglClass; struct _ClutterBackendCogl { -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 ClutterBackendX11 parent_instance; -#else /* COGL_HAS_X11_SUPPORT */ +#elif defined(CLUTTER_WINDOWING_GDK) + ClutterBackendGdk parent_instance; + +#else ClutterBackend parent_instance; /* main stage singleton */ @@ -73,7 +79,7 @@ struct _ClutterBackendCogl /* event timer */ GTimer *event_timer; -#endif /* COGL_HAS_X11_SUPPORT */ +#endif /* CLUTTER_WINDOWING_X11 || CLUTTER_WINDOWING_GDK */ CoglContext *cogl_context; @@ -82,8 +88,10 @@ struct _ClutterBackendCogl struct _ClutterBackendCoglClass { -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 ClutterBackendX11Class parent_class; +#elif defined(CLUTTER_WINDOWING_GDK) + ClutterBackendGdkClass parent_class; #else ClutterBackendClass parent_class; #endif diff --git a/clutter/cogl/clutter-stage-cogl.c b/clutter/cogl/clutter-stage-cogl.c index 0acec4bef..9e9c0fd57 100644 --- a/clutter/cogl/clutter-stage-cogl.c +++ b/clutter/cogl/clutter-stage-cogl.c @@ -30,6 +30,17 @@ #include "config.h" #endif +#include "clutter-config.h" + +#ifdef CLUTTER_WINDOWING_GDK +#include + +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#endif + #include "clutter-stage-cogl.h" #include "clutter-backend-cogl.h" @@ -43,7 +54,7 @@ #include "clutter-stage-private.h" #include "clutter-util.h" -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK) static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL; #endif @@ -51,8 +62,10 @@ static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterStageCogl, _clutter_stage_cogl, -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) CLUTTER_TYPE_STAGE_X11, +#elif defined(CLUTTER_WINDOWING_GDK) + CLUTTER_TYPE_STAGE_GDK, #else G_TYPE_OBJECT, #endif @@ -66,7 +79,7 @@ clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window) CLUTTER_NOTE (BACKEND, "Unrealizing Cogl stage [%p]", stage_cogl); -#ifdef COGL_HAS_XLIB_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK) /* chain up to the StageX11 implementation */ clutter_stage_window_parent_iface->unrealize (stage_window); #endif @@ -101,8 +114,10 @@ static gboolean clutter_stage_cogl_realize (ClutterStageWindow *stage_window) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); -#ifdef COGL_HAS_XLIB_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_window); +#elif defined(CLUTTER_WINDOWING_GDK) + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); #endif ClutterBackend *backend; CoglFramebuffer *framebuffer; @@ -115,16 +130,25 @@ clutter_stage_cogl_realize (ClutterStageWindow *stage_window) G_OBJECT_TYPE_NAME (stage_cogl), stage_cogl); +#if defined(CLUTTER_WINDOWING_GDK) + /* we need to chain early to parent in the Gdk case, as the X window + must be created by GDK, not Cogl */ + if (!clutter_stage_window_parent_iface->realize (stage_window)) + return FALSE; +#endif + backend = clutter_get_default_backend (); -#ifdef COGL_HAS_XLIB_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) clutter_actor_get_size (CLUTTER_ACTOR (stage_x11->wrapper), &width, &height); +#elif defined(CLUTTER_WINDOWING_GDK) + clutter_actor_get_size (CLUTTER_ACTOR (stage_gdk->wrapper), &width, &height); #endif stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context, width, height); -#ifdef COGL_HAS_XLIB_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) if (stage_x11->xwin != None) { cogl_x11_onscreen_set_foreign_window_xid (stage_cogl->onscreen, @@ -133,6 +157,24 @@ clutter_stage_cogl_realize (ClutterStageWindow *stage_window) stage_x11); } +#elif defined(CLUTTER_WINDOWING_GDK) +#if defined(COGL_HAS_XLIB_SUPPORT) && defined(GDK_WINDOWING_X11) + if (GDK_IS_X11_WINDOW (stage_gdk->window)) + { + cogl_x11_onscreen_set_foreign_window_xid (stage_cogl->onscreen, + gdk_x11_window_get_xid (stage_gdk->window), + _clutter_stage_gdk_update_foreign_event_mask, + stage_gdk); + } + else +#endif + { + g_warning ("Unsupported GdkWindow type %s", G_OBJECT_TYPE_NAME (stage_gdk->window)); + + cogl_object_unref (stage_cogl->onscreen); + stage_cogl->onscreen = NULL; + return FALSE; + } #endif clutter_vblank = _clutter_backend_cogl_get_vblank (); @@ -161,7 +203,7 @@ clutter_stage_cogl_realize (ClutterStageWindow *stage_window) stage_cogl); } -#ifdef COGL_HAS_XLIB_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 if (stage_x11->xwin == None) stage_x11->xwin = cogl_x11_onscreen_get_window_xid (stage_cogl->onscreen); @@ -179,7 +221,7 @@ clutter_stage_cogl_get_pending_swaps (ClutterStageWindow *stage_window) return stage_cogl->pending_swaps; } -#ifndef COGL_HAS_XLIB_SUPPORT +#if !(defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK)) static ClutterActor * clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window) @@ -238,7 +280,7 @@ clutter_stage_cogl_resize (ClutterStageWindow *stage_window, { } -#endif /* COGL_HAS_XLIB_SUPPORT */ +#endif /* X11 || GDK */ static gboolean clutter_stage_cogl_has_redraw_clips (ClutterStageWindow *stage_window) @@ -368,10 +410,14 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) "The time spent in blit_sub_buffer", 0 /* no application private data */); -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) ClutterStageX11 *stage_x11 = CLUTTER_STAGE_X11 (stage_cogl); wrapper = CLUTTER_ACTOR (stage_x11->wrapper); +#elif defined(CLUTTER_WINDOWING_GDK) + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_cogl); + + wrapper = CLUTTER_ACTOR (stage_gdk->wrapper); #else wrapper = CLUTTER_ACTOR (stage_cogl->wrapper); #endif @@ -390,7 +436,7 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) /* some drivers struggle to get going and produce some junk * frames when starting up... */ G_LIKELY (stage_cogl->frame_count > 3) -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) /* While resizing a window clipped redraws are disabled to avoid * artefacts. See clutter-event-x11.c:event_translate for a * detailed explanation */ @@ -564,7 +610,7 @@ clutter_stage_cogl_get_active_framebuffer (ClutterStageWindow *stage_window) static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface) { -#ifdef COGL_HAS_X11_SUPPORT +#if defined(CLUTTER_WINDOWING_X11) || defined(CLUTTER_WINDOWING_GDK) clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface); iface->realize = clutter_stage_cogl_realize; diff --git a/clutter/cogl/clutter-stage-cogl.h b/clutter/cogl/clutter-stage-cogl.h index 73bfb06e0..ade2322cd 100644 --- a/clutter/cogl/clutter-stage-cogl.h +++ b/clutter/cogl/clutter-stage-cogl.h @@ -13,8 +13,14 @@ #include #include #include +#endif + +#ifdef CLUTTER_WINDOWING_X11 #include "../x11/clutter-stage-x11.h" #endif +#ifdef CLUTTER_WINDOWING_GDK +#include "../gdk/clutter-stage-gdk.h" +#endif #include "clutter-backend-cogl.h" @@ -32,12 +38,13 @@ typedef struct _ClutterStageCoglClass ClutterStageCoglClass; struct _ClutterStageCogl { -#ifdef COGL_HAS_X11_SUPPORT - +#ifdef CLUTTER_WINDOWING_X11 ClutterStageX11 parent_instance; -#else +#elif defined(CLUTTER_WINDOWING_GDK) + ClutterStageGdk parent_instance; +#else GObject parent_instance; /* the stage wrapper */ @@ -69,9 +76,9 @@ struct _ClutterStageCogl struct _ClutterStageCoglClass { -#ifdef COGL_HAS_X11_SUPPORT +#ifdef CLUTTER_WINDOWING_X11 ClutterStageX11Class parent_class; -#else +#elif defined(CLUTTER_WINDOWING_GDK) GObjectClass parent_class; #endif }; diff --git a/clutter/gdk/clutter-backend-gdk-private.h b/clutter/gdk/clutter-backend-gdk-private.h new file mode 100644 index 000000000..18c53facd --- /dev/null +++ b/clutter/gdk/clutter-backend-gdk-private.h @@ -0,0 +1,32 @@ +/* An OpenGL based 'interactive canvas' library. + * Authored By Matthew Allum + * Copyright (C) 2006-2007 OpenedHand + * + * 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 . + * + * + */ + +#ifndef __CLUTTER_BACKEND_PRIVATE_X11_H__ +#define __CLUTTER_BACKEND_PRIVATE_X11_H__ + + +G_BEGIN_DECLS + +void _clutter_backend_x11_events_init (ClutterBackend *backend); +void _clutter_backend_x11_events_uninit (ClutterBackend *backend); + +G_END_DECLS + +#endif diff --git a/clutter/gdk/clutter-backend-gdk.c b/clutter/gdk/clutter-backend-gdk.c new file mode 100644 index 000000000..9286d85b8 --- /dev/null +++ b/clutter/gdk/clutter-backend-gdk.c @@ -0,0 +1,368 @@ +/* Clutter. + * An OpenGL based 'interactive canvas' library. + * Authored By Matthew Allum + * Copyright (C) 2006-2007 OpenedHand + * + * 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 . + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif +/* other backends not yet supported */ + +#include "clutter-backend-gdk.h" +#include "clutter-device-manager-gdk.h" +#include "clutter-settings-gdk.h" +#include "clutter-stage-gdk.h" +#include "clutter-gdk.h" + +#include "clutter-backend.h" +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-event-private.h" +#include "clutter-main.h" +#include "clutter-private.h" + +#define clutter_backend_gdk_get_type _clutter_backend_gdk_get_type +G_DEFINE_TYPE (ClutterBackendGdk, clutter_backend_gdk, CLUTTER_TYPE_BACKEND); + +/* singleton object */ +static ClutterBackendGdk *backend_singleton = NULL; + +/* global for pre init setup calls */ +static GdkDisplay *_foreign_dpy = NULL; + +static void +clutter_backend_gdk_init_settings (ClutterBackendGdk *backend_gdk) +{ + ClutterSettings *settings = clutter_settings_get_default (); + int i; + + for (i = 0; i < G_N_ELEMENTS (_clutter_settings_map); i++) + { + GValue val = G_VALUE_INIT; + + g_value_init (&val, CLUTTER_SETTING_TYPE(i)); + gdk_screen_get_setting (backend_gdk->screen, + CLUTTER_SETTING_GDK_NAME(i), + &val); + g_object_set_property (G_OBJECT (settings), + CLUTTER_SETTING_PROPERTY(i), + &val); + g_value_unset (&val); + } +} + +void +_clutter_backend_gdk_update_setting (ClutterBackendGdk *backend_gdk, + const gchar *setting_name) +{ + ClutterSettings *settings = clutter_settings_get_default (); + int i; + + for (i = 0; i < G_N_ELEMENTS (_clutter_settings_map); i++) + { + if (g_strcmp0 (CLUTTER_SETTING_GDK_NAME (i), setting_name) == 0) + { + GValue val = G_VALUE_INIT; + + g_value_init (&val, CLUTTER_SETTING_TYPE (i)); + gdk_screen_get_setting (backend_gdk->screen, + CLUTTER_SETTING_GDK_NAME (i), + &val); + g_object_set_property (G_OBJECT (settings), + CLUTTER_SETTING_PROPERTY (i), + &val); + g_value_unset (&val); + + break; + } + } +} + +static GdkFilterReturn +cogl_gdk_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ +#ifdef GDK_WINDOWING_X11 + CoglFilterReturn ret; + + ret = cogl_xlib_handle_event ((XEvent*)xevent); + switch (ret) + { + case COGL_FILTER_REMOVE: + return GDK_FILTER_REMOVE; + + case COGL_FILTER_CONTINUE: + default: + return GDK_FILTER_CONTINUE; + } +#endif +} + +static gboolean +_clutter_backend_gdk_pre_parse (ClutterBackend *backend, + GError **error) +{ + /* nothing to do here */ + return TRUE; +} + +static gboolean +_clutter_backend_gdk_post_parse (ClutterBackend *backend, + GError **error) +{ + ClutterBackendGdk *backend_gdk = CLUTTER_BACKEND_GDK (backend); + + if (_foreign_dpy) + backend_gdk->display = _foreign_dpy; + + /* Init Gdk, if outside code did not already */ + if (!gdk_init_check (NULL, NULL)) + return FALSE; + + /* + * Only open connection if not already set by prior call to + * clutter_gdk_set_display() + */ + if (backend_gdk->display == NULL) + backend_gdk->display = g_object_ref (gdk_display_get_default ()); + + g_assert (backend_gdk->display != NULL); + +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY (backend_gdk->display)) + { + /* Cogl needs to know the Xlib display connection for + CoglTexturePixmapX11 */ + cogl_xlib_set_display (gdk_x11_display_get_xdisplay (backend_gdk->display)); + } +#endif + + backend_gdk->screen = gdk_display_get_default_screen (backend_gdk->display); + + /* add event filter for Cogl events */ + gdk_window_add_filter (NULL, cogl_gdk_filter, NULL); + + clutter_backend_gdk_init_settings (backend_gdk); + + CLUTTER_NOTE (BACKEND, + "Gdk Display '%s' opened", + gdk_display_get_name (backend_gdk->display)); + + return TRUE; +} + + +static void +clutter_backend_gdk_init_events (ClutterBackend *backend) +{ + CLUTTER_NOTE (EVENT, "initialising the event loop"); + + _clutter_backend_gdk_events_init (backend); +} + +static void +clutter_backend_gdk_finalize (GObject *gobject) +{ + ClutterBackendGdk *backend_gdk = CLUTTER_BACKEND_GDK (gobject); + + gdk_window_remove_filter (NULL, cogl_gdk_filter, NULL); + g_object_unref (backend_gdk->display); + + if (backend_singleton) + backend_singleton = NULL; + + G_OBJECT_CLASS (clutter_backend_gdk_parent_class)->finalize (gobject); +} + +static void +clutter_backend_gdk_dispose (GObject *gobject) +{ + ClutterBackendGdk *backend_gdk = CLUTTER_BACKEND_GDK (gobject); + ClutterStageManager *stage_manager; + + CLUTTER_NOTE (BACKEND, "Disposing the of stages"); + stage_manager = clutter_stage_manager_get_default (); + + g_object_unref (stage_manager); + + CLUTTER_NOTE (BACKEND, "Removing the event source"); + _clutter_backend_gdk_events_uninit (CLUTTER_BACKEND (backend_gdk)); + + G_OBJECT_CLASS (clutter_backend_gdk_parent_class)->dispose (gobject); +} + +static GObject * +clutter_backend_gdk_constructor (GType gtype, + guint n_params, + GObjectConstructParam *params) +{ + GObjectClass *parent_class; + GObject *retval; + + if (backend_singleton == NULL) + { + parent_class = G_OBJECT_CLASS (clutter_backend_gdk_parent_class); + retval = parent_class->constructor (gtype, n_params, params); + + backend_singleton = CLUTTER_BACKEND_GDK (retval); + + return retval; + } + + g_critical ("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_gdk_get_features (ClutterBackend *backend) +{ + return CLUTTER_FEATURE_STAGE_USER_RESIZE | CLUTTER_FEATURE_STAGE_CURSOR; +} + +static void +clutter_backend_gdk_copy_event_data (ClutterBackend *backend, + const ClutterEvent *src, + ClutterEvent *dest) +{ + GdkEvent *gdk_event; + + gdk_event = _clutter_event_get_platform_data (src); + if (gdk_event != NULL) + _clutter_event_set_platform_data (dest, gdk_event_copy (gdk_event)); +} + +static void +clutter_backend_gdk_free_event_data (ClutterBackend *backend, + ClutterEvent *event) +{ + GdkEvent *gdk_event; + + gdk_event = _clutter_event_get_platform_data (event); + if (gdk_event != NULL) + gdk_event_free (gdk_event); +} + +static ClutterDeviceManager * +clutter_backend_gdk_get_device_manager (ClutterBackend *backend) +{ + ClutterBackendGdk *backend_gdk = CLUTTER_BACKEND_GDK (backend); + + if (G_UNLIKELY (backend_gdk->device_manager == NULL)) + { + backend_gdk->device_manager = g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_GDK, + "backend", backend_gdk, + "gdk-display", backend_gdk->display, + NULL); + } + + return backend_gdk->device_manager; +} + +static void +clutter_backend_gdk_class_init (ClutterBackendGdkClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); + + gobject_class->constructor = clutter_backend_gdk_constructor; + gobject_class->dispose = clutter_backend_gdk_dispose; + gobject_class->finalize = clutter_backend_gdk_finalize; + + backend_class->pre_parse = _clutter_backend_gdk_pre_parse; + backend_class->post_parse = _clutter_backend_gdk_post_parse; + backend_class->init_events = clutter_backend_gdk_init_events; + backend_class->get_features = clutter_backend_gdk_get_features; + backend_class->get_device_manager = clutter_backend_gdk_get_device_manager; + backend_class->copy_event_data = clutter_backend_gdk_copy_event_data; + backend_class->free_event_data = clutter_backend_gdk_free_event_data; +} + +static void +clutter_backend_gdk_init (ClutterBackendGdk *backend_gdk) +{ + /* nothing to do here */ +} + +/** + * clutter_gdk_get_default_display: + * + * Retrieves the pointer to the default display. + * + * Return value: (transfer none): the default display + * + * Since: 0.6 + */ +GdkDisplay * +clutter_gdk_get_default_display (void) +{ + if (!backend_singleton) + { + g_critical ("GDK backend has not been initialised"); + return NULL; + } + + return backend_singleton->display; +} + +/** + * clutter_gdk_set_display: + * @display: pointer to a GDK display 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_gdk_set_display() before g_option_context_parse(). + * + * Since: 0.8 + */ +void +clutter_gdk_set_display (GdkDisplay *display) +{ + if (_clutter_context_is_initialized ()) + { + g_warning ("%s() can only be used before calling clutter_init()", + G_STRFUNC); + return; + } + + _foreign_dpy = g_object_ref (display); +} diff --git a/clutter/gdk/clutter-backend-gdk.h b/clutter/gdk/clutter-backend-gdk.h new file mode 100644 index 000000000..effa6425d --- /dev/null +++ b/clutter/gdk/clutter-backend-gdk.h @@ -0,0 +1,72 @@ +/* Clutter. + * An OpenGL based 'interactive canvas' library. + * Authored By Matthew Allum + * Copyright (C) 2006-2007 OpenedHand + * 2011 Giovanni Campagna + * + * 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 . + * + * + */ + +#ifndef __CLUTTER_BACKEND_GDK_H__ +#define __CLUTTER_BACKEND_GDK_H__ + +#include +#include +#include + +#include "clutter-gdk.h" + +#include "clutter-backend-private.h" + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BACKEND_GDK (_clutter_backend_gdk_get_type ()) +#define CLUTTER_BACKEND_GDK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_GDK, ClutterBackendGdk)) +#define CLUTTER_IS_BACKEND_GDK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_GDK)) +#define CLUTTER_BACKEND_GDK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_GDK, ClutterBackendGdkClass)) +#define CLUTTER_IS_BACKEND_GDK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_GDK)) +#define CLUTTER_BACKEND_GDK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_GDK, ClutterBackendGdkClass)) + +typedef struct _ClutterBackendGdk ClutterBackendGdk; +typedef struct _ClutterBackendGdkClass ClutterBackendGdkClass; + +struct _ClutterBackendGdk +{ + ClutterBackend parent_instance; + + GdkDisplay *display; + GdkScreen *screen; + + ClutterDeviceManager *device_manager; +}; + +struct _ClutterBackendGdkClass +{ + ClutterBackendClass parent_class; + + /* nothing here, for now */ +}; + +GType _clutter_backend_gdk_get_type (void) G_GNUC_CONST; + +void _clutter_backend_gdk_events_init (ClutterBackend *backend); +void _clutter_backend_gdk_events_uninit (ClutterBackend *backend); + +void _clutter_backend_gdk_update_setting (ClutterBackendGdk *backend, const gchar *name); + +G_END_DECLS + +#endif /* __CLUTTER_BACKEND_GDK_H__ */ diff --git a/clutter/gdk/clutter-device-manager-gdk.c b/clutter/gdk/clutter-device-manager-gdk.c new file mode 100644 index 000000000..5175bbab5 --- /dev/null +++ b/clutter/gdk/clutter-device-manager-gdk.c @@ -0,0 +1,249 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2011 Intel Corp. + * 2011 Giovanni Campagna + * + * 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 . + * + * Author: Emmanuele Bassi + */ + +#include "config.h" + +#include + +#include "clutter-device-manager-gdk.h" + +#include "clutter-backend-gdk.h" +#include "clutter-input-device-gdk.h" +#include "clutter-stage-gdk.h" + +#include "clutter-backend.h" +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-event-private.h" +#include "clutter-event-translator.h" +#include "clutter-stage-private.h" +#include "clutter-private.h" + +#define clutter_device_manager_gdk_get_type _clutter_device_manager_gdk_get_type + +G_DEFINE_TYPE (ClutterDeviceManagerGdk, clutter_device_manager_gdk, CLUTTER_TYPE_DEVICE_MANAGER) + +enum { + PROP_0, + PROP_GDK_DISPLAY, + PROP_LAST +}; + +ClutterInputDevice * +_clutter_device_manager_gdk_lookup_device (ClutterDeviceManager *manager, + GdkDevice *device) +{ + ClutterDeviceManagerGdk *manager_gdk = CLUTTER_DEVICE_MANAGER_GDK (manager); + ClutterInputDevice *clutter_device; + + clutter_device = g_object_get_data (G_OBJECT (device), "clutter-device"); + if (clutter_device != NULL) + return clutter_device; + + clutter_device = _clutter_input_device_gdk_new (manager, device); + g_object_set_data_full (G_OBJECT (device), "clutter-device", clutter_device, g_object_unref); + + manager_gdk->device_cache = g_slist_prepend (manager_gdk->device_cache, g_object_ref (clutter_device)); + g_hash_table_replace (manager_gdk->device_by_id, + GINT_TO_POINTER (clutter_input_device_get_device_id (clutter_device)), + g_object_ref (clutter_device)); + + return clutter_device; +} + +static void +clutter_device_manager_gdk_add_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + /* XXX implement */ +} + +static void +clutter_device_manager_gdk_remove_device (ClutterDeviceManager *manager, + ClutterInputDevice *device) +{ + /* XXX implement */ +} + +static const GSList * +clutter_device_manager_gdk_get_devices (ClutterDeviceManager *manager) +{ + ClutterDeviceManagerGdk *manager_gdk = CLUTTER_DEVICE_MANAGER_GDK (manager); + + return manager_gdk->device_cache; +} + +static ClutterInputDevice * +clutter_device_manager_gdk_get_device (ClutterDeviceManager *manager, + gint id) +{ + ClutterDeviceManagerGdk *manager_gdk = CLUTTER_DEVICE_MANAGER_GDK (manager); + + return g_hash_table_lookup (manager_gdk->device_by_id, GINT_TO_POINTER (id)); +} + +static ClutterInputDevice * +clutter_device_manager_gdk_get_core_device (ClutterDeviceManager *manager, + ClutterInputDeviceType device_type) +{ + ClutterDeviceManagerGdk *manager_gdk = CLUTTER_DEVICE_MANAGER_GDK (manager); + GdkDevice *gdk_device; + + gdk_device = gdk_device_manager_get_client_pointer (manager_gdk->device_manager); + + g_assert (gdk_device != NULL); + + if (device_type == CLUTTER_KEYBOARD_DEVICE) + gdk_device = gdk_device_get_associated_device (gdk_device); + else if (device_type != CLUTTER_POINTER_DEVICE) + return NULL; + + return _clutter_device_manager_gdk_lookup_device (manager, gdk_device); +} + +static void +gdk_device_added (GdkDeviceManager *gdk_manager, + GdkDevice *device, + ClutterDeviceManager *self) +{ + /* this will do the right thing if the device is not there */ + ClutterInputDevice *clutter_device = _clutter_device_manager_gdk_lookup_device (self, device); + + _clutter_device_manager_add_device (self, clutter_device); +} + +static void +gdk_device_removed (GdkDeviceManager *gdk_manager, + GdkDevice *device, + ClutterDeviceManagerGdk *self) +{ + ClutterInputDevice *clutter_device = g_object_get_data (G_OBJECT (device), "clutter-device"); + + if (clutter_device == NULL) + return; + + self->device_cache = g_slist_remove (self->device_cache, clutter_device); + g_object_unref (clutter_device); + + g_hash_table_remove (self->device_by_id, + GINT_TO_POINTER (clutter_input_device_get_device_id (clutter_device))); + + _clutter_device_manager_remove_device (CLUTTER_DEVICE_MANAGER (self), clutter_device); +} + +static void +gdk_device_foreach_cb (gpointer data, + gpointer user_data) +{ + _clutter_device_manager_gdk_lookup_device (user_data, data); +} + +static void +clutter_device_manager_gdk_constructed (GObject *gobject) +{ + ClutterDeviceManagerGdk *manager_gdk = CLUTTER_DEVICE_MANAGER_GDK (gobject); + GList *all_devices; + + g_assert (manager_gdk->device_manager != NULL); + + all_devices = gdk_device_manager_list_devices (manager_gdk->device_manager, + GDK_DEVICE_TYPE_MASTER); + g_list_foreach (all_devices, gdk_device_foreach_cb, manager_gdk); + g_list_free (all_devices); + + all_devices = gdk_device_manager_list_devices (manager_gdk->device_manager, + GDK_DEVICE_TYPE_SLAVE); + g_list_foreach (all_devices, gdk_device_foreach_cb, manager_gdk); + g_list_free (all_devices); + + all_devices = gdk_device_manager_list_devices (manager_gdk->device_manager, + GDK_DEVICE_TYPE_FLOATING); + g_list_foreach (all_devices, gdk_device_foreach_cb, manager_gdk); + g_list_free (all_devices); + + g_object_connect (manager_gdk->device_manager, + "object-signal::device-added", gdk_device_added, gobject, + "object-signal::device-removed", gdk_device_removed, gobject, + NULL); + + if (G_OBJECT_CLASS (clutter_device_manager_gdk_parent_class)->constructed) + G_OBJECT_CLASS (clutter_device_manager_gdk_parent_class)->constructed (gobject); +} + +static void +clutter_device_manager_gdk_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterDeviceManagerGdk *manager_gdk = CLUTTER_DEVICE_MANAGER_GDK (gobject); + GdkDisplay *gdk_display; + + switch (prop_id) + { + case PROP_GDK_DISPLAY: + gdk_display = GDK_DISPLAY (g_value_get_object (value)); + manager_gdk->device_manager = gdk_display_get_device_manager (gdk_display); + g_object_ref (manager_gdk->device_manager); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_device_manager_gdk_class_init (ClutterDeviceManagerGdkClass *klass) +{ + ClutterDeviceManagerClass *manager_class; + GObjectClass *gobject_class; + GParamSpec *pspec; + + gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->constructed = clutter_device_manager_gdk_constructed; + gobject_class->set_property = clutter_device_manager_gdk_set_property; + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_gdk_add_device; + manager_class->remove_device = clutter_device_manager_gdk_remove_device; + manager_class->get_devices = clutter_device_manager_gdk_get_devices; + manager_class->get_core_device = clutter_device_manager_gdk_get_core_device; + manager_class->get_device = clutter_device_manager_gdk_get_device; + + pspec = g_param_spec_object ("gdk-display", + "GdkDisplay", + "The GDK display", + GDK_TYPE_DISPLAY, + CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (gobject_class, PROP_GDK_DISPLAY, pspec); +} + +static void +clutter_device_manager_gdk_init (ClutterDeviceManagerGdk *self) +{ + self->device_by_id = g_hash_table_new_full (NULL, NULL, + NULL, (GDestroyNotify) g_object_unref); +} diff --git a/clutter/gdk/clutter-device-manager-gdk.h b/clutter/gdk/clutter-device-manager-gdk.h new file mode 100644 index 000000000..ade24cd89 --- /dev/null +++ b/clutter/gdk/clutter-device-manager-gdk.h @@ -0,0 +1,63 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2011 Intel Corp. + * + * 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 . + * + * Author: Emmanuele Bassi + */ + +#ifndef __CLUTTER_DEVICE_MANAGER_GDK_H__ +#define __CLUTTER_DEVICE_MANAGER_GDK_H__ + +#include +#include + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_DEVICE_MANAGER_GDK (_clutter_device_manager_gdk_get_type ()) +#define CLUTTER_DEVICE_MANAGER_GDK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEVICE_MANAGER_GDK, ClutterDeviceManagerGdk)) +#define CLUTTER_IS_DEVICE_MANAGER_GDK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEVICE_MANAGER_GDK)) +#define CLUTTER_DEVICE_MANAGER_GDK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEVICE_MANAGER_GDK, ClutterDeviceManagerGdkClass)) +#define CLUTTER_IS_DEVICE_MANAGER_GDK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEVICE_MANAGER_GDK)) +#define CLUTTER_DEVICE_MANAGER_GDK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEVICE_MANAGER_GDK, ClutterDeviceManagerGdkClass)) + +typedef struct _ClutterDeviceManagerGdk ClutterDeviceManagerGdk; +typedef struct _ClutterDeviceManagerGdkClass ClutterDeviceManagerGdkClass; + +struct _ClutterDeviceManagerGdk +{ + ClutterDeviceManager parent_instance; + + GdkDeviceManager *device_manager; + GSList *device_cache; + GHashTable *device_by_id; +}; + +struct _ClutterDeviceManagerGdkClass +{ + ClutterDeviceManagerClass parent_class; +}; + +GType _clutter_device_manager_gdk_get_type (void) G_GNUC_CONST; + +ClutterInputDevice * _clutter_device_manager_gdk_lookup_device (ClutterDeviceManager *manager, + GdkDevice *device); + +G_END_DECLS + +#endif /* __CLUTTER_DEVICE_MANAGER_GDK_H__ */ diff --git a/clutter/gdk/clutter-event-gdk.c b/clutter/gdk/clutter-event-gdk.c new file mode 100644 index 000000000..840250968 --- /dev/null +++ b/clutter/gdk/clutter-event-gdk.c @@ -0,0 +1,264 @@ +/* Clutter. + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd + * Copyright (C) 2009, 2010 Intel Corp. + * 2011 Giovanni Campagna + * + * 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 . + * + * + * + * Authored by: + * Matthew Allum + * Emmanuele Bassi + */ + +#include "config.h" + +#include "clutter-gdk.h" +#include "clutter-backend-gdk.h" +#include "clutter-device-manager-gdk.h" + +#include "clutter-debug.h" +#include "clutter-main.h" +#include "clutter-backend-private.h" +#include "clutter-event-private.h" +#include "clutter-stage-private.h" + +#include + +#include + +static void +gdk_event_handler (GdkEvent *event, + gpointer user_data) +{ + clutter_gdk_handle_event (event); +} + +void +_clutter_backend_gdk_events_init (ClutterBackend *backend) +{ + gdk_event_handler_set (gdk_event_handler, NULL, NULL); + + CLUTTER_NOTE (EVENT, "GDK event handler set"); +} + +void +_clutter_backend_gdk_events_uninit (ClutterBackend *backend) +{ + gdk_event_handler_set (NULL, NULL, NULL); +} + +/** + * clutter_gdk_handle_event: + * @event: a #GdkEvent + * + * This function processes a single GDK event; it can be used to hook + * into external event processing + * + * Return value: #GdkFilterReturn. %GDK_FILTER_REMOVE indicates that + * Clutter has internally handled the event and the caller should do + * no further processing. %GDK_FILTER_CONTINUE indicates that Clutter + * is either not interested in the event, or has used the event to + * update internal state without taking any exclusive action. + * %GDK_FILTER_TRANSLATE will not occur. + * + */ +GdkFilterReturn +clutter_gdk_handle_event (GdkEvent *gdk_event) +{ + ClutterDeviceManager *device_manager; + ClutterBackendGdk *backend_gdk; + ClutterStage *stage = NULL; + ClutterEvent *event = NULL; + gint spin = 0; + GdkFilterReturn result = GDK_FILTER_CONTINUE; + + if (gdk_event->any.window == NULL) + return GDK_FILTER_CONTINUE; + + clutter_threads_enter (); + + backend_gdk = CLUTTER_BACKEND_GDK (clutter_get_default_backend ()); + stage = clutter_gdk_get_stage_from_window (gdk_event->any.window); + device_manager = clutter_device_manager_get_default (); + + if (stage == NULL) + goto out; + + switch (gdk_event->type) { + case GDK_DELETE: + event = clutter_event_new (CLUTTER_DELETE); + break; + + case GDK_DESTROY: + event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); + break; + + case GDK_EXPOSE: + clutter_redraw (stage); + break; + + case GDK_DAMAGE: + /* This is handled by cogl */ + goto out; + + case GDK_MOTION_NOTIFY: + event = clutter_event_new (CLUTTER_MOTION); + event->motion.time = gdk_event->motion.time; + event->motion.x = gdk_event->motion.x; + event->motion.y = gdk_event->motion.y; + event->motion.axes = NULL; + /* It's all X in the end, right? */ + event->motion.modifier_state = gdk_event->motion.state; + event->motion.device = _clutter_device_manager_gdk_lookup_device (device_manager, + gdk_event->motion.device); + break; + + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + event = clutter_event_new (gdk_event->type == GDK_BUTTON_PRESS ? + CLUTTER_BUTTON_PRESS : + CLUTTER_BUTTON_RELEASE); + event->button.time = gdk_event->button.time; + event->button.x = gdk_event->button.x; + event->button.y = gdk_event->button.y; + event->button.axes = NULL; + event->button.modifier_state = gdk_event->button.state; + event->button.button = gdk_event->button.button; + event->button.click_count = 1; + event->button.device = _clutter_device_manager_gdk_lookup_device (device_manager, + gdk_event->button.device); + break; + + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + /* these are handled by clutter-main.c updating click_count */ + goto out; + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + event = clutter_event_new (gdk_event->type == GDK_KEY_PRESS ? + CLUTTER_KEY_PRESS : + CLUTTER_KEY_RELEASE); + event->key.time = gdk_event->key.time; + event->key.modifier_state = gdk_event->key.state; + event->key.keyval = gdk_event->key.keyval; + event->key.hardware_keycode = gdk_event->key.hardware_keycode; + event->key.unicode_value = g_utf8_get_char (gdk_event->key.string); + break; + + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + event = clutter_event_new (gdk_event->type == GDK_ENTER_NOTIFY ? + CLUTTER_ENTER : + CLUTTER_LEAVE); + event->crossing.source = CLUTTER_ACTOR (stage); + event->crossing.time = gdk_event->crossing.time; + event->crossing.x = gdk_event->crossing.x; + event->crossing.y = gdk_event->crossing.y; + + /* XXX: no better fallback here? */ + event->crossing.device = clutter_device_manager_get_core_device (device_manager, + CLUTTER_POINTER_DEVICE); + + if (gdk_event->type == GDK_ENTER_NOTIFY) + _clutter_stage_add_device (stage, event->crossing.device); + else + _clutter_stage_remove_device (stage, event->crossing.device); + break; + + case GDK_FOCUS_CHANGE: + event = clutter_event_new (CLUTTER_STAGE_STATE); + event->stage_state.time = 0; /* XXX: there is no timestamp in this GdkEvent */ + event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; + event->stage_state.new_state = gdk_event->focus_change.in ? CLUTTER_STAGE_STATE_ACTIVATED : 0; + break; + + case GDK_CONFIGURE: + clutter_actor_set_size (CLUTTER_ACTOR (stage), + gdk_event->configure.width, + gdk_event->configure.height); + break; + + case GDK_SCROLL: + event = clutter_event_new (CLUTTER_SCROLL); + event->scroll.time = gdk_event->scroll.time; + event->scroll.x = gdk_event->scroll.x; + event->scroll.y = gdk_event->scroll.y; + event->scroll.modifier_state = gdk_event->scroll.state; + event->scroll.axes = NULL; + event->scroll.direction = gdk_event->scroll.direction; + event->scroll.device = _clutter_device_manager_gdk_lookup_device (device_manager, + gdk_event->scroll.device); + + case GDK_WINDOW_STATE: + event = clutter_event_new (CLUTTER_STAGE_STATE); + event->stage_state.changed_mask = 0; + event->stage_state.new_state = 0; + if (gdk_event->window_state.changed_mask & GDK_WINDOW_STATE_WITHDRAWN) + { + event->stage_state.changed_mask |= CLUTTER_STAGE_STATE_OFFSCREEN; + event->stage_state.new_state |= (gdk_event->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN) ? + CLUTTER_STAGE_STATE_OFFSCREEN : 0; + } + if (gdk_event->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) + { + event->stage_state.changed_mask |= CLUTTER_STAGE_STATE_FULLSCREEN; + event->stage_state.new_state |= (gdk_event->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) ? + CLUTTER_STAGE_STATE_FULLSCREEN : 0; + } + break; + + case GDK_SETTING: + _clutter_backend_gdk_update_setting (backend_gdk, gdk_event->setting.name); + break; + + default: + break; + } + + if (event != NULL) + { + event->any.stage = stage; + if (gdk_event->any.send_event) + event->any.flags = CLUTTER_EVENT_FLAG_SYNTHETIC; + + _clutter_event_push (event, FALSE); + spin = 1; + + CLUTTER_NOTE (EVENT, "Translated one event from Gdk"); + + /* handle also synthetic enter/leave events */ + if (event->type == CLUTTER_MOTION) + spin += 2; + + while (spin > 0 && (event = clutter_event_get ())) + { + /* forward the event into clutter for emission etc. */ + clutter_do_event (event); + clutter_event_free (event); + --spin; + } + + result = GDK_FILTER_REMOVE; + } + + out: + + clutter_threads_leave (); + return result; +} diff --git a/clutter/gdk/clutter-gdk.h b/clutter/gdk/clutter-gdk.h new file mode 100644 index 000000000..6aa825bba --- /dev/null +++ b/clutter/gdk/clutter-gdk.h @@ -0,0 +1,55 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006 OpenedHand + * + * 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 . + * + * + */ + +/** + * SECTION:clutter-gdk + * @short_description: GDK specific API + * + * The GDK backend for Clutter provides some specific API, allowing + * integration with the GDK API for manipulating the stage window and + * handling events outside of Clutter. + */ + +#ifndef __CLUTTER_GDK_H__ +#define __CLUTTER_GDK_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +void clutter_gdk_set_display (GdkDisplay *display); +GdkWindow *clutter_gdk_get_stage_window (ClutterStage *stage); +gboolean clutter_gdk_set_stage_foreign (ClutterStage *stage, + GdkWindow *window); + +GdkFilterReturn clutter_gdk_handle_event (GdkEvent *event); + +ClutterStage *clutter_gdk_get_stage_from_window (GdkWindow *window); + +G_END_DECLS + +#endif /* __CLUTTER_GDK_H__ */ diff --git a/clutter/gdk/clutter-input-device-gdk.c b/clutter/gdk/clutter-input-device-gdk.c new file mode 100644 index 000000000..9d37dbc40 --- /dev/null +++ b/clutter/gdk/clutter-input-device-gdk.c @@ -0,0 +1,160 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2011 Intel Corp. + * 2011 Giovanni Campagna + * + * 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 . + * + * Author: Emmanuele Bassi + */ + +#include "config.h" + +#include "clutter-input-device-gdk.h" + +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-private.h" +#include "clutter-stage-private.h" + +#include "clutter-backend-gdk.h" +#include "clutter-stage-gdk.h" + +typedef struct _ClutterInputDeviceClass ClutterInputDeviceGdkClass; + +#define clutter_input_device_gdk_get_type _clutter_input_device_gdk_get_type + +G_DEFINE_TYPE (ClutterInputDeviceGdk, + clutter_input_device_gdk, + CLUTTER_TYPE_INPUT_DEVICE); + +static int device_int_counter; + +enum { + PROP_0, + PROP_GDK_DEVICE, + PROP_LAST +}; + +static void +clutter_input_device_gdk_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterInputDeviceGdk *self = CLUTTER_INPUT_DEVICE_GDK (gobject); + + switch (prop_id) + { + case PROP_GDK_DEVICE: + self->gdk_device = GDK_DEVICE (g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_input_device_gdk_class_init (ClutterInputDeviceGdkClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = clutter_input_device_gdk_set_property; + + g_object_class_install_property (gobject_class, PROP_GDK_DEVICE, + g_param_spec_object ("gdk-device", + "GdkDevice", + "The GDK device", + GDK_TYPE_DEVICE, + CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +clutter_input_device_gdk_init (ClutterInputDeviceGdk *self) +{ +} + +ClutterInputDevice* +_clutter_input_device_gdk_new (ClutterDeviceManager *manager, + GdkDevice *device) +{ + ClutterBackend *backend; + ClutterInputDevice *clutter_device; + ClutterInputMode input_mode = CLUTTER_INPUT_MODE_FLOATING; + ClutterInputDeviceType device_type = CLUTTER_EXTENSION_DEVICE; + gboolean has_cursor = FALSE; + const gchar *name; + gboolean is_enabled = FALSE; + + g_object_get (manager, "backend", &backend, NULL); + + /* yay for name consistency */ + switch (gdk_device_get_device_type (device)) + { + case GDK_DEVICE_TYPE_MASTER: + input_mode = CLUTTER_INPUT_MODE_MASTER; + is_enabled = TRUE; + break; + case GDK_DEVICE_TYPE_SLAVE: + input_mode = CLUTTER_INPUT_MODE_SLAVE; + is_enabled = FALSE; + break; + case GDK_DEVICE_TYPE_FLOATING: + input_mode = CLUTTER_INPUT_MODE_FLOATING; + is_enabled = FALSE; + break; + } + + switch (gdk_device_get_source (device)) + { + case GDK_SOURCE_MOUSE: + device_type = CLUTTER_POINTER_DEVICE; + break; + case GDK_SOURCE_PEN: + device_type = CLUTTER_PEN_DEVICE; + break; + case GDK_SOURCE_ERASER: + device_type = CLUTTER_ERASER_DEVICE; + break; + case GDK_SOURCE_CURSOR: + device_type = CLUTTER_CURSOR_DEVICE; + break; + case GDK_SOURCE_KEYBOARD: + device_type = CLUTTER_KEYBOARD_DEVICE; + break; + } + + if (device_type != CLUTTER_KEYBOARD_DEVICE) + /* why Gdk asserts if passed a GDK_SOURCE_KEYBOARD device? */ + has_cursor = gdk_device_get_has_cursor (device); + name = gdk_device_get_name (device); + + clutter_device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_GDK, + "backend", backend, + "device-manager", manager, + "device-mode", input_mode, + "device-type", device_type, + "has-cursor", has_cursor, + "gdk-device", device, + "id", device_int_counter++, + "name", name, + "enabled", is_enabled, + NULL); + return clutter_device; +} diff --git a/clutter/gdk/clutter-input-device-gdk.h b/clutter/gdk/clutter-input-device-gdk.h new file mode 100644 index 000000000..bcc57183a --- /dev/null +++ b/clutter/gdk/clutter-input-device-gdk.h @@ -0,0 +1,55 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright © 2011 Intel Corp. + * + * 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 . + * + * Author: Emmanuele Bassi + */ + +#ifndef __CLUTTER_INPUT_DEVICE_GDK_H__ +#define __CLUTTER_INPUT_DEVICE_GDK_H__ + +#include +#include + +#include "clutter-backend.h" +#include "clutter-device-manager-private.h" + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_INPUT_DEVICE_GDK (_clutter_input_device_gdk_get_type ()) +#define CLUTTER_INPUT_DEVICE_GDK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_GDK, ClutterInputDeviceGdk)) +#define CLUTTER_IS_INPUT_DEVICE_GDK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_GDK)) + +typedef struct _ClutterInputDeviceGdk ClutterInputDeviceGdk; + +struct _ClutterInputDeviceGdk +{ + ClutterInputDevice parent; + + GdkDevice *gdk_device; +}; + +GType _clutter_input_device_gdk_get_type (void) G_GNUC_CONST; + +ClutterInputDevice * _clutter_input_device_gdk_new (ClutterDeviceManager *manager, + GdkDevice *device); + +G_END_DECLS + +#endif /* __CLUTTER_INPUT_DEVICE_GDK_H__ */ diff --git a/clutter/gdk/clutter-settings-gdk.h b/clutter/gdk/clutter-settings-gdk.h new file mode 100644 index 000000000..c2b576e01 --- /dev/null +++ b/clutter/gdk/clutter-settings-gdk.h @@ -0,0 +1,28 @@ +#ifndef __CLUTTER_SETTINGS_GDK_H__ +#define __CLUTTER_SETTINGS_GDK_H__ + +/* XSETTINGS key names to ClutterSettings properties */ +static const struct { + const char *gdk_setting_name; + const char *settings_property; + GType type; +} _clutter_settings_map[] = { + { "gtk-double-click-time", "double-click-time", G_TYPE_INT }, + { "gtk-double-click-distance", "double-click-distance", G_TYPE_INT }, + { "gtk-dnd-drag-threshold", "dnd-drag-threshold", G_TYPE_INT }, + { "gtk-font-name", "font-name", G_TYPE_STRING }, + { "gtk-xft-antialias", "font-antialias", G_TYPE_INT }, + { "gtk-xft-dpi", "font-dpi", G_TYPE_INT }, + { "gtk-xft-hinting", "font-hinting", G_TYPE_INT }, + { "gtk-xft-hintstyle", "font-hint-style", G_TYPE_STRING }, + { "gtk-xft-rgba", "font-subpixel-order", G_TYPE_STRING }, + { "gtk-fontconfig-timestamp", "fontconfig-timestamp", G_TYPE_UINT }, +}; + +static const gint _n_clutter_settings_map = G_N_ELEMENTS (_clutter_settings_map); + +#define CLUTTER_SETTING_TYPE(id) (_clutter_settings_map[(id)].type) +#define CLUTTER_SETTING_GDK_NAME(id) (_clutter_settings_map[(id)].gdk_setting_name) +#define CLUTTER_SETTING_PROPERTY(id) (_clutter_settings_map[(id)].settings_property) + +#endif /* __CLUTTER_SETTINGS_GDK_H__ */ diff --git a/clutter/gdk/clutter-stage-gdk.c b/clutter/gdk/clutter-stage-gdk.c new file mode 100644 index 000000000..600da646e --- /dev/null +++ b/clutter/gdk/clutter-stage-gdk.c @@ -0,0 +1,601 @@ +/* Clutter. + * An OpenGL based 'interactive canvas' library. + * Authored By Matthew Allum + * Copyright (C) 2006-2007 OpenedHand + * + * 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 . + * + * + */ + +#include "config.h" + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "clutter-backend-gdk.h" +#include "clutter-stage-gdk.h" +#include "clutter-gdk.h" + +#include "clutter-actor-private.h" +#include "clutter-debug.h" +#include "clutter-device-manager-private.h" +#include "clutter-enum-types.h" +#include "clutter-event-translator.h" +#include "clutter-event-private.h" +#include "clutter-feature.h" +#include "clutter-main.h" +#include "clutter-paint-volume-private.h" +#include "clutter-private.h" +#include "clutter-stage-private.h" + +#include "cogl/cogl.h" + +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); + +#define clutter_stage_gdk_get_type _clutter_stage_gdk_get_type + +G_DEFINE_TYPE_WITH_CODE (ClutterStageGdk, + clutter_stage_gdk, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, + clutter_stage_window_iface_init)); + +enum { + PROP_0, + PROP_WRAPPER, + PROP_BACKEND, + PROP_LAST +}; + +void +_clutter_stage_gdk_update_foreign_event_mask (CoglOnscreen *onscreen, + guint32 event_mask, + void *user_data) +{ + ClutterStageGdk *stage_gdk = user_data; + + /* we assume that a GDK event mask is bitwise compatible with X11 + event masks */ + gdk_window_set_events (stage_gdk->window, event_mask | CLUTTER_STAGE_GDK_EVENT_MASK); +} + + +static void +clutter_stage_gdk_set_gdk_geometry (ClutterStageGdk *stage) +{ + GdkGeometry geometry; + gboolean resize = clutter_stage_get_user_resizable (stage->wrapper); + + if (!resize) + { + geometry.min_width = geometry.max_width = gdk_window_get_width (stage->window); + geometry.min_height = geometry.max_height = gdk_window_get_height (stage->window); + + gdk_window_set_geometry_hints (stage->window, + &geometry, + GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); + } + else + { + clutter_stage_get_minimum_size (stage->wrapper, + (guint *)&geometry.min_width, + (guint *)&geometry.min_height); + + gdk_window_set_geometry_hints (stage->window, + &geometry, + GDK_HINT_MIN_SIZE); + } +} + +static void +clutter_stage_gdk_get_geometry (ClutterStageWindow *stage_window, + ClutterGeometry *geometry) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + if (stage_gdk->window) + { + geometry->width = gdk_window_get_width (stage_gdk->window); + geometry->height = gdk_window_get_height (stage_gdk->window); + } + else + { + geometry->width = 640; + geometry->height = 480; + } +} + +static void +clutter_stage_gdk_resize (ClutterStageWindow *stage_window, + gint width, + gint height) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + if (width == 0 || height == 0) + { + /* Should not happen, if this turns up we need to debug it and + * determine the cleanest way to fix. + */ + g_warning ("GDK stage not allowed to have 0 width or height"); + width = 1; + height = 1; + } + + CLUTTER_NOTE (BACKEND, "New size received: (%d, %d)", width, height); + + CLUTTER_SET_PRIVATE_FLAGS (stage_gdk->wrapper, + CLUTTER_IN_RESIZE); + + gdk_window_resize (stage_gdk->window, width, height); +} + +static void +clutter_stage_gdk_unrealize (ClutterStageWindow *stage_window) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + if (stage_gdk->window) + { + g_object_set_data (G_OBJECT (stage_gdk->window), + "clutter-stage-window", NULL); + if (stage_gdk->foreign_window) + g_object_unref (stage_gdk->window); + else + gdk_window_destroy (stage_gdk->window); + stage_gdk->window = NULL; + } +} + +static gboolean +clutter_stage_gdk_realize (ClutterStageWindow *stage_window) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + ClutterBackendGdk *backend_gdk = stage_gdk->backend; + GdkWindowAttr attributes; + gboolean cursor_visible; + gboolean use_alpha; + gfloat width, height; + + if (stage_gdk->foreign_window && + stage_gdk->window) + { + /* complete realizing the stage */ + ClutterGeometry geometry; + + clutter_stage_gdk_get_geometry (stage_window, &geometry); + clutter_actor_set_geometry (CLUTTER_ACTOR (stage_gdk->wrapper), &geometry); + + gdk_window_ensure_native (stage_gdk->window); + gdk_window_set_events (stage_gdk->window, + CLUTTER_STAGE_GDK_EVENT_MASK); + + return TRUE; + } + + attributes.title = NULL; + g_object_get (stage_gdk->wrapper, + "cursor-visible", &cursor_visible, + "title", &attributes.title, + "width", &width, + "height", &height, + "use-alpha", &use_alpha, + NULL); + + attributes.width = width; + attributes.height = height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.window_type = GDK_WINDOW_TOPLEVEL; + attributes.event_mask = CLUTTER_STAGE_GDK_EVENT_MASK; + + attributes.cursor = NULL; + if (!cursor_visible) + { + if (stage_gdk->blank_cursor == NULL) + stage_gdk->blank_cursor = gdk_cursor_new (GDK_BLANK_CURSOR); + + attributes.cursor = stage_gdk->blank_cursor; + } + + if (use_alpha) + { + attributes.visual = gdk_screen_get_rgba_visual (backend_gdk->screen); + + if (attributes.visual == NULL) + clutter_stage_set_use_alpha (stage_gdk->wrapper, FALSE); + } + else + { + /* This could still be an RGBA visual, although normally it's not */ + attributes.visual = gdk_screen_get_system_visual (backend_gdk->screen); + } + + if (stage_gdk->window != NULL) + { + g_critical ("Stage realized more than once"); + g_object_set_data (G_OBJECT (stage_gdk->window), + "clutter-stage-window", NULL); + if (stage_gdk->foreign_window) + g_object_unref (stage_gdk->window); + else + gdk_window_destroy (stage_gdk->window); + } + + stage_gdk->foreign_window = FALSE; + stage_gdk->window = gdk_window_new (NULL, &attributes, + GDK_WA_TITLE | GDK_WA_CURSOR | GDK_WA_VISUAL); + gdk_window_ensure_native (stage_gdk->window); + + clutter_stage_gdk_set_gdk_geometry (stage_gdk); + + g_object_set_data (G_OBJECT (stage_gdk->window), + "clutter-stage-window", stage_gdk); + + g_free (attributes.title); + + CLUTTER_NOTE (BACKEND, "Successfully realized stage"); + + return TRUE; +} + +static void +clutter_stage_gdk_set_fullscreen (ClutterStageWindow *stage_window, + gboolean is_fullscreen) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + ClutterStage *stage = stage_gdk->wrapper; + + if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage)) + return; + + if (stage_gdk->window == NULL) + return; + + CLUTTER_NOTE (BACKEND, "%ssetting fullscreen", is_fullscreen ? "" : "un"); + + if (is_fullscreen) + gdk_window_fullscreen (stage_gdk->window); + else + gdk_window_unfullscreen (stage_gdk->window); +} + +static void +clutter_stage_gdk_set_cursor_visible (ClutterStageWindow *stage_window, + gboolean cursor_visible) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + if (stage_gdk->window == NULL) + return; + + if (cursor_visible) + { + gdk_window_set_cursor (stage_gdk->window, NULL); + } + else + { + if (stage_gdk->blank_cursor == NULL) + stage_gdk->blank_cursor = gdk_cursor_new (GDK_BLANK_CURSOR); + + gdk_window_set_cursor (stage_gdk->window, stage_gdk->blank_cursor); + } +} + +static void +clutter_stage_gdk_set_title (ClutterStageWindow *stage_window, + const gchar *title) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + if (stage_gdk->window == NULL) + return; + + gdk_window_set_title (stage_gdk->window, title); +} + +static void +clutter_stage_gdk_set_user_resizable (ClutterStageWindow *stage_window, + gboolean is_resizable) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + GdkWMFunction function; + + if (stage_gdk->window == NULL) + return; + + function = GDK_FUNC_MOVE | GDK_FUNC_MINIMIZE | GDK_FUNC_CLOSE; + if (is_resizable) + function |= GDK_FUNC_RESIZE | GDK_FUNC_MAXIMIZE; + + gdk_window_set_functions (stage_gdk->window, function); + + clutter_stage_gdk_set_gdk_geometry (stage_gdk); +} + +static void +clutter_stage_gdk_set_accept_focus (ClutterStageWindow *stage_window, + gboolean accept_focus) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + if (stage_gdk->window == NULL) + return; + + gdk_window_set_accept_focus (stage_gdk->window, accept_focus); +} + +static void +clutter_stage_gdk_show (ClutterStageWindow *stage_window, + gboolean do_raise) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + g_return_if_fail (stage_gdk->window != NULL); + + clutter_actor_map (CLUTTER_ACTOR (stage_gdk->wrapper)); + + if (do_raise) + gdk_window_show (stage_gdk->window); + else + gdk_window_show_unraised (stage_gdk->window); +} + +static void +clutter_stage_gdk_hide (ClutterStageWindow *stage_window) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (stage_window); + + g_return_if_fail (stage_gdk->window != NULL); + + clutter_actor_unmap (CLUTTER_ACTOR (stage_gdk->wrapper)); + gdk_window_hide (stage_gdk->window); +} + +static ClutterActor * +clutter_stage_gdk_get_wrapper (ClutterStageWindow *stage_window) +{ + return CLUTTER_ACTOR (CLUTTER_STAGE_GDK (stage_window)->wrapper); +} + +static void +clutter_stage_gdk_dispose (GObject *gobject) +{ + ClutterStageGdk *stage_gdk = CLUTTER_STAGE_GDK (gobject); + + if (stage_gdk->window) + { + g_object_set_data (G_OBJECT (stage_gdk->window), + "clutter-stage-window", NULL); + if (stage_gdk->foreign_window) + g_object_unref (stage_gdk->window); + else + gdk_window_destroy (stage_gdk->window); + stage_gdk->window = NULL; + } + + if (stage_gdk->blank_cursor) + { + g_object_unref (stage_gdk->blank_cursor); + stage_gdk->blank_cursor = NULL; + } + + G_OBJECT_CLASS (clutter_stage_gdk_parent_class)->dispose (gobject); +} + +static void +clutter_stage_gdk_set_property (GObject *gobject, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + ClutterStageGdk *self = CLUTTER_STAGE_GDK (gobject); + + switch (prop_id) + { + case PROP_WRAPPER: + self->wrapper = CLUTTER_STAGE (g_value_get_object (value)); + break; + + case PROP_BACKEND: + self->backend = CLUTTER_BACKEND_GDK (g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + break; + } +} + +static void +clutter_stage_gdk_class_init (ClutterStageGdkClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->dispose = clutter_stage_gdk_dispose; + gobject_class->set_property = clutter_stage_gdk_set_property; + + g_object_class_install_property (gobject_class, PROP_WRAPPER, + g_param_spec_object ("wrapper", + "Wrapper", + "ClutterStage wrapping this native stage", + CLUTTER_TYPE_STAGE, + CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (gobject_class, PROP_BACKEND, + g_param_spec_object ("backend", + "ClutterBackend", + "The Clutter backend singleton", + CLUTTER_TYPE_BACKEND_GDK, + CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +clutter_stage_gdk_init (ClutterStageGdk *stage) +{ + /* nothing to do here */ +} + +static void +clutter_stage_window_iface_init (ClutterStageWindowIface *iface) +{ + iface->get_wrapper = clutter_stage_gdk_get_wrapper; + iface->set_title = clutter_stage_gdk_set_title; + iface->set_fullscreen = clutter_stage_gdk_set_fullscreen; + iface->set_cursor_visible = clutter_stage_gdk_set_cursor_visible; + iface->set_user_resizable = clutter_stage_gdk_set_user_resizable; + iface->set_accept_focus = clutter_stage_gdk_set_accept_focus; + iface->show = clutter_stage_gdk_show; + iface->hide = clutter_stage_gdk_hide; + iface->resize = clutter_stage_gdk_resize; + iface->get_geometry = clutter_stage_gdk_get_geometry; + iface->realize = clutter_stage_gdk_realize; + iface->unrealize = clutter_stage_gdk_unrealize; +} + +/** + * clutter_gdk_get_stage_window: + * @stage: a #ClutterStage + * + * Gets the stages GdkWindow. + * + * Return value: (transfer none): A GdkWindow* for the stage window. + * + * Since: 0.4 + */ +GdkWindow* +clutter_gdk_get_stage_window (ClutterStage *stage) +{ + ClutterStageWindow *impl; + + g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None); + + impl = _clutter_stage_get_window (stage); + g_assert (CLUTTER_IS_STAGE_GDK (impl)); + + return CLUTTER_STAGE_GDK (impl)->window; +} + +/** + * clutter_gdk_get_stage_from_window: + * @window: a #GtkWindow + * + * Gets the stage for a particular X window. + * + * Return value: (transfer none): A #ClutterStage, or% NULL if a stage + * does not exist for the window + * + * Since: 0.8 + */ +ClutterStage * +clutter_gdk_get_stage_from_window (GdkWindow *window) +{ + ClutterStageGdk *stage_gdk = g_object_get_data (G_OBJECT (window), "clutter-stage-window"); + + if (stage_gdk != NULL && CLUTTER_IS_STAGE_GDK (stage_gdk)) + return stage_gdk->wrapper; + + return NULL; +} + +typedef struct +{ + ClutterStageGdk *stage_gdk; + GdkWindow *window; +} ForeignWindowClosure; + +static void +set_foreign_window_callback (ClutterActor *actor, + void *data) +{ + ForeignWindowClosure *closure = data; + ClutterStageGdk *stage_gdk = closure->stage_gdk; + + stage_gdk->window = closure->window; + stage_gdk->foreign_window = TRUE; + + /* calling this with the stage unrealized will unset the stage + * from the GL context; once the stage is realized the GL context + * will be set again + */ + clutter_stage_ensure_current (CLUTTER_STAGE (actor)); +} + +/** + * clutter_gdk_set_stage_foreign: + * @stage: a #ClutterStage + * @window: an existing #GdkWindow + * + * Target the #ClutterStage to use an existing external #GdkWindow + * + * Return value: %TRUE if foreign window is valid + * + * Since: 0.4 + */ +gboolean +clutter_gdk_set_stage_foreign (ClutterStage *stage, + GdkWindow *window) +{ + ForeignWindowClosure closure; + ClutterStageGdk *stage_gdk; + ClutterStageWindow *impl; + ClutterActor *actor; + gpointer gtk_data = NULL; + + g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); + g_return_val_if_fail (!CLUTTER_ACTOR_IN_DESTRUCTION (stage), FALSE); + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + + impl = _clutter_stage_get_window (stage); + stage_gdk = CLUTTER_STAGE_GDK (impl); + + if (g_object_get_data (G_OBJECT (window), "clutter-stage-window") != NULL) + { + g_critical ("The provided GdkWindow is already in use by another ClutterStage"); + return FALSE; + } + + gdk_window_get_user_data (window, >k_data); + if (gtk_data != NULL) + { + g_critical ("The provided GdkWindow is already in use by a GtkWidget. " + "Use a child GdkWindow for embedding instead"); + return FALSE; + } + + closure.stage_gdk = stage_gdk; + closure.window = g_object_ref (window); + + actor = CLUTTER_ACTOR (stage); + + _clutter_actor_rerealize (actor, + set_foreign_window_callback, + &closure); + + /* Queue a relayout - so the stage will be allocated the new + * window size. + * + * Note also that when the stage gets allocated the new + * window size that will result in the stage's + * priv->viewport being changed, which will in turn result + * in the Cogl viewport changing when _clutter_do_redraw + * calls _clutter_stage_maybe_setup_viewport(). + */ + clutter_actor_queue_relayout (actor); + + return TRUE; +} diff --git a/clutter/gdk/clutter-stage-gdk.h b/clutter/gdk/clutter-stage-gdk.h new file mode 100644 index 000000000..38cf39980 --- /dev/null +++ b/clutter/gdk/clutter-stage-gdk.h @@ -0,0 +1,84 @@ +/* Clutter. + * An OpenGL based 'interactive canvas' library. + * Authored By Matthew Allum + * Copyright (C) 2006-2007 OpenedHand + * 2011 Giovanni Campagna + * + * 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 . + * + * + */ + +#ifndef __CLUTTER_STAGE_GDK_H__ +#define __CLUTTER_STAGE_GDK_H__ + +#include +#include +#include + +#include "clutter-backend-gdk.h" + +G_BEGIN_DECLS + +#define CLUTTER_TYPE_STAGE_GDK (_clutter_stage_gdk_get_type ()) +#define CLUTTER_STAGE_GDK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_GDK, ClutterStageGdk)) +#define CLUTTER_IS_STAGE_GDK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_GDK)) +#define CLUTTER_STAGE_GDK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_GDK, ClutterStageGdkClass)) +#define CLUTTER_IS_STAGE_GDK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_GDK)) +#define CLUTTER_STAGE_GDK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_GDK, ClutterStageGdkClass)) + +typedef struct _ClutterStageGdk ClutterStageGdk; +typedef struct _ClutterStageGdkClass ClutterStageGdkClass; + +struct _ClutterStageGdk +{ + GObject parent_instance; + + GdkWindow *window; + GdkCursor *blank_cursor; + + /* backpointers */ + ClutterStage *wrapper; + ClutterBackendGdk *backend; + + gboolean foreign_window; +}; + +struct _ClutterStageGdkClass +{ + GObjectClass parent_class; +}; + +#define CLUTTER_STAGE_GDK_EVENT_MASK \ + (GDK_STRUCTURE_MASK | \ + GDK_FOCUS_CHANGE_MASK | \ + GDK_EXPOSURE_MASK | \ + GDK_PROPERTY_CHANGE_MASK | \ + GDK_ENTER_NOTIFY_MASK | \ + GDK_LEAVE_NOTIFY_MASK | \ + GDK_KEY_PRESS_MASK | \ + GDK_KEY_RELEASE_MASK | \ + GDK_BUTTON_PRESS_MASK | \ + GDK_BUTTON_RELEASE_MASK | \ + GDK_POINTER_MOTION_MASK) + +GType _clutter_stage_gdk_get_type (void) G_GNUC_CONST; + +void _clutter_stage_gdk_update_foreign_event_mask (CoglOnscreen *onscreen, + guint32 event_mask, + void *user_data); + +G_END_DECLS + +#endif /* __CLUTTER_STAGE_H__ */ diff --git a/configure.ac b/configure.ac index 966626bbd..bf12228a8 100644 --- a/configure.ac +++ b/configure.ac @@ -226,6 +226,23 @@ AS_CASE([$CLUTTER_FLAVOUR], []) ], + [gdk], + [ + CLUTTER_STAGE_TYPE="CLUTTER_TYPE_STAGE_COGL" + + # We don't claim to support X11 (even though that's the preferred + # GDK backend), to avoid building all the ClutterX11 stuff + SUPPORT_GDK=1 + + CLUTTER_WINSYS=cogl + CLUTTER_WINSYS_BASE=gdk + CLUTTER_WINSYS_BASE_LIB="gdk/libclutter-gdk.la" + CLUTTER_SONAME_INFIX=gdk + + BACKEND_PC_FILES="$BACKEND_PC_FILES gdk-3.0" + PKG_CHECK_EXISTS([gl], [BACKEND_PC_FILES="$BACKEND_PC_FILES gl"], []) + ], + [opengl-egl-xlib], [ CLUTTER_STAGE_TYPE="CLUTTER_TYPE_STAGE_EGL" @@ -419,6 +436,7 @@ AS_IF([test "x$CLUTTER_EGL_BACKEND" = "xcex100"], AM_CONDITIONAL(SUPPORT_GLX, [test "x$SUPPORT_GLX" = "x1"]) AM_CONDITIONAL(SUPPORT_X11, [test "x$SUPPORT_X11" = "x1"]) AM_CONDITIONAL(SUPPORT_XLIB, [test "x$SUPPORT_XLIB" = "x1"]) +AM_CONDITIONAL(SUPPORT_GDK, [test "x$SUPPORT_GDK" = "x1"]) AM_CONDITIONAL(SUPPORT_EGL, [test "x$SUPPORT_EGL" = "x1"]) AM_CONDITIONAL(SUPPORT_OSX, [test "x$CLUTTER_WINSYS" = "xosx"]) AM_CONDITIONAL(SUPPORT_WIN32, [test "x$CLUTTER_WINSYS" = "xwin32"]) @@ -446,6 +464,9 @@ CLUTTER_CONFIG_DEFINES= AS_IF([test "x$SUPPORT_XLIB" = "x1"], [CLUTTER_CONFIG_DEFINES="$CLUTTER_CONFIG_DEFINES #define CLUTTER_WINDOWING_X11 1"]) +AS_IF([test "x$SUPPORT_GDK" = "x1"], + [CLUTTER_CONFIG_DEFINES="$CLUTTER_CONFIG_DEFINES +#define CLUTTER_WINDOWING_GDK 1"]) AS_IF([test "x$SUPPORT_GLX" = "x1"], [CLUTTER_CONFIG_DEFINES="$CLUTTER_CONFIG_DEFINES #define CLUTTER_WINDOWING_GLX 1"]) diff --git a/tests/conform/test-cogl-texture-pixmap-x11.c b/tests/conform/test-cogl-texture-pixmap-x11.c index abdef1298..36f86d458 100644 --- a/tests/conform/test-cogl-texture-pixmap-x11.c +++ b/tests/conform/test-cogl-texture-pixmap-x11.c @@ -4,7 +4,7 @@ static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; -#ifdef COGL_HAS_XLIB +#ifdef CLUTTER_WINDOWING_X11 #include #include @@ -195,13 +195,13 @@ queue_redraw (gpointer stage) return TRUE; } -#endif /* COGL_HAS_XLIB */ +#endif /* CLUTTER_WINDOWING_X11 */ void test_cogl_texture_pixmap_x11 (TestConformSimpleFixture *fixture, gconstpointer data) { -#ifdef COGL_HAS_XLIB +#ifdef CLUTTER_WINDOWING_X11 TestState state; guint idle_handler; @@ -235,11 +235,11 @@ test_cogl_texture_pixmap_x11 (TestConformSimpleFixture *fixture, if (g_test_verbose ()) g_print ("OK\n"); -#else /* COGL_HAS_XLIB */ +#else /* CLUTTER_WINDOWING_X11 */ if (g_test_verbose ()) g_print ("Skipping\n"); -#endif /* COGL_HAS_XLIB */ +#endif /* CLUTTER_WINDOWING_X11 */ } diff --git a/tests/conform/test-conform-common.c b/tests/conform/test-conform-common.c index 484c010bb..e7cd42bc4 100644 --- a/tests/conform/test-conform-common.c +++ b/tests/conform/test-conform-common.c @@ -3,7 +3,7 @@ #include #include -#ifdef COGL_HAS_XLIB +#ifdef CLUTTER_WINDOWING_X11 #include #include #endif @@ -48,7 +48,7 @@ test_conform_simple_fixture_setup (TestConformSimpleFixture *fixture, g_assert (clutter_init (shared_state->argc_addr, shared_state->argv_addr) == CLUTTER_INIT_SUCCESS); -#ifdef COGL_HAS_XLIB +#ifdef CLUTTER_WINDOWING_X11 /* A lot of the tests depend on a specific stage / framebuffer size * when they read pixels back to verify the results of the test. *