svn update: 48958 (latest:48959)
[framework/uifw/ecore.git] / src / lib / ecore_evas / ecore_evas_cocoa.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #ifdef BUILD_ECORE_EVAS_COCOA
10 #import <Cocoa/Cocoa.h>
11 #endif
12
13 #include "Ecore.h"
14 #include "ecore_private.h"
15 #include "Ecore_Input.h"
16 #include "Ecore_Input_Evas.h"
17
18 #include "ecore_evas_private.h"
19 #include "Ecore_Evas.h"
20
21 #ifdef BUILD_ECORE_EVAS_COCOA
22 #include "Ecore_Cocoa.h"
23 #include "Evas_Engine_Quartz.h"
24
25 // FIXME: this engine has lots of problems. only 1 window at a time, drawRect looks wrong, doesnt handle resizes and more
26
27 static int                      _ecore_evas_init_count = 0;
28 static Ecore_Evas               *ecore_evases = NULL;
29 static Ecore_Event_Handler      *ecore_evas_event_handlers[4] = {
30    NULL, NULL, NULL, NULL
31 };
32 static Ecore_Idle_Enterer       *ecore_evas_idle_enterer = NULL;
33 static Ecore_Poller             *ecore_evas_event = NULL;
34
35 static const char               *ecore_evas_cocoa_default = "EFL Cocoa";
36
37 @interface EvasView : NSView
38 {
39         CGContextRef ctx;
40 }
41 @end
42
43 static EvasView * evas_view;
44 static NSWindow * main_window;
45
46 @implementation EvasView
47
48 - (id) init
49 {
50    self = [super init];
51    if (self != nil)
52    {
53       ctx = NULL;
54    }
55    return self;
56 }
57
58 - (void)drawRect:(NSRect)rect
59 {
60    if(ctx != NULL)
61    {
62       Ecore_Evas  *ee;
63
64       EINA_INLIST_FOREACH(ecore_evases, ee)
65       {
66          if (ee->visible)
67             evas_damage_rectangle_add(ee->evas, 0, 0, 400, 400);
68       }
69       return;
70    }
71
72    ctx = [[NSGraphicsContext currentContext] graphicsPort];
73    CGContextRetain(ctx);
74 }
75
76 - (CGContextRef)context
77 {
78    return ctx;
79 }
80
81 @end
82
83 static Ecore_Evas *
84 _ecore_evas_cocoa_match(void)
85 {
86    return ecore_evases;
87 }
88
89 static int
90 _ecore_evas_cocoa_event_got_focus(void *data __UNUSED__, int type __UNUSED__, void *event)
91 {
92    Ecore_Evas                   *ee;
93
94    ee = _ecore_evas_cocoa_match();
95
96    if (!ee) return 1;
97    ee->prop.focused = 1;
98
99    return 0;
100 }
101
102 static int
103 _ecore_evas_cocoa_event_lost_focus(void *data __UNUSED__, int type __UNUSED__, void *event)
104 {
105    Ecore_Evas                   *ee;
106
107    ee = _ecore_evas_cocoa_match();
108
109    if (!ee) return 1;
110    ee->prop.focused = 0;
111
112    return 0;
113 }
114
115 static int
116 _ecore_evas_cocoa_event_video_resize(void *data __UNUSED__, int type __UNUSED__, void *event)
117 {
118    /*Ecore_Cocoa_Event_Video_Resize *e;
119    Ecore_Evas                   *ee;
120
121    e = event;
122    ee = _ecore_evas_cocoa_match();
123
124    if (!ee) return 1; // pass on event
125    evas_output_size_set(ee->evas, e->w, e->h);
126
127    return 0;*/
128 }
129
130 static int
131 _ecore_evas_cocoa_event_video_expose(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
132 {
133    Ecore_Evas                   *ee;
134    int                          w;
135    int                          h;
136
137    ee = _ecore_evas_cocoa_match();
138
139    if (!ee) return 1;
140    evas_output_size_get(ee->evas, &w, &h);
141    evas_damage_rectangle_add(ee->evas, 0, 0, w, h);
142
143    return 0;
144 }
145
146 static int
147 _ecore_evas_idle_enter(void *data __UNUSED__)
148 {
149    Ecore_Evas  *ee;
150    double       t1 = 0.;
151    double       t2 = 0.;
152
153    EINA_INLIST_FOREACH(ecore_evases, ee)
154    {
155       if (ee->visible)
156          evas_render(ee->evas);
157       else
158          evas_norender(ee->evas);
159    }
160
161    return 1;
162 }
163
164 static int
165 _ecore_evas_cocoa_event(void *data)
166 {
167    ecore_cocoa_feed_events();
168
169    return 1;
170 }
171
172 static int
173 _ecore_evas_cocoa_init(int w, int h)
174 {
175    _ecore_evas_init_count++;
176    if (_ecore_evas_init_count > 1) return _ecore_evas_init_count;
177
178    ecore_event_evas_init();
179
180    ecore_evas_idle_enterer = ecore_idle_enterer_add(_ecore_evas_idle_enter, NULL);
181    ecore_evas_event = ecore_poller_add(ECORE_POLLER_CORE, 1, ecore_evas_event, NULL);
182    ecore_poller_poll_interval_set(ECORE_POLLER_CORE, 0.006);
183
184    ecore_evas_event_handlers[0] = ecore_event_handler_add(ECORE_COCOA_EVENT_GOT_FOCUS, _ecore_evas_cocoa_event_got_focus, NULL);
185    ecore_evas_event_handlers[1] = ecore_event_handler_add(ECORE_COCOA_EVENT_LOST_FOCUS, _ecore_evas_cocoa_event_lost_focus, NULL);
186    ecore_evas_event_handlers[2] = ecore_event_handler_add(ECORE_COCOA_EVENT_RESIZE, _ecore_evas_cocoa_event_video_resize, NULL);
187    ecore_evas_event_handlers[3] = ecore_event_handler_add(ECORE_COCOA_EVENT_EXPOSE, _ecore_evas_cocoa_event_video_expose, NULL);
188
189    return _ecore_evas_init_count;
190 }
191
192 static int
193 _ecore_evas_cocoa_shutdown(void)
194 {
195    _ecore_evas_init_count--;
196    if (_ecore_evas_init_count == 0)
197    {
198       int i;
199
200       while (ecore_evases) _ecore_evas_free(ecore_evases);
201
202       for (i = 0; i < sizeof (ecore_evas_event_handlers) / sizeof (Ecore_Event_Handler*); i++)
203          ecore_event_handler_del(ecore_evas_event_handlers[i]);
204       ecore_event_evas_shutdown();
205       ecore_idle_enterer_del(ecore_evas_idle_enterer);
206       ecore_evas_idle_enterer = NULL;
207       ecore_poller_del(ecore_evas_event);
208       ecore_evas_event = NULL;
209
210       ecore_event_evas_shutdown();
211    }
212    if (_ecore_evas_init_count < 0) _ecore_evas_init_count = 0;
213    return _ecore_evas_init_count;
214 }
215
216 static void
217 _ecore_evas_cocoa_free(Ecore_Evas *ee)
218 {
219    ecore_evases = (Ecore_Evas *) eina_inlist_remove(EINA_INLIST_GET(ecore_evases), EINA_INLIST_GET(ee));
220    ecore_event_window_unregister(0);
221    _ecore_evas_cocoa_shutdown();
222    ecore_cocoa_shutdown();
223 }
224
225 static void
226 _ecore_evas_resize(Ecore_Evas *ee, int w, int h)
227 {
228    if ((w == ee->w) && (h == ee->h)) return;
229    ee->w = w;
230    ee->h = h;
231
232    evas_output_size_set(ee->evas, ee->w, ee->h);
233    evas_output_viewport_set(ee->evas, 0, 0, ee->w, ee->h);
234    evas_damage_rectangle_add(ee->evas, 0, 0, ee->w, ee->h);
235
236    if (ee->func.fn_resize) ee->func.fn_resize(ee);
237 }
238
239 static void
240 _ecore_evas_move_resize(Ecore_Evas *ee, int x __UNUSED__, int y __UNUSED__, int w, int h)
241 {
242    if ((w == ee->w) && (h == ee->h)) return;
243    ee->w = w;
244    ee->h = h;
245
246    evas_output_size_set(ee->evas, ee->w, ee->h);
247    evas_output_viewport_set(ee->evas, 0, 0, ee->w, ee->h);
248    evas_damage_rectangle_add(ee->evas, 0, 0, ee->w, ee->h);
249
250    if (ee->func.fn_resize) ee->func.fn_resize(ee);
251 }
252
253 static void
254 _ecore_evas_object_cursor_del(void *data, Evas *e, Evas_Object *obj, void *event_info)
255 {
256    Ecore_Evas *ee;
257
258    ee = data;
259    if (ee)
260      ee->prop.cursor.object = NULL;
261 }
262
263 static void
264 _ecore_evas_object_cursor_set(Ecore_Evas *ee, Evas_Object *obj, int layer, int hot_x, int hot_y)
265 {
266    int x, y;
267
268    if (ee->prop.cursor.object) evas_object_del(ee->prop.cursor.object);
269
270    if (obj == NULL)
271    {
272       ee->prop.cursor.object = NULL;
273       ee->prop.cursor.layer = 0;
274       ee->prop.cursor.hot.x = 0;
275       ee->prop.cursor.hot.y = 0;
276       return;
277    }
278
279    ee->prop.cursor.object = obj;
280    ee->prop.cursor.layer = layer;
281    ee->prop.cursor.hot.x = hot_x;
282    ee->prop.cursor.hot.y = hot_y;
283
284    evas_pointer_output_xy_get(ee->evas, &x, &y);
285    evas_object_layer_set(ee->prop.cursor.object, ee->prop.cursor.layer);
286    evas_object_move(ee->prop.cursor.object,
287                     x - ee->prop.cursor.hot.x,
288                     y - ee->prop.cursor.hot.y);
289
290    evas_object_pass_events_set(ee->prop.cursor.object, 1);
291
292    if (evas_pointer_inside_get(ee->evas))
293       evas_object_show(ee->prop.cursor.object);
294
295    evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _ecore_evas_object_cursor_del, ee);
296 }
297
298 static Ecore_Evas_Engine_Func _ecore_cocoa_engine_func =
299 {
300    _ecore_evas_cocoa_free,
301    NULL,
302    NULL,
303    NULL,
304    NULL,
305    NULL,
306    NULL,
307    NULL,
308    NULL,
309    NULL,
310    NULL,
311    NULL,
312    NULL,
313    NULL,
314    NULL,
315    NULL,
316    NULL,
317    _ecore_evas_resize,
318    _ecore_evas_move_resize,
319    NULL,
320    NULL,
321    NULL,
322    NULL,
323    NULL,
324    NULL,
325    NULL,
326    NULL,
327    NULL,
328    NULL,
329    NULL,
330    NULL,
331    NULL,
332    _ecore_evas_object_cursor_set,
333    NULL,
334    NULL,
335    NULL,
336    NULL,
337    NULL,
338    NULL,
339    NULL,
340    NULL,
341    NULL,
342    NULL,
343    NULL, //transparent
344      
345      NULL // render
346 };
347 #endif
348
349 EAPI Ecore_Evas *
350 ecore_evas_cocoa_new(const char* name, int w, int h)
351 {
352 #ifdef BUILD_ECORE_EVAS_COCOA
353    Evas_Engine_Info_Quartz *einfo;
354    Ecore_Evas           *ee;
355    int                  rmethod;
356
357    if (!name)
358      name = ecore_evas_cocoa_default;
359
360    rmethod = evas_render_method_lookup("quartz");
361    if (!rmethod) return NULL;
362
363    if (!ecore_cocoa_init(name)) return NULL;
364
365    ee = calloc(1, sizeof(Ecore_Evas));
366    if (!ee)
367      goto shutdown_ecore_cocoa;
368
369    ECORE_MAGIC_SET(ee, ECORE_MAGIC_EVAS);
370
371    _ecore_evas_cocoa_init(w, h);
372
373    ecore_event_window_register(0, ee, ee->evas, _ecore_evas_mouse_move_process);
374
375    ee->engine.func = (Ecore_Evas_Engine_Func *)&_ecore_cocoa_engine_func;
376
377    ee->driver = "quartz";
378    if (name) ee->name = strdup(name);
379    if (!ee->name)
380      goto free_ee;
381
382    if (w < 1) w = 1;
383    if (h < 1) h = 1;
384    ee->visible = 1;
385    ee->w = w;
386    ee->h = h;
387
388    ee->prop.max.w = 0;
389    ee->prop.max.h = 0;
390    ee->prop.layer = 0;
391    ee->prop.focused = 1;
392    ee->prop.borderless = 1;
393    ee->prop.override = 1;
394    ee->prop.maximized = 1;
395    ee->prop.withdrawn = 0;
396    ee->prop.sticky = 0;
397
398    // init evas here
399    ee->evas = evas_new();
400    if (!ee->evas)
401      goto free_name;
402    evas_data_attach_set(ee->evas, ee);
403    evas_output_method_set(ee->evas, rmethod);
404
405    // Set up the Cocoa runtime
406    [[NSAutoreleasePool alloc] init];
407    [NSApplication sharedApplication];
408
409    // Register ourselves as a full-fledged Cocoa app, instead of a NSUIElement.
410    // This gives benefits like window focus and a dock icon!
411    ProcessSerialNumber psn = { 0, kCurrentProcess };
412    TransformProcessType (&psn, kProcessTransformToForegroundApplication);
413
414    [NSApp finishLaunching];
415
416    // Create our main window, and embed an EvasView in it
417    main_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,w,h) styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask | NSMiniaturizableWindowMask) backing:NSBackingStoreBuffered defer:NO screen:nil];
418    /* FIXME: manage the case where main_window is NULL witht a goto free_evas; */
419    [main_window makeKeyAndOrderFront:NSApp];
420    [main_window setTitle:[NSString stringWithUTF8String:name]];
421    [main_window makeMainWindow];
422    [main_window setAcceptsMouseMovedEvents:YES];
423    [NSApp activateIgnoringOtherApps:YES];
424
425    evas_view = [[EvasView alloc] initWithFrame:NSMakeRect(0,0,w,h)];
426    [[main_window contentView] addSubview:evas_view];
427
428    // drawRect: must be run at least once, to make sure we've set ctx
429    [evas_view display];
430
431    evas_output_size_set(ee->evas, w, h);
432    evas_output_viewport_set(ee->evas, 0, 0, w, h);
433
434    einfo = (Evas_Engine_Info_Quartz*) evas_engine_info_get(ee->evas);
435    if (!einfo)
436      goto free_window;
437
438    einfo->info.context = [[evas_view context] retain];
439    evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo);
440
441    ecore_evases = (Ecore_Evas *) eina_inlist_prepend(EINA_INLIST_GET(ecore_evases), EINA_INLIST_GET(ee));
442
443    evas_event_feed_mouse_in(ee->evas, (unsigned int)((unsigned long long)(ecore_time_get() * 1000.0) & 0xffffffff), NULL);
444    evas_focus_in(ee->evas);
445
446    return ee;
447
448  free_window:
449    /* FIXME: free window here */
450  free_evas:
451    free(ee->evas);
452  free_name:
453    free(ee->name);
454  free_ee:
455    _ecore_evas_cocoa_shutdown();
456    free(ee);
457  shutdown_ecore_cocoa:
458    ecore_cocoa_shutdown();
459
460    return NULL;
461 #else
462    ERR("OUTCH name='%s' size=%dx%d!", name ? name : "", w, h);
463    return NULL;
464 #endif
465 }