2007-10-26 Matthew Allum <mallum@openedhand.com>
authorMatthew Allum <mallum@openedhand.com>
Fri, 26 Oct 2007 22:06:45 +0000 (22:06 +0000)
committerMatthew Allum <mallum@openedhand.com>
Fri, 26 Oct 2007 22:06:45 +0000 (22:06 +0000)
        * clutter/clutter-texture.c:
        * clutter/cogl/cogl.h:
        * clutter/cogl/gl/cogl-defines.h:
        * clutter/cogl/gles/cogl-defines.h:
        * clutter/cogl/gl/cogl.c:
        * clutter/cogl/gles/cogl.c:
        Switch from use of guint to COGLuint. Avoids problems when
        guint != GLuint on some platforms, i.e OSX.
        (Tommi Komulainen, #526)

        * clutter/Makefile.am:
        * clutter/osx/Makefile.am:
        * clutter/osx/clutter-backend-osx.c:
        * clutter/osx/clutter-backend-osx.h:
        * clutter/osx/clutter-event-osx.c:
        * clutter/osx/clutter-osx.h:
        * clutter/osx/clutter-stage-osx.c:
        * clutter/osx/clutter-stage-osx.h:
        * configure.ac:
        Add initial Cocoa/OSX Backend (by Tommi Komulainen, see #526)

16 files changed:
ChangeLog
clutter/Makefile.am
clutter/clutter-texture.c
clutter/cogl/cogl.h
clutter/cogl/gl/cogl-defines.h
clutter/cogl/gl/cogl.c
clutter/cogl/gles/cogl-defines.h
clutter/cogl/gles/cogl.c
clutter/osx/Makefile.am [new file with mode: 0644]
clutter/osx/clutter-backend-osx.c [new file with mode: 0644]
clutter/osx/clutter-backend-osx.h [new file with mode: 0644]
clutter/osx/clutter-event-osx.c [new file with mode: 0644]
clutter/osx/clutter-osx.h [new file with mode: 0644]
clutter/osx/clutter-stage-osx.c [new file with mode: 0644]
clutter/osx/clutter-stage-osx.h [new file with mode: 0644]
configure.ac

index dfd64c6..ab5d3b1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2007-10-26  Matthew Allum  <mallum@openedhand.com>
+
+       * clutter/clutter-texture.c:
+       * clutter/cogl/cogl.h:
+       * clutter/cogl/gl/cogl-defines.h:
+       * clutter/cogl/gles/cogl-defines.h:
+       * clutter/cogl/gl/cogl.c:
+       * clutter/cogl/gles/cogl.c:
+       Switch from use of guint to COGLuint. Avoids problems when 
+        guint != GLuint on some platforms, i.e OSX.
+       (Tommi Komulainen, #526)
+
+       * clutter/Makefile.am:
+       * clutter/osx/Makefile.am:
+       * clutter/osx/clutter-backend-osx.c:
+       * clutter/osx/clutter-backend-osx.h:
+       * clutter/osx/clutter-event-osx.c:
+       * clutter/osx/clutter-osx.h:
+       * clutter/osx/clutter-stage-osx.c:
+       * clutter/osx/clutter-stage-osx.h:
+       * configure.ac:
+       Add initial Cocoa/OSX Backend (by Tommi Komulainen, see #526)
+
 2007-10-26  Emmanuele Bassi  <ebassi@openedhand.com>
 
        * clutter/clutter-entry.c: Add a :x-align property for aligning
index f98ad7d..81f3fa5 100644 (file)
@@ -2,7 +2,7 @@ NULL =
 
 SUBDIRS = cogl pango json $(clutterbackend) 
 
-DIST_SUBDIRS = pango glx eglx eglnative cogl sdl json
+DIST_SUBDIRS = pango glx eglx eglnative cogl sdl json osx
 
 target = $(clutterbackend)
 
index 2b24187..5eac3b9 100644 (file)
@@ -90,7 +90,7 @@ struct _ClutterTexturePrivate
   ClutterTextureTileDimension *y_tiles;
   gint                         n_x_tiles;
   gint                         n_y_tiles;
-  guint                       *tiles;
+  COGLuint                    *tiles;
 };
 
 enum
@@ -440,7 +440,7 @@ texture_upload_data (ClutterTexture *texture,
       /* Single Texture */
       if (!priv->tiles)
        {
-         priv->tiles = g_new (guint, 1);
+         priv->tiles = g_new (COGLuint, 1);
          glGenTextures (1, priv->tiles);
          create_textures = TRUE;
        }
@@ -511,7 +511,7 @@ texture_upload_data (ClutterTexture *texture,
 
   if (priv->tiles == NULL)
     {
-      priv->tiles = g_new (guint, priv->n_x_tiles * priv->n_y_tiles);
+      priv->tiles = g_new (COGLuint, priv->n_x_tiles * priv->n_y_tiles);
       glGenTextures (priv->n_x_tiles * priv->n_y_tiles, priv->tiles);
       create_textures = TRUE;
     }
@@ -1423,7 +1423,7 @@ clutter_texture_set_from_yuv_data   (ClutterTexture     *texture,
 
   if (!priv->tiles)
     {
-      priv->tiles = g_new (guint, 1);
+      priv->tiles = g_new (COGLuint, 1);
       glGenTextures (1, priv->tiles);
     }
 
index 66d55fe..80e512e 100644 (file)
 #define __COGL_H__
 
 #include <glib.h>
-#include <clutter/clutter-color.h>
-#include <clutter/clutter-feature.h>
-#include <clutter/clutter-fixed.h>
-#include <clutter/clutter-types.h>
+#include <clutter/clutter.h>
 
 #include "cogl-defines.h"
 
@@ -133,13 +130,13 @@ cogl_texture_quad (gint   x1,
                   ClutterFixed ty2);
 
 void
-cogl_textures_create (guint num, guint *textures);
+cogl_textures_create (guint num, COGLuint *textures);
 
 void
-cogl_textures_destroy (guint num, const guint *textures);
+cogl_textures_destroy (guint num, const COGLuint *textures);
 
 void
-cogl_texture_bind (COGLenum target, guint texture);
+cogl_texture_bind (COGLenum target, COGLuint texture);
 
 void
 cogl_texture_set_alignment (COGLenum target, 
index e5e8ba4..47fde3d 100644 (file)
 #define __COGL_DEFINES_H__
 
 #ifdef WIN32
+
 #include <windows.h>
 #include <GL/Glee.h>
+
 #else
+
+#if defined(HAVE_GL_GL_H)
 #include <GL/gl.h>
+#elif defined(HAVE_OPENGL_GL_H)
+#include <OpenGL/gl.h>
 #endif
 
+#endif /* WIN32 */
+
 G_BEGIN_DECLS
 
 typedef GLenum COGLenum;
 typedef GLint COGLint;
+typedef GLuint COGLuint;
 
 /* FIXME + DOCUMENT */
 
index 6d2876a..c209234 100644 (file)
 
 #include "cogl.h"
 
-#ifdef WIN32
-#include <windows.h>
-#include <GL/Glee.h>
-#else
-#include <GL/gl.h>
-#endif
 #include <string.h>
 
 #ifdef HAVE_CLUTTER_GLX
@@ -333,7 +327,7 @@ cogl_texture_can_size (COGLenum       target,
 #ifdef GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB
   if (target == CGL_TEXTURE_RECTANGLE_ARB)
     {
-      gint max_size = 0;
+      GLint max_size = 0;
 
       GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) );
 
@@ -381,19 +375,19 @@ cogl_texture_quad (gint   x1,
 }
 
 void
-cogl_textures_create (guint num, guint *textures)
+cogl_textures_create (guint num, COGLuint *textures)
 {
   GE( glGenTextures (num, textures) );
 }
 
 void
-cogl_textures_destroy (guint num, const guint *textures)
+cogl_textures_destroy (guint num, const COGLuint *textures)
 {
   GE( glDeleteTextures (num, textures) );
 }
 
 void
-cogl_texture_bind (COGLenum target, guint texture)
+cogl_texture_bind (COGLenum target, COGLuint texture)
 {
   GE( glBindTexture (target, texture) );
 }
@@ -696,12 +690,25 @@ cogl_get_viewport (ClutterFixed v[4])
 void
 cogl_get_bitmasks (gint *red, gint *green, gint *blue, gint *alpha)
 {
+  GLint value;
   if (red)
-    GE( glGetIntegerv(GL_RED_BITS, red) );
+    {
+      GE( glGetIntegerv(GL_RED_BITS, &value) );
+      *red = value;
+    }
   if (green)
-    GE( glGetIntegerv(GL_GREEN_BITS, green) );
+    {
+      GE( glGetIntegerv(GL_GREEN_BITS, &value) );
+      *green = value;
+    }
   if (blue)
-    GE( glGetIntegerv(GL_BLUE_BITS, blue) );
+    {
+      GE( glGetIntegerv(GL_BLUE_BITS, &value) );
+      *blue = value;
+    }
   if (alpha)
-    GE( glGetIntegerv(GL_ALPHA_BITS, alpha ) );
+    {
+      GE( glGetIntegerv(GL_ALPHA_BITS, &value ) );
+      *alpha = value;
+    }
 }
index 37a9ab0..41eac15 100644 (file)
@@ -439,6 +439,7 @@ G_BEGIN_DECLS
 
 typedef GLenum COGLenum;
 typedef GLint  COGLint;
+typedef GLuint COGLuint;
 
 /* extras */
 
index 8a64d4c..e6e6880 100644 (file)
@@ -323,19 +323,19 @@ cogl_texture_quad (gint   x1,
 }
 
 void
-cogl_textures_create (guint num, guint *textures)
+cogl_textures_create (guint num, COGLuint *textures)
 {
   GE( glGenTextures (num, textures) );
 }
 
 void
-cogl_textures_destroy (guint num, const guint *textures)
+cogl_textures_destroy (guint num, const COGLuint *textures)
 {
   GE( glDeleteTextures (num, textures) );
 }
 
 void
-cogl_texture_bind (COGLenum target, guint texture)
+cogl_texture_bind (COGLenum target, COGLuint texture)
 {
   GE( glBindTexture (target, texture) );
 }
diff --git a/clutter/osx/Makefile.am b/clutter/osx/Makefile.am
new file mode 100644 (file)
index 0000000..c0714db
--- /dev/null
@@ -0,0 +1,24 @@
+libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter
+libclutterinclude_HEADERS = clutter-osx.h
+
+INCLUDES = \
+       -DG_LOG_DOMAIN=\"ClutterOSX\" \
+       -I$(top_srcdir) \
+       -I$(top_srcdir)/clutter/cogl            \
+       -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \
+       $(CLUTTER_CFLAGS) \
+       $(CLUTTER_DEBUG_CFLAGS) \
+       $(GCC_FLAGS) \
+       -xobjective-c
+
+LDADD = $(CLUTTER_LIBS)
+
+noinst_LTLIBRARIES = libclutter-osx.la
+
+libclutter_osx_la_SOURCES = \
+       clutter-backend-osx.h \
+       clutter-backend-osx.c \
+       clutter-event-osx.c \
+       clutter-stage-osx.h \
+       clutter-stage-osx.c \
+       clutter-osx.h
diff --git a/clutter/osx/clutter-backend-osx.c b/clutter/osx/clutter-backend-osx.c
new file mode 100644 (file)
index 0000000..c3919e6
--- /dev/null
@@ -0,0 +1,182 @@
+/* Clutter -  An OpenGL based 'interactive canvas' library.
+ * OSX backend - initial entry point
+ *
+ * Copyright (C) 2007  Tommi Komulainen <tommi.komulainen@iki.fi>
+ * Copyright (C) 2007  OpenedHand 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "config.h"
+
+#include "clutter-osx.h"
+#include "clutter-backend-osx.h"
+#include "clutter-stage-osx.h"
+
+#include <clutter/clutter-debug.h>
+#import <AppKit/AppKit.h>
+
+G_DEFINE_TYPE (ClutterBackendOSX, clutter_backend_osx, CLUTTER_TYPE_BACKEND)
+
+/*************************************************************************/
+static gboolean
+clutter_backend_osx_post_parse (ClutterBackend  *backend,
+                                GError         **error)
+{
+  CLUTTER_NOTE (BACKEND, "post_parse");
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  /* Bring our app to foreground, background apps don't appear in dock or
+   * accept keyboard focus.
+   */
+  const ProcessSerialNumber psn = { 0, kCurrentProcess };
+  TransformProcessType (&psn, kProcessTransformToForegroundApplication);
+
+  /* Also raise our app to front, otherwise our window will remain under the
+   * terminal.
+   */
+  SetFrontProcess (&psn);
+
+  [NSApplication sharedApplication];
+
+  /* Initialize(?) OpenGL -- without this glGetString crashes
+   *
+   * Program received signal EXC_BAD_ACCESS, Could not access memory.
+   * Reason: KERN_PROTECTION_FAILURE at address: 0x00000ac0
+   * 0x92b22b2f in glGetString ()
+   */
+  [NSOpenGLView defaultPixelFormat];
+
+  CLUTTER_OSX_POOL_RELEASE();
+
+  return TRUE;
+}
+
+static ClutterFeatureFlags
+clutter_backend_osx_get_features (ClutterBackend *backend)
+{
+  return CLUTTER_FEATURE_STAGE_USER_RESIZE;
+}
+
+static gboolean
+clutter_backend_osx_init_stage (ClutterBackend  *backend,
+                                GError         **error)
+{
+  ClutterBackendOSX *self = CLUTTER_BACKEND_OSX (backend);
+  ClutterActor *stage;
+
+  CLUTTER_NOTE (BACKEND, "init_stage");
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  g_assert (self->stage == NULL);
+
+  /* Allocate ourselves a GL context. We need one this early for clutter to
+   * manage textures.
+   */
+  NSOpenGLPixelFormatAttribute attrs[] = {
+    NSOpenGLPFADoubleBuffer,
+    0
+  };
+  self->pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+  self->context = [[NSOpenGLContext alloc]
+                   initWithFormat: self->pixel_format
+                     shareContext: nil];
+  [self->context makeCurrentContext];
+
+  stage = clutter_stage_osx_new (backend);
+  self->stage = g_object_ref_sink (stage);
+
+  CLUTTER_OSX_POOL_RELEASE();
+
+  return TRUE;
+}
+
+static void
+clutter_backend_osx_init_events (ClutterBackend *backend)
+{
+  CLUTTER_NOTE (BACKEND, "init_events");
+
+  _clutter_events_osx_init ();
+}
+
+static ClutterActor *
+clutter_backend_osx_get_stage (ClutterBackend *backend)
+{
+  ClutterBackendOSX *self = CLUTTER_BACKEND_OSX (backend);
+
+  return self->stage;
+}
+
+static void
+clutter_backend_osx_redraw (ClutterBackend *backend)
+{
+  ClutterBackendOSX *self = CLUTTER_BACKEND_OSX (backend);
+  ClutterStageOSX *stage_osx;
+
+  stage_osx = CLUTTER_STAGE_OSX (self->stage);
+  [stage_osx->view setNeedsDisplay: YES];
+}
+
+/*************************************************************************/
+
+static void
+clutter_backend_osx_init (ClutterBackendOSX *self)
+{
+}
+
+static void
+clutter_backend_osx_dispose (GObject *object)
+{
+  ClutterBackendOSX *self = CLUTTER_BACKEND_OSX (object);
+
+  if (self->stage)
+    {
+      clutter_actor_destroy (self->stage);
+      self->stage = NULL;
+    }
+
+  [self->context release];
+  self->context = NULL;
+
+  [self->pixel_format release];
+  self->pixel_format = NULL;
+
+
+  G_OBJECT_CLASS (clutter_backend_osx_parent_class)->dispose (object);
+}
+
+static void
+clutter_backend_osx_class_init (ClutterBackendOSXClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass);
+
+  object_class->dispose = clutter_backend_osx_dispose;
+
+  backend_class->post_parse   = clutter_backend_osx_post_parse;
+  backend_class->get_features = clutter_backend_osx_get_features;
+  backend_class->init_stage  = clutter_backend_osx_init_stage;
+  backend_class->init_events = clutter_backend_osx_init_events;
+  backend_class->get_stage   = clutter_backend_osx_get_stage;
+  backend_class->redraw      = clutter_backend_osx_redraw;
+}
+
+GType
+_clutter_backend_impl_get_type (void)
+{
+  return clutter_backend_osx_get_type ();
+}
diff --git a/clutter/osx/clutter-backend-osx.h b/clutter/osx/clutter-backend-osx.h
new file mode 100644 (file)
index 0000000..c403657
--- /dev/null
@@ -0,0 +1,60 @@
+/* Clutter -  An OpenGL based 'interactive canvas' library.
+ * OSX backend - initial entry point
+ *
+ * Copyright (C) 2007  Tommi Komulainen <tommi.komulainen@iki.fi>
+ * Copyright (C) 2007  OpenedHand 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __CLUTTER_BACKEND_OSX_H__
+#define __CLUTTER_BACKEND_OSX_H__
+
+#include <clutter/clutter-backend.h>
+
+@class NSOpenGLPixelFormat, NSOpenGLContext;
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_BACKEND_OSX             (clutter_backend_osx_get_type())
+#define CLUTTER_BACKEND_OSX(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),CLUTTER_TYPE_BACKEND_OSX,ClutterBackendOSX))
+#define CLUTTER_BACKEND_OSX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),CLUTTER_TYPE_BACKEND_OSX,ClutterBackend))
+#define CLUTTER_IS_BACKEND_OSX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),CLUTTER_TYPE_BACKEND_OSX))
+#define CLUTTER_IS_BACKEND_OSX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),CLUTTER_TYPE_BACKEND_OSX))
+#define CLUTTER_BACKEND_OSX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj),CLUTTER_TYPE_BACKEND_OSX,ClutterBackendOSXClass))
+
+typedef struct _ClutterBackendOSX      ClutterBackendOSX;
+typedef struct _ClutterBackendOSXClass ClutterBackendOSXClass;
+
+struct _ClutterBackendOSX
+{
+  ClutterBackend parent;
+
+  NSOpenGLPixelFormat *pixel_format;
+  NSOpenGLContext     *context;
+
+  ClutterActor *stage;
+};
+
+struct _ClutterBackendOSXClass
+{
+  ClutterBackendClass parent_class;
+};
+
+GType        clutter_backend_osx_get_type    (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __CLUTTER_BACKEND_OSX_H__ */
diff --git a/clutter/osx/clutter-event-osx.c b/clutter/osx/clutter-event-osx.c
new file mode 100644 (file)
index 0000000..07b6248
--- /dev/null
@@ -0,0 +1,387 @@
+/* Clutter -  An OpenGL based 'interactive canvas' library.
+ * OSX backend - event loops integration
+ *
+ * Copyright (C) 2007  Tommi Komulainen <tommi.komulainen@iki.fi>
+ * Copyright (C) 2007  OpenedHand 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "config.h"
+
+#include "clutter-osx.h"
+
+#import <AppKit/AppKit.h>
+#include <glib/gmain.h>
+#include <clutter/clutter-debug.h>
+#include <clutter/clutter-private.h>
+#include <clutter/clutter-keysyms.h>
+
+/* Overriding the poll function because the events are not delivered over file
+ * descriptors and setting up a GSource would just introduce polling.
+ */
+
+static GPollFunc old_poll_func = NULL;
+
+/*************************************************************************/
+@interface NSEvent (Clutter)
+- (gint)clutterTime;
+- (gint)clutterButton;
+- (void)clutterX:(gint*)ptrX y:(gint*)ptrY;
+- (gint)clutterModifierState;
+- (guint)clutterKeyVal;
+@end
+
+@implementation NSEvent (Clutter)
+- (gint)clutterTime
+{
+  return [self timestamp] * 1000;
+}
+
+- (gint)clutterButton
+{
+  switch ([self buttonNumber])
+    {
+    case 0: return 1;   /* left   */
+    case 1: return 3;   /* right  */
+    case 2: return 2;   /* middle */
+    default: return 1 + [self buttonNumber];
+    }
+}
+
+- (void)clutterX:(gint*)ptrX y:(gint*)ptrY
+{
+  NSView *view = [[self window] contentView];
+  NSPoint pt = [view convertPoint:[self locationInWindow] fromView:nil];
+
+  *ptrX = (gint)pt.x;
+  *ptrY = (gint)pt.y;
+}
+
+- (gint)clutterModifierState
+{
+  guint mods = [self modifierFlags];
+  gint rv = 0;
+
+  if (mods & NSAlphaShiftKeyMask)
+    rv |= CLUTTER_LOCK_MASK;
+  if (mods & NSShiftKeyMask)
+    rv |= CLUTTER_SHIFT_MASK;
+  if (mods & NSControlKeyMask)
+    rv |= CLUTTER_CONTROL_MASK;
+  if (mods & NSAlternateKeyMask)
+    rv |= CLUTTER_MOD1_MASK;
+  if (mods & NSCommandKeyMask)
+    rv |= CLUTTER_MOD2_MASK;
+
+  return rv;
+}
+
+- (guint)clutterKeyVal
+{
+  /* FIXME: doing this right is a lot of work, see gdkkeys-quartz.c in gtk+
+   * For now handle some common/simple keys only. Might not work with other
+   * hardware than mine (MacBook Pro, finnish layout). Sorry.
+   */
+  unichar c = [[self characters] characterAtIndex:0];
+
+  /* Latin-1 characters, 1:1 mapping - this ought to be reliable */
+  if ((c >= 0x0020 && c <= 0x007e) ||
+      (c >= 0x00a0 && c <= 0x00ff))
+    return c;
+
+  switch (c)
+    {
+    /* these should be fairly standard */
+    /* (maybe add 0x0008 (Ctrl+H) for backspace too) */
+    case 0x000d: return CLUTTER_Return;
+    case 0x001b: return CLUTTER_Escape;
+    case 0x007f: return CLUTTER_BackSpace;
+    /* Defined in NSEvent.h */
+    case NSUpArrowFunctionKey:    return CLUTTER_Up;
+    case NSDownArrowFunctionKey:  return CLUTTER_Down;
+    case NSLeftArrowFunctionKey:  return CLUTTER_Left;
+    case NSRightArrowFunctionKey: return CLUTTER_Right;
+    case NSF1FunctionKey:         return CLUTTER_F1;
+    case NSF2FunctionKey:         return CLUTTER_F2;
+    case NSF3FunctionKey:         return CLUTTER_F3;
+    case NSF4FunctionKey:         return CLUTTER_F4;
+    case NSF5FunctionKey:         return CLUTTER_F5;
+    case NSF6FunctionKey:         return CLUTTER_F6;
+    case NSF7FunctionKey:         return CLUTTER_F7;
+    case NSF8FunctionKey:         return CLUTTER_F8;
+    case NSF9FunctionKey:         return CLUTTER_F9;
+    case NSF10FunctionKey:        return CLUTTER_F10;
+    case NSF11FunctionKey:        return CLUTTER_F11;
+    case NSF12FunctionKey:        return CLUTTER_F12;
+    case NSInsertFunctionKey:     return CLUTTER_Insert;
+    case NSDeleteFunctionKey:     return CLUTTER_Delete;
+    case NSHomeFunctionKey:       return CLUTTER_Home;
+    case NSEndFunctionKey:        return CLUTTER_End;
+    case NSPageUpFunctionKey:     return CLUTTER_Page_Up;
+    case NSPageDownFunctionKey:   return CLUTTER_Page_Down;
+    }
+
+  CLUTTER_NOTE (BACKEND, "unhandled unicode key 0x%x (%d)", c, c);
+
+  /* hardware dependent, worksforme(tm) Redundant due to above, left around as
+   * example.
+   */
+  switch ([self keyCode])
+    {
+    case 115: return CLUTTER_Home;
+    case 116: return CLUTTER_Page_Up;
+    case 117: return CLUTTER_Delete;
+    case 119: return CLUTTER_End;
+    case 121: return CLUTTER_Page_Down;
+    case 123: return CLUTTER_Left;
+    case 124: return CLUTTER_Right;
+    case 125: return CLUTTER_Down;
+    case 126: return CLUTTER_Up;
+    }
+
+  return 0;
+}
+@end
+
+/*************************************************************************/
+static gboolean
+clutter_event_osx_translate (NSEvent *nsevent, ClutterEvent *event)
+{
+  event->any.time = [nsevent clutterTime];
+
+  switch ([nsevent type])
+    {
+    case NSLeftMouseDown:
+    case NSRightMouseDown:
+    case NSOtherMouseDown:
+      event->type = CLUTTER_BUTTON_PRESS;
+      /* fall through */
+    case NSLeftMouseUp:
+    case NSRightMouseUp:
+    case NSOtherMouseUp:
+      if (event->type != CLUTTER_BUTTON_PRESS)
+        event->type = CLUTTER_BUTTON_RELEASE;
+
+      event->button.button = [nsevent clutterButton];
+      event->button.click_count = [nsevent clickCount];
+      event->motion.modifier_state = [nsevent clutterModifierState];
+      [nsevent clutterX:&(event->button.x) y:&(event->button.y)];
+
+      CLUTTER_NOTE (EVENT, "button %d %s at %d,%d clicks=%d",
+                    [nsevent buttonNumber],
+                    event->type == CLUTTER_BUTTON_PRESS ? "press" : "release",
+                    event->button.x, event->button.y,
+                    event->button.click_count);
+      return TRUE;
+
+    case NSMouseMoved:
+    case NSLeftMouseDragged:
+    case NSRightMouseDragged:
+    case NSOtherMouseDragged:
+      event->type = CLUTTER_MOTION;
+
+      [nsevent clutterX:(&event->motion.x) y:&(event->motion.y)];
+      event->motion.modifier_state = [nsevent clutterModifierState];
+
+      CLUTTER_NOTE (EVENT, "motion %d at %d,%d",
+                    [nsevent buttonNumber],
+                    event->button.x, event->button.y);
+      return TRUE;
+
+    case NSKeyDown:
+      event->type = CLUTTER_KEY_PRESS;
+      /* fall through */
+    case NSKeyUp:
+      if (event->type != CLUTTER_KEY_PRESS)
+        event->type = CLUTTER_KEY_RELEASE;
+
+      event->key.hardware_keycode = [nsevent keyCode];
+      event->key.modifier_state = [nsevent clutterModifierState];
+      event->key.keyval = [nsevent clutterKeyVal];
+
+      CLUTTER_NOTE (EVENT, "key %d (%s) (%s) %s, keyval %d",
+                    [nsevent keyCode],
+                    [[nsevent characters] UTF8String],
+                    [[nsevent charactersIgnoringModifiers] UTF8String],
+                    event->type == CLUTTER_KEY_PRESS ? "press" : "release",
+                    event->key.keyval);
+      return TRUE;
+
+    default:
+      CLUTTER_NOTE (EVENT, "unhandled event %d", [nsevent type]);
+      break;
+    }
+
+  return FALSE;
+}
+
+void
+_clutter_event_osx_put (NSEvent *nsevent)
+{
+  ClutterEvent event;
+  event.type = CLUTTER_NOTHING;
+
+  if (clutter_event_osx_translate (nsevent, &event))
+    {
+      g_assert (event.type != CLUTTER_NOTHING);
+      clutter_event_put (&event);
+    }
+}
+
+typedef struct {
+  CFSocketRef        sock;
+  CFRunLoopSourceRef source;
+
+  gushort            revents;
+} SocketInfo;
+
+static void
+socket_activity_cb (CFSocketRef           sock,
+                    CFSocketCallBackType  cbtype,
+                    CFDataRef             address,
+                    const void           *data,
+                    void                 *info)
+{
+  SocketInfo *si = info;
+
+  if (cbtype & kCFSocketReadCallBack)
+    si->revents |= G_IO_IN;
+  if (cbtype & kCFSocketWriteCallBack)
+    si->revents |= G_IO_OUT;
+}
+
+static gint
+clutter_event_osx_poll_func (GPollFD *ufds, guint nfds, gint timeout)
+{
+  NSDate     *until_date;
+  NSEvent    *nsevent;
+  SocketInfo *sockets = NULL;
+  gint        n_active = 0;
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  if (timeout == -1)
+    until_date = [NSDate distantFuture];
+  else if (timeout == 0)
+    until_date = [NSDate distantPast];
+  else
+    until_date = [NSDate dateWithTimeIntervalSinceNow:timeout/1000.0];
+
+  /* File descriptors appear to be similar enough to sockets so that they can
+   * be used in CFRunLoopSource.
+   *
+   * We could also launch a thread to call old_poll_func and signal the main
+   * thread. No idea which way is better.
+   */
+  if (nfds > 0)
+    {
+      CFRunLoopRef run_loop;
+
+      run_loop = [[NSRunLoop currentRunLoop] getCFRunLoop];
+      sockets = g_new (SocketInfo, nfds);
+
+      int i;
+      for (i = 0; i < nfds; i++)
+        {
+          SocketInfo *si = &sockets[i];
+          CFSocketCallBackType cbtype;
+
+          cbtype = 0;
+          if (ufds[i].events & G_IO_IN)
+            cbtype |= kCFSocketReadCallBack;
+          if (ufds[i].events & G_IO_OUT)
+            cbtype |= kCFSocketWriteCallBack;
+          /* FIXME: how to handle G_IO_HUP and G_IO_ERR? */
+
+          const CFSocketContext ctxt = {
+            0, si, NULL, NULL, NULL
+          };
+          si->sock = CFSocketCreateWithNative (NULL, ufds[i].fd, cbtype, socket_activity_cb, &ctxt);
+          si->source = CFSocketCreateRunLoopSource (NULL, si->sock, 0);
+          si->revents = 0;
+
+          CFRunLoopAddSource (run_loop, si->source, kCFRunLoopCommonModes);
+        }
+    }
+
+  nsevent = [NSApp nextEventMatchingMask: NSAnyEventMask
+                               untilDate: until_date
+                                  inMode: NSDefaultRunLoopMode
+                                 dequeue: YES];
+
+  /* Push the events to NSApplication which will do some magic(?) and forward
+   * interesting events to our view. While we could do event translation here
+   * we'd also need to filter out clicks on titlebar, and perhaps do special
+   * handling for the first click (couldn't figure it out - always ended up
+   * missing a screen refresh) and maybe other things.
+   */
+  [NSApp sendEvent:nsevent];
+
+  if (nfds > 0)
+    {
+      int i;
+      for (i = 0; i < nfds; i++)
+        {
+          SocketInfo *si = &sockets[i];
+
+          if ((ufds[i].revents = si->revents) != 0)
+            n_active++;
+
+          /* Invalidating the source also removes it from run loop and
+           * guarantees the callback is never called again.
+           * CFRunLoopRemoveSource removes the source from the loop, but might
+           * still call the callback which would be badly timed.
+           */
+          CFRunLoopSourceInvalidate (si->source);
+          CFRelease (si->source);
+          CFRelease (si->sock);
+        }
+
+      g_free (sockets);
+    }
+
+  /* FIXME this could result in infinite loop */
+  ClutterEvent *event = clutter_event_get ();
+  while (event)
+    {
+      clutter_do_event (event);
+      clutter_event_free (event);
+      event = clutter_event_get ();
+    }
+
+  CLUTTER_OSX_POOL_RELEASE();
+
+  return n_active;
+}
+
+void
+_clutter_events_osx_init (void)
+{
+  g_assert (old_poll_func == NULL);
+
+  old_poll_func = g_main_context_get_poll_func (NULL);
+  g_main_context_set_poll_func (NULL, clutter_event_osx_poll_func);
+}
+
+void
+_clutter_events_osx_uninit (void)
+{
+  if (old_poll_func)
+    {
+      g_main_context_set_poll_func (NULL, old_poll_func);
+      old_poll_func = NULL;
+    }
+}
diff --git a/clutter/osx/clutter-osx.h b/clutter/osx/clutter-osx.h
new file mode 100644 (file)
index 0000000..d727138
--- /dev/null
@@ -0,0 +1,41 @@
+/* Clutter -  An OpenGL based 'interactive canvas' library.
+ * OSX backend
+ *
+ * Copyright (C) 2007  Tommi Komulainen <tommi.komulainen@iki.fi>
+ * Copyright (C) 2007  OpenedHand 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __CLUTTER_OSX_H__
+#define __CLUTTER_OSX_H__
+
+#include <clutter/clutter-stage.h>
+
+@class NSEvent;
+
+G_BEGIN_DECLS
+
+#define CLUTTER_OSX_POOL_ALLOC()        NSAutoreleasePool *autorelease_pool = [[NSAutoreleasePool alloc] init]
+#define CLUTTER_OSX_POOL_RELEASE()      [autorelease_pool release];
+
+void _clutter_events_osx_init   (void);
+void _clutter_events_osx_uninit (void);
+
+void _clutter_event_osx_put     (NSEvent *nsevent);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_OSX_H__ */
diff --git a/clutter/osx/clutter-stage-osx.c b/clutter/osx/clutter-stage-osx.c
new file mode 100644 (file)
index 0000000..79c6da2
--- /dev/null
@@ -0,0 +1,496 @@
+/* Clutter -  An OpenGL based 'interactive canvas' library.
+ * OSX backend - integration with NSWindow and NSView
+ *
+ * Copyright (C) 2007  Tommi Komulainen <tommi.komulainen@iki.fi>
+ * Copyright (C) 2007  OpenedHand 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "config.h"
+
+#include "clutter-osx.h"
+#include "clutter-stage-osx.h"
+#include "clutter-backend-osx.h"
+#import <AppKit/AppKit.h>
+
+#include <clutter/clutter-debug.h>
+#include <clutter/clutter-private.h>
+
+G_DEFINE_TYPE (ClutterStageOSX, clutter_stage_osx, CLUTTER_TYPE_STAGE)
+
+/* FIXME: this should be in clutter-stage.c */
+static void
+clutter_stage_osx_state_update (ClutterStageOSX   *self,
+                                ClutterStageState  unset_flags,
+                                ClutterStageState  set_flags);
+
+#define CLUTTER_OSX_FULLSCREEN_WINDOW_LEVEL (NSMainMenuWindowLevel + 1)
+
+/*************************************************************************/
+@interface ClutterGLWindow : NSWindow
+{
+  ClutterStageOSX *stage;
+}
+@end
+
+@implementation ClutterGLWindow
+- (id)initWithView:(NSView *)aView UTF8Title:(const char *)aTitle stage:(ClutterStageOSX *)aStage
+{
+  if ((self = [super initWithContentRect: [aView frame]
+                               styleMask: NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask
+                                 backing: NSBackingStoreBuffered
+                                   defer: NO]) != nil)
+    {
+      [self setDelegate: self];
+      [self useOptimizedDrawing: YES];
+      [self setContentView: aView];
+      [self setTitle:[NSString stringWithUTF8String: aTitle]];
+      stage = aStage;
+    }
+  return self;
+}
+
+- (BOOL) windowShouldClose: (id) sender
+{
+  CLUTTER_NOTE (BACKEND, "windowShouldClose");
+
+  ClutterEvent event;
+  event.type = CLUTTER_DELETE;
+  clutter_event_put (&event);
+
+  return NO;
+}
+
+- (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen*)aScreen
+{
+  /* in fullscreen mode we don't want to be constrained by menubar or dock
+   * FIXME: calculate proper constraints depending on fullscreen mode
+   */
+
+  return frameRect;
+}
+
+- (void) windowDidBecomeKey:(NSNotification*)aNotification
+{
+  CLUTTER_NOTE (BACKEND, "windowDidBecomeKey");
+
+  if (stage->stage_state & CLUTTER_STAGE_STATE_FULLSCREEN)
+    [self setLevel: CLUTTER_OSX_FULLSCREEN_WINDOW_LEVEL];
+
+  clutter_stage_osx_state_update (stage, 0, CLUTTER_STAGE_STATE_ACTIVATED);
+}
+
+- (void) windowDidResignKey:(NSNotification*)aNotification
+{
+  CLUTTER_NOTE (BACKEND, "windowDidResignKey");
+
+  if (stage->stage_state & CLUTTER_STAGE_STATE_FULLSCREEN)
+    {
+      [self setLevel: NSNormalWindowLevel];
+      [self orderBack: nil];
+    }
+
+  clutter_stage_osx_state_update (stage, CLUTTER_STAGE_STATE_ACTIVATED, 0);
+}
+@end
+
+/*************************************************************************/
+@interface ClutterGLView : NSOpenGLView
+{
+  ClutterActor *stage;
+}
+- (void) drawRect: (NSRect) bounds;
+@end
+
+@implementation ClutterGLView
+- (id) initWithFrame: (NSRect)aFrame pixelFormat:(NSOpenGLPixelFormat*)aFormat stage:(ClutterActor*)aStage
+{
+  if ((self = [super initWithFrame:aFrame pixelFormat:aFormat]) != nil)
+    {
+      self->stage = aStage;
+    }
+  return self;
+}
+
+- (void) drawRect: (NSRect) bounds
+{
+  clutter_actor_paint (self->stage);
+  [[self openGLContext] flushBuffer];
+}
+
+/* In order to receive key events */
+- (BOOL) acceptsFirstResponder
+{
+  return YES;
+}
+
+/* We want 0,0 top left */
+- (BOOL) isFlipped
+{
+  return YES;
+}
+
+- (void) setFrameSize: (NSSize) aSize
+{
+  CLUTTER_NOTE (BACKEND, "setFrameSize: %dx%d",
+                (int)aSize.width, (int)aSize.height);
+
+  [super setFrameSize: aSize];
+
+  clutter_actor_set_size (self->stage, (int)aSize.width, (int)aSize.height);
+
+  CLUTTER_SET_PRIVATE_FLAGS(self->stage, CLUTTER_ACTOR_SYNC_MATRICES);
+}
+
+/* Simply forward all events that reach our view to clutter. */
+
+#define EVENT_HANDLER(event) -(void)event:(NSEvent *)theEvent { \
+  _clutter_event_osx_put (theEvent);                            \
+}
+EVENT_HANDLER(mouseDown)
+EVENT_HANDLER(mouseDragged)
+EVENT_HANDLER(mouseUp)
+EVENT_HANDLER(mouseMoved)
+EVENT_HANDLER(mouseEntered)
+EVENT_HANDLER(mouseExited)
+EVENT_HANDLER(rightMouseDown)
+EVENT_HANDLER(rightMouseDragged)
+EVENT_HANDLER(rightMouseUp)
+EVENT_HANDLER(otherMouseDown)
+EVENT_HANDLER(otherMouseDragged)
+EVENT_HANDLER(otherMouseUp)
+EVENT_HANDLER(scrollWheel)
+EVENT_HANDLER(keyDown)
+EVENT_HANDLER(keyUp)
+EVENT_HANDLER(flagsChanged)
+EVENT_HANDLER(helpRequested)
+EVENT_HANDLER(tabletPoint)
+EVENT_HANDLER(tabletProximity)
+
+#undef EVENT_HANDLER
+@end
+
+/*************************************************************************/
+static void
+clutter_stage_osx_state_update (ClutterStageOSX   *self,
+                                ClutterStageState  unset_flags,
+                                ClutterStageState  set_flags)
+{
+  ClutterStageStateEvent event;
+
+  event.new_state = self->stage_state;
+  event.new_state |= set_flags;
+  event.new_state &= ~unset_flags;
+
+  if (event.new_state == self->stage_state)
+    return;
+
+  event.changed_mask = event.new_state ^ self->stage_state;
+
+  self->stage_state = event.new_state;
+
+  event.type = CLUTTER_STAGE_STATE;
+  clutter_event_put ((ClutterEvent*)&event);
+}
+
+static void
+clutter_stage_osx_save_frame (ClutterStageOSX *self)
+{
+  if (CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (self)))
+    {
+      g_assert (self->window != NULL);
+
+      self->normalFrame = [self->window frame];
+      self->haveNormalFrame = TRUE;
+    }
+}
+
+static void
+clutter_stage_osx_set_frame (ClutterStageOSX *self)
+{
+  g_assert (CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (self)));
+  g_assert (self->window != NULL);
+
+  if (self->stage_state & CLUTTER_STAGE_STATE_FULLSCREEN)
+    {
+      /* Raise above the menubar (and dock) covering the whole screen.
+       *
+       * NOTE: This effectively breaks Option-Tabbing as our window covers
+       * all other applications completely. However we deal with the situation
+       * by lowering the window to the bottom of the normal level stack on
+       * windowDidResignKey notification.
+       */
+      [self->window setLevel: CLUTTER_OSX_FULLSCREEN_WINDOW_LEVEL];
+
+      [self->window setFrame: [self->window frameRectForContentRect: [[self->window screen] frame]] display: NO];
+    }
+  else
+    {
+      [self->window setLevel: NSNormalWindowLevel];
+
+      if (self->haveNormalFrame)
+        [self->window setFrame: self->normalFrame display: NO];
+      else
+        /* looks better than positioning to 0,0 (bottom right) */
+        [self->window center];
+    }
+}
+
+/*************************************************************************/
+static void
+clutter_stage_osx_realize (ClutterActor *actor)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (actor);
+  ClutterBackendOSX *backend_osx;
+
+  CLUTTER_NOTE (BACKEND, "realize");
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  if (CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->realize)
+    CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->realize (actor);
+
+  backend_osx = CLUTTER_BACKEND_OSX (self->backend);
+
+  NSRect rect = NSMakeRect(0, 0, self->requisition_width, self->requisition_height);
+
+  self->view = [[ClutterGLView alloc]
+                initWithFrame: rect
+                  pixelFormat: backend_osx->pixel_format
+                        stage: actor];
+
+  self->window = [[ClutterGLWindow alloc]
+                  initWithView: self->view
+                     UTF8Title: clutter_stage_get_title (CLUTTER_STAGE (self))
+                         stage: self];
+
+  /* looks better than positioning to 0,0 (bottom right) */
+  [self->window center];
+
+  /* To not miss all textures created with the context created in the backend
+   * make sure we share the context. (By default NSOpenGLView creates its own
+   * context.)
+   */
+  NSOpenGLContext *context = backend_osx->context;
+
+  [self->view setOpenGLContext: context];
+  [context setView: self->view];
+
+  CLUTTER_OSX_POOL_RELEASE();
+
+  CLUTTER_SET_PRIVATE_FLAGS(self, CLUTTER_ACTOR_SYNC_MATRICES);
+}
+
+static void
+clutter_stage_osx_unrealize (ClutterActor *actor)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (actor);
+
+  CLUTTER_NOTE (BACKEND, "unrealize");
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  [self->view release];
+  [self->window close];
+
+  self->view = NULL;
+  self->window = NULL;
+
+  CLUTTER_OSX_POOL_RELEASE();
+
+  if (CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->unrealize)
+    CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->unrealize (actor);
+}
+
+static void
+clutter_stage_osx_show (ClutterActor *actor)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (actor);
+
+  CLUTTER_NOTE (BACKEND, "show");
+
+  if (CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->show)
+    CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->show (actor);
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  clutter_stage_osx_set_frame (self);
+
+  [self->window makeKeyAndOrderFront: nil];
+
+  CLUTTER_OSX_POOL_RELEASE();
+}
+
+static void
+clutter_stage_osx_hide (ClutterActor *actor)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (actor);
+
+  CLUTTER_NOTE (BACKEND, "hide");
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  [self->window orderOut: nil];
+
+  CLUTTER_OSX_POOL_RELEASE();
+
+  if (CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->hide)
+    CLUTTER_ACTOR_CLASS (clutter_stage_osx_parent_class)->hide (actor);
+}
+
+static void
+clutter_stage_osx_query_coords (ClutterActor    *actor,
+                                ClutterActorBox *box)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (actor);
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  box->x1 = 0;
+  box->y1 = 0;
+  box->x2 = box->x1 + CLUTTER_UNITS_FROM_FLOAT (self->requisition_width);
+  box->y2 = box->y1 + CLUTTER_UNITS_FROM_FLOAT (self->requisition_height);
+
+  CLUTTER_OSX_POOL_RELEASE();
+}
+
+static void
+clutter_stage_osx_request_coords (ClutterActor    *actor,
+                                  ClutterActorBox *box)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (actor);
+
+  CLUTTER_NOTE (BACKEND, "request_coords: %d,%d %dx%d",
+                CLUTTER_UNITS_TO_INT (box->x1),
+                CLUTTER_UNITS_TO_INT (box->y1),
+                CLUTTER_UNITS_TO_INT (box->x2 - box->x1),
+                CLUTTER_UNITS_TO_INT (box->y2 - box->y1));
+
+  self->requisition_width  = CLUTTER_UNITS_TO_INT (box->x2 - box->x1);
+  self->requisition_height = CLUTTER_UNITS_TO_INT (box->y2 - box->y1);
+
+  if (CLUTTER_ACTOR_IS_REALIZED (actor))
+    {
+      CLUTTER_OSX_POOL_ALLOC();
+
+      NSSize size = NSMakeSize(self->requisition_width,
+                               self->requisition_height);
+      [self->window setContentSize: size];
+
+      CLUTTER_OSX_POOL_RELEASE();
+    }
+}
+
+/*************************************************************************/
+static void
+clutter_stage_osx_set_title (ClutterStage *stage,
+                             const char   *title)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (stage);
+
+  CLUTTER_NOTE (BACKEND, "set_title: %s", title);
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  if (CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (stage)))
+    [self->window setTitle:[NSString stringWithUTF8String:title]];
+
+  CLUTTER_OSX_POOL_RELEASE();
+}
+
+static void
+clutter_stage_osx_set_fullscreen (ClutterStage *stage,
+                                  gboolean      fullscreen)
+{
+  ClutterStageOSX *self = CLUTTER_STAGE_OSX (stage);
+
+  CLUTTER_NOTE (BACKEND, "set_fullscreen: %u", fullscreen);
+
+  CLUTTER_OSX_POOL_ALLOC();
+
+  /* Make sure to update the state before clutter_stage_osx_set_frame.
+   *
+   * Toggling fullscreen isn't atomic, there's two "events" involved:
+   *  - stage state change (via state_update)
+   *  - stage size change (via set_frame -> setFrameSize / set_size)
+   *
+   * We do state change first. Not sure there's any difference.
+   */
+  if (fullscreen)
+    clutter_stage_osx_state_update (self, 0, CLUTTER_STAGE_STATE_FULLSCREEN);
+  else
+    clutter_stage_osx_state_update (self, CLUTTER_STAGE_STATE_FULLSCREEN, 0);
+
+  if (CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR (self)))
+    {
+      if (fullscreen)
+        clutter_stage_osx_save_frame (self);
+
+      clutter_stage_osx_set_frame (self);
+    }
+  else if (fullscreen)
+    {
+      /* FIXME: if you go fullscreen before realize we throw away the normal
+       * stage size and can't return. Need to maintain them separately.
+       */
+      NSSize size = [[NSScreen mainScreen] frame].size;
+
+      clutter_actor_set_size (CLUTTER_ACTOR (self),
+                              (int)size.width, (int)size.height);
+    }
+
+  CLUTTER_OSX_POOL_RELEASE();
+}
+
+/*************************************************************************/
+ClutterActor *
+clutter_stage_osx_new (ClutterBackend *backend)
+{
+  ClutterStageOSX *self;
+
+  self = g_object_new (CLUTTER_TYPE_STAGE_OSX, NULL);
+  self->backend = backend;
+
+  return CLUTTER_ACTOR(self);
+}
+
+/*************************************************************************/
+static void
+clutter_stage_osx_init (ClutterStageOSX *self)
+{
+  self->requisition_width  = 640;
+  self->requisition_height = 480;
+
+  CLUTTER_SET_PRIVATE_FLAGS(self, CLUTTER_ACTOR_SYNC_MATRICES);
+}
+
+static void
+clutter_stage_osx_class_init (ClutterStageOSXClass *klass)
+{
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass);
+
+  actor_class->realize   = clutter_stage_osx_realize;
+  actor_class->unrealize = clutter_stage_osx_unrealize;
+  actor_class->show      = clutter_stage_osx_show;
+  actor_class->hide      = clutter_stage_osx_hide;
+
+  actor_class->query_coords   = clutter_stage_osx_query_coords;
+  actor_class->request_coords = clutter_stage_osx_request_coords;
+
+  stage_class->set_title = clutter_stage_osx_set_title;
+  stage_class->set_fullscreen = clutter_stage_osx_set_fullscreen;
+}
diff --git a/clutter/osx/clutter-stage-osx.h b/clutter/osx/clutter-stage-osx.h
new file mode 100644 (file)
index 0000000..0e5d29d
--- /dev/null
@@ -0,0 +1,73 @@
+/* Clutter -  An OpenGL based 'interactive canvas' library.
+ * OSX backend - integration with NSWindow and NSView
+ *
+ * Copyright (C) 2007  Tommi Komulainen <tommi.komulainen@iki.fi>
+ * Copyright (C) 2007  OpenedHand 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __CLUTTER_STAGE_OSX_H__
+#define __CLUTTER_STAGE_OSX_H__
+
+#include <clutter/clutter-stage.h>
+#include <clutter/clutter-backend.h>
+
+#import <Foundation/Foundation.h>
+@class NSOpenGLView, NSWindow;
+
+G_BEGIN_DECLS
+
+/* convenience macros */
+#define CLUTTER_TYPE_STAGE_OSX             (clutter_stage_osx_get_type())
+#define CLUTTER_STAGE_OSX(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),CLUTTER_TYPE_STAGE_OSX,ClutterStageOSX))
+#define CLUTTER_STAGE_OSX_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass),CLUTTER_TYPE_STAGE_OSX,ClutterStage))
+#define CLUTTER_IS_STAGE_OSX(obj)          (G_TYPE_CHECK_INSTANCE_TYPE((obj),CLUTTER_TYPE_STAGE_OSX))
+#define CLUTTER_IS_STAGE_OSX_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),CLUTTER_TYPE_STAGE_OSX))
+#define CLUTTER_STAGE_OSX_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj),CLUTTER_TYPE_STAGE_OSX,ClutterStageOSXClass))
+
+typedef struct _ClutterStageOSX      ClutterStageOSX;
+typedef struct _ClutterStageOSXClass ClutterStageOSXClass;
+
+struct _ClutterStageOSX
+{
+  ClutterStage parent;
+
+  ClutterBackend *backend;
+
+  NSWindow *window;
+  NSOpenGLView *view;
+
+  gboolean haveNormalFrame;
+  NSRect normalFrame;
+
+  gint requisition_width;
+  gint requisition_height;
+
+  ClutterStageState stage_state;
+};
+
+struct _ClutterStageOSXClass
+{
+  ClutterStageClass parent_class;
+};
+
+GType           clutter_stage_osx_get_type    (void) G_GNUC_CONST;
+
+ClutterActor*   clutter_stage_osx_new (ClutterBackend *backend);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_STAGE_OSX_H__ */
index 6e25187..4881473 100644 (file)
@@ -258,8 +258,20 @@ case $clutterbackend in
     EGL_CFLAGS="$TSLIB_CFLAGS"
     ;;
 
+  osx)
 
-  *) AC_MSG_ERROR([Invalid backend for Clutter: use glx,sdl, eglx or eglnative])
+    CLUTTER_FLAVOUR="osx"
+    AC_DEFINE([HAVE_CLUTTER_OSX], [1], [Have the OSX backend])
+
+    CLUTTER_COGL="gl"
+    AC_DEFINE([HAVE_COGL_GL], [1], [Have GL for rendering])
+
+    OSX_LIBS="-framework Cocoa -framework OpenGL"
+    AC_CHECK_HEADERS([OpenGL/gl.h],,
+                     [AC_MSG_ERROR([Unable to locate required GL headers])])
+    ;;
+
+  *) AC_MSG_ERROR([Invalid backend for Clutter: use glx,sdl,osx,eglx or eglnative])
     ;;
 
 esac
@@ -351,8 +363,8 @@ dnl ========================================================================
 
 AC_SUBST(GCC_FLAGS)
 
-CLUTTER_CFLAGS="$SDL_CFLAGS $EGL_CFLAGS $GLX_CFLAGS $CLUTTER_DEPS_CFLAGS "
-CLUTTER_LIBS="$SDL_LIBS $EGL_LIBS $GLX_LIBS $CLUTTER_DEPS_LIBS"
+CLUTTER_CFLAGS="$SDL_CFLAGS $EGL_CFLAGS $GLX_CFLAGS $OSX_CFLAGS $CLUTTER_DEPS_CFLAGS "
+CLUTTER_LIBS="$SDL_LIBS $EGL_LIBS $GLX_LIBS $OSX_LIBS $CLUTTER_DEPS_LIBS"
 
 AC_SUBST(CLUTTER_CFLAGS)
 AC_SUBST(CLUTTER_LIBS)
@@ -364,6 +376,7 @@ AC_CONFIG_FILES([
         clutter/glx/Makefile
         clutter/eglx/Makefile
         clutter/eglnative/Makefile
+        clutter/osx/Makefile
         clutter/sdl/Makefile
         clutter/cogl/Makefile
         clutter/cogl/gl/Makefile