1 /* Clutter -  An OpenGL based 'interactive canvas' library.
2  * OSX backend - initial entry point
3  *
4  * Copyright (C) 2007-2008  Tommi Komulainen <tommi.komulainen@iki.fi>
5  * Copyright (C) 2007  OpenedHand Ltd.
6  * Copyright (C) 2011  Crystalnix <vgachkaylo@crystalnix.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  *
22  */
23 #include "config.h"
24 
25 #import "clutter-osx.h"
26 #import "clutter-backend-osx.h"
27 #import "clutter-device-manager-osx.h"
28 #import "clutter-stage-osx.h"
29 #import "clutter-event-loop-osx.h"
30 
31 #include "clutter-debug.h"
32 #include "clutter-private.h"
33 #include "clutter-stage-private.h"
34 
35 #include "cogl/cogl.h"
36 
37 #import <AppKit/AppKit.h>
38 
39 #define DEFAULT_FONT_NAME       "Lucida Grande 13"
40 
41 #define clutter_backend_osx_get_type    _clutter_backend_osx_get_type
42 
43 G_DEFINE_TYPE (ClutterBackendOSX, clutter_backend_osx, CLUTTER_TYPE_BACKEND)
44 
45 /* various flags corresponding to pre init setup calls */
46 static gboolean _no_event_retrieval = FALSE;
47 
48 /*************************************************************************/
49 static gboolean
clutter_backend_osx_post_parse(ClutterBackend * backend,GError ** error)50 clutter_backend_osx_post_parse (ClutterBackend  *backend,
51                                 GError         **error)
52 {
53   ClutterSettings *settings = clutter_settings_get_default ();
54 
55   CLUTTER_OSX_POOL_ALLOC();
56   /* getting standart dpi for main screen */
57   NSDictionary* prop = [[NSScreen mainScreen] deviceDescription];
58   NSSize size;
59   [[prop valueForKey:@"NSDeviceResolution"] getValue:&size];
60   CLUTTER_OSX_POOL_RELEASE();
61 
62   /* setting dpi for backend, it needs by font rendering library */
63   if (size.height > 0)
64     {
65       int font_dpi = size.height * 1024;
66 
67       g_object_set (settings, "font-dpi", font_dpi, NULL);
68     }
69 
70   /* set the default font name */
71   g_object_set (settings, "font-name", DEFAULT_FONT_NAME, NULL);
72 
73   /* finish launching the application */
74   [NSApp finishLaunching];
75 
76   return TRUE;
77 }
78 
79 void
clutter_osx_disable_event_retrieval(void)80 clutter_osx_disable_event_retrieval (void)
81 {
82   if (_clutter_context_is_initialized ())
83     {
84       g_warning ("clutter_osx_disable_event_retrieval() can only be "
85                  "called before clutter_init()");
86       return;
87     }
88 
89   _no_event_retrieval = TRUE;
90 }
91 
92 static ClutterFeatureFlags
clutter_backend_osx_get_features(ClutterBackend * backend)93 clutter_backend_osx_get_features (ClutterBackend *backend)
94 {
95   return CLUTTER_FEATURE_STAGE_MULTIPLE
96        | CLUTTER_FEATURE_STAGE_USER_RESIZE;
97 }
98 
99 void
_clutter_backend_osx_events_init(ClutterBackend * backend)100 _clutter_backend_osx_events_init (ClutterBackend *backend)
101 {
102   ClutterBackendOSX *backend_osx = CLUTTER_BACKEND_OSX (backend);
103 
104   if (backend_osx->device_manager != NULL)
105     return;
106 
107   CLUTTER_NOTE (BACKEND, "init_events");
108 
109   backend->device_manager = backend_osx->device_manager =
110     g_object_new (CLUTTER_TYPE_DEVICE_MANAGER_OSX,
111                   "backend", CLUTTER_BACKEND(backend_osx),
112                   NULL);
113 
114   if (!_no_event_retrieval)
115     _clutter_osx_event_loop_init ();
116 }
117 
118 static gboolean
clutter_backend_osx_create_context(ClutterBackend * backend,GError ** error)119 clutter_backend_osx_create_context (ClutterBackend  *backend,
120                                     GError         **error)
121 {
122   ClutterBackendOSX *backend_osx = CLUTTER_BACKEND_OSX (backend);
123 
124   CLUTTER_OSX_POOL_ALLOC();
125 
126   if (backend_osx->context == nil)
127     {
128       /* Allocate ourselves a GL context. Since we're supposed to have
129        * only one per backend we can just as well create it now.
130        */
131       NSOpenGLPixelFormatAttribute attrs[] = {
132         NSOpenGLPFADoubleBuffer,
133         NSOpenGLPFADepthSize, 32,
134         NSOpenGLPFAStencilSize, 8,
135         0
136       };
137 
138 #ifdef MAC_OS_X_VERSION_10_5
139       const int sw = 1;
140 #else
141       const long sw = 1;
142 #endif
143 
144       backend_osx->pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
145 
146       backend_osx->context = [[NSOpenGLContext alloc] initWithFormat: backend_osx->pixel_format
147                                                         shareContext: nil];
148 
149       /* Enable vblank sync - http://developer.apple.com/qa/qa2007/qa1521.html */
150       [backend_osx->context setValues:&sw forParameter: NSOpenGLCPSwapInterval];
151 
152       CLUTTER_NOTE (BACKEND, "Context was created");
153     }
154 
155   [backend_osx->context makeCurrentContext];
156 
157   CLUTTER_BACKEND_CLASS (clutter_backend_osx_parent_class)->create_context (backend,
158                                                                             error);
159 
160   CLUTTER_OSX_POOL_RELEASE();
161 
162   return TRUE;
163 }
164 
165 static void
clutter_backend_osx_ensure_context(ClutterBackend * backend,ClutterStage * wrapper)166 clutter_backend_osx_ensure_context (ClutterBackend *backend,
167                                     ClutterStage   *wrapper)
168 {
169   ClutterBackendOSX *backend_osx = CLUTTER_BACKEND_OSX (backend);
170 
171   CLUTTER_OSX_POOL_ALLOC();
172 
173   CLUTTER_NOTE (BACKEND, "ensure_context: wrapper=%p", wrapper);
174 
175   if (wrapper)
176     {
177       ClutterStageWindow *impl = _clutter_stage_get_window (wrapper);
178       ClutterStageOSX *stage_osx;
179 
180       g_assert (CLUTTER_IS_STAGE_OSX (impl));
181       stage_osx = CLUTTER_STAGE_OSX (impl);
182 
183       [backend_osx->context clearDrawable];
184       [backend_osx->context setView:stage_osx->view];
185       [backend_osx->context makeCurrentContext];
186     }
187   else
188     {
189       [backend_osx->context clearDrawable];
190       [NSOpenGLContext clearCurrentContext];
191     }
192 
193   CLUTTER_OSX_POOL_RELEASE();
194 }
195 
196 /*************************************************************************/
197 
198 static void
clutter_backend_osx_init(ClutterBackendOSX * backend_osx)199 clutter_backend_osx_init (ClutterBackendOSX *backend_osx)
200 {
201   const ProcessSerialNumber psn = { 0, kCurrentProcess };
202 
203   backend_osx->context = nil;
204   backend_osx->pixel_format = nil;
205 
206   /* Bring our app to foreground, background apps don't appear in dock or
207    * accept keyboard focus.
208    */
209   TransformProcessType (&psn, kProcessTransformToForegroundApplication);
210 
211   /* Also raise our app to front, otherwise our window will remain under the
212    * terminal.
213    */
214   SetFrontProcess (&psn);
215 
216   [NSApplication sharedApplication];
217 }
218 
219 static void
clutter_backend_osx_dispose(GObject * object)220 clutter_backend_osx_dispose (GObject *object)
221 {
222   ClutterBackendOSX *self = CLUTTER_BACKEND_OSX (object);
223 
224   [self->context release];
225   self->context = NULL;
226 
227   [self->pixel_format release];
228   self->pixel_format = NULL;
229 
230   G_OBJECT_CLASS (clutter_backend_osx_parent_class)->dispose (object);
231 }
232 
233 static void
clutter_backend_osx_class_init(ClutterBackendOSXClass * klass)234 clutter_backend_osx_class_init (ClutterBackendOSXClass *klass)
235 {
236   GObjectClass *object_class = G_OBJECT_CLASS (klass);
237   ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass);
238 
239   object_class->dispose = clutter_backend_osx_dispose;
240 
241   backend_class->stage_window_type = CLUTTER_TYPE_STAGE_OSX;
242 
243   backend_class->post_parse         = clutter_backend_osx_post_parse;
244   backend_class->get_features       = clutter_backend_osx_get_features;
245   backend_class->create_context     = clutter_backend_osx_create_context;
246   backend_class->ensure_context     = clutter_backend_osx_ensure_context;
247 }
248 
249 ClutterBackend *
clutter_backend_osx_new(void)250 clutter_backend_osx_new (void)
251 {
252   return g_object_new (CLUTTER_TYPE_BACKEND_OSX, NULL);
253 }
254