sgl

view src/wsys_cocoa.m @ 33:46e90f9c1e0f

added most of the missing events in the cocoa module
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 05 Jul 2011 06:19:31 +0300
parents fc2dba4c5a5f
children
line source
1 /* SimplyGL window system module for Cocoa */
2 /* mac-framework: -framework Cocoa */
4 #include "config.h"
6 #ifdef USE_WSYS_MODULE_COCOA
8 #import <Cocoa/Cocoa.h>
9 #include "sgl.h"
10 #include "wsys.h"
11 #include "log.h"
13 @interface OpenGLView : NSOpenGLView
14 {
15 int foo;
16 }
18 -(id) initWithFrame: (NSRect) frame pixelFormat: (NSOpenGLPixelFormat*) pf;
20 -(void) drawRect: (NSRect) rect;
21 -(void) reshape;
22 -(void) keyDown: (NSEvent*) ev;
23 -(void) keyUp: (NSEvent*) ev;
24 -(void) mouseDown: (NSEvent*) ev;
25 -(void) mouseUp: (NSEvent*) ev;
26 -(void) rightMouseDown: (NSEvent*) ev;
27 -(void) rightMouseUp: (NSEvent*) ev;
28 -(void) otherMouseDown: (NSEvent*) ev;
29 -(void) otherMouseUp: (NSEvent*) ev;
30 -(void) mouseDragged: (NSEvent*) ev;
31 -(void) rightMouseDragged: (NSEvent*) ev;
32 -(void) otherMouseDragged: (NSEvent*) ev;
34 -(BOOL) acceptsFirstResponder;
35 @end
38 @interface AppDelegate : NSObject
39 {
40 int foo;
41 }
43 -(void) applicationWillFinishLaunching: (NSNotification*) notification;
44 -(void) applicationDidFinishLaunching: (NSNotification*) notification;
46 -(BOOL) applicationShouldTerminate: (NSApplication*) app;
47 -(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) app;
48 -(void) applicationWillTerminate: (NSNotification*) notification;
49 @end
51 struct window;
53 @interface WinDelegate : NSObject <NSWindowDelegate>
54 {
55 @public
56 struct window *win;
57 }
58 -(id) init;
59 -(void) dealloc;
61 -(void) windowDidExpose: (NSNotification*) notification;
62 -(void) windowDidResize: (NSNotification*) notification;
63 -(BOOL) windowShouldClose: (id) win;
64 -(void) windowWillClose: (NSNotification*) notification;
65 @end
68 struct window {
69 int wid;
70 int width, height;
71 NSWindow *win;
72 OpenGLView *view;
73 NSOpenGLContext *ctx;
74 int needs_redisplay;
75 struct window *next;
76 };
79 static int init(void);
80 static void shutdown(void);
82 /* video mode switching */
83 static int set_vidmode(int xsz, int ysz);
84 static int get_vidmode(int *xsz, int *ysz);
86 /* create/destroy windows */
87 static int create_window(int xsz, int ysz, unsigned int flags);
88 static void close_window(int wid);
90 /* window management */
91 static int set_active(int wid);
92 static struct window *find_window(int wid);
93 static int activate_window(struct window *win);
94 static int set_title(const char *str);
95 static void redisplay(void);
96 static void swap_buffers(void);
98 static int get_modifiers(void);
100 /* event handling and friends */
101 static void set_event(int idx, int enable);
102 static int process_events(void);
104 static void select_event_window(NSEvent *ev);
105 static void handle_key(NSEvent *ev, int state);
106 static void handle_mouse(NSEvent *ev, int state);
107 static void handle_motion(NSEvent *ev);
109 static void fill_attr(NSOpenGLPixelFormatAttribute *attr, unsigned int flags);
112 static struct wsys_module ws = {
113 "cocoa", 0,
114 init,
115 shutdown,
116 set_vidmode,
117 get_vidmode,
118 create_window,
119 close_window,
120 set_active,
121 set_title,
122 redisplay,
123 swap_buffers,
124 get_modifiers,
125 set_event,
126 process_events,
127 0
128 };
130 static struct window *winlist, *active_win;
131 static int quit_main_loop;
133 static NSAutoreleasePool *global_pool;
135 void sgl_register_cocoa(void)
136 {
137 sgl_register_module(&ws);
138 }
141 @implementation OpenGLView
143 -(id) initWithFrame: (NSRect) frame pixelFormat: (NSOpenGLPixelFormat*) pf
144 {
145 self = [super initWithFrame: frame pixelFormat: pf];
146 return self;
147 }
149 -(void) drawRect: (NSRect) rect
150 {
151 sgl_display_callback_t func = sgl_get_callback(SGL_DISPLAY);
152 if(func) {
153 func();
154 }
155 }
157 -(void) reshape
158 {
159 NSSize sz;
160 sgl_reshape_callback_t func;
162 sz = [self bounds].size;
164 if((func = sgl_get_callback(SGL_RESHAPE)) && (sz.width != active_win->width ||
165 sz.height != active_win->height)) {
166 active_win->width = sz.width;
167 active_win->height = sz.height;
168 func(sz.width, sz.height);
169 }
170 }
172 -(void) keyDown: (NSEvent*) ev
173 {
174 handle_key(ev, 1);
175 }
177 -(void) keyUp: (NSEvent*) ev
178 {
179 handle_key(ev, 0);
180 }
182 -(void) mouseDown: (NSEvent*) ev
183 {
184 handle_mouse(ev, 1);
185 }
187 -(void) mouseUp: (NSEvent*) ev
188 {
189 handle_mouse(ev, 0);
190 }
192 -(void) rightMouseDown: (NSEvent*) ev
193 {
194 handle_mouse(ev, 1);
195 }
197 -(void) rightMouseUp: (NSEvent*) ev
198 {
199 handle_mouse(ev, 0);
200 }
202 -(void) otherMouseDown: (NSEvent*) ev
203 {
204 handle_mouse(ev, 1);
205 }
207 -(void) otherMouseUp: (NSEvent*) ev
208 {
209 handle_mouse(ev, 0);
210 }
212 -(void) mouseDragged: (NSEvent*) ev
213 {
214 handle_motion(ev);
215 }
217 -(void) rightMouseDragged: (NSEvent*) ev
218 {
219 handle_motion(ev);
220 }
222 -(void) otherMouseDragged: (NSEvent*) ev
223 {
224 handle_motion(ev);
225 }
228 -(BOOL) acceptsFirstResponder
229 {
230 return YES;
231 }
232 @end
234 @implementation AppDelegate
235 -(void) applicationWillFinishLaunching: (NSNotification*) notification
236 {
237 }
239 -(void) applicationDidFinishLaunching: (NSNotification*) notification
240 {
241 }
243 -(BOOL) applicationShouldTerminate: (NSApplication*) app
244 {
245 return NSTerminateNow;
246 }
248 -(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) app
249 {
250 return YES;
251 }
253 -(void) applicationWillTerminate: (NSNotification*) notification
254 {
255 /*[NSApp setDelegate: nil];
256 [global_pool drain];*/
257 }
258 @end
260 @implementation WinDelegate
261 -(id) init
262 {
263 self = [super init];
264 return self;
265 }
267 -(void) dealloc
268 {
269 [super dealloc];
270 }
272 -(void) windowDidExpose: (NSNotification*) notification
273 {
274 }
276 -(void) windowDidResize: (NSNotification*) notification
277 {
278 }
280 -(BOOL) windowShouldClose: (id) win
281 {
282 assert(self->win);
283 close_window(self->win->wid);
284 return YES;
285 }
287 -(void) windowWillClose: (NSNotification*) notification
288 {
289 /*[NSApp terminate: nil];*/
290 }
291 @end
293 static int init(void)
294 {
295 AppDelegate *delegate;
297 global_pool = [[NSAutoreleasePool alloc] init];
299 [NSApplication sharedApplication];
301 delegate = [[AppDelegate alloc] init];
302 [NSApp setDelegate: delegate];
303 return 0;
304 }
306 static void shutdown(void)
307 {
308 while(winlist) {
309 close_window(winlist->wid);
310 }
312 quit_main_loop = 1;
313 [NSApp terminate: nil];
314 }
317 /* video mode switching */
318 static int set_vidmode(int xsz, int ysz)
319 {
320 return 0; /* TODO */
321 }
323 static int get_vidmode(int *xsz, int *ysz)
324 {
325 return 0; /* TODO */
326 }
329 /* create/destroy windows */
330 static int create_window(int xsz, int ysz, unsigned int flags)
331 {
332 NSAutoreleasePool *pool;
333 WinDelegate *delegate;
334 NSWindow *nswin;
335 NSRect rect;
336 OpenGLView *view;
337 NSOpenGLPixelFormat *pf;
338 NSOpenGLPixelFormatAttribute attr[32];
339 unsigned int style;
340 struct window *win;
341 static int next_id = 1;
343 if(!(win = malloc(sizeof *win))) {
344 return -1;
345 }
347 pool = [[NSAutoreleasePool alloc] init];
349 /* create the view */
350 fill_attr(attr, flags);
351 pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes: attr] autorelease];
352 view = [[OpenGLView alloc] initWithFrame: rect pixelFormat: pf];
354 /* create the window and attach the OpenGL view */
355 rect.origin.x = rect.origin.y = 0;
356 rect.size.width = xsz;
357 rect.size.height = ysz;
359 style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
360 NSResizableWindowMask;
362 nswin = [[NSWindow alloc] initWithContentRect: rect styleMask: style
363 backing: NSBackingStoreBuffered defer: YES];
365 delegate = [[WinDelegate alloc] init];
367 [nswin setDelegate: delegate];
368 [nswin setTitle: @"OpenGL/Cocoa"];
369 [nswin setReleasedWhenClosed: YES];
370 [nswin setContentView: view];
371 [nswin makeFirstResponder: view];
372 [nswin makeKeyAndOrderFront: nil];
373 [view release];
375 win->win = nswin;
376 win->view = view;
377 win->ctx = [view openGLContext];
378 win->wid = next_id++;
379 win->needs_redisplay = 1;
380 win->next = winlist;
381 winlist = win;
383 delegate->win = win;
385 if(!active_win) {
386 activate_window(win);
387 }
389 [pool drain];
390 return win->wid;
391 }
393 static void close_window(int wid)
394 {
395 struct window *win, *prev, dummy;
396 sgl_close_callback_t close_func;
398 dummy.next = win = winlist;
399 prev = &dummy;
401 while(win) {
402 if(win->wid == wid) {
403 if((close_func = sgl_get_callback(SGL_CLOSE))) {
404 close_func(wid);
405 }
406 [win->win close];
408 prev->next = win->next;
410 if(!dummy.next) {
411 winlist = 0;
412 }
414 if(active_win == win) {
415 activate_window(winlist);
416 }
417 free(win);
418 break;
419 }
420 prev = win;
421 win = win->next;
422 }
424 if(!dummy.next) {
425 winlist = 0;
426 shutdown();
427 }
428 }
431 /* window management */
432 static int set_active(int wid)
433 {
434 struct window *win = find_window(wid);
435 return activate_window(win);
436 }
438 static struct window *find_window(int wid)
439 {
440 struct window *win = winlist;
442 while(win) {
443 if(win->wid == wid) {
444 return win;
445 }
446 win = win->next;
447 }
448 return 0;
449 }
451 static int activate_window(struct window *win)
452 {
453 if(!win) {
454 return -1;
455 }
456 [win->ctx makeCurrentContext];
457 active_win = win;
458 return 0;
459 }
461 static int set_title(const char *str)
462 {
463 NSString *nsstr;
465 nsstr = [[NSString alloc] initWithCString: str encoding: NSASCIIStringEncoding];
466 [active_win->win setTitle: nsstr];
467 [nsstr release];
468 return 0;
469 }
471 static void redisplay(void)
472 {
473 active_win->needs_redisplay = 1;
474 }
476 static void swap_buffers(void)
477 {
478 [active_win->ctx flushBuffer];
479 }
482 static int get_modifiers(void)
483 {
484 unsigned int nsmod = [NSEvent modifierFlags];
485 unsigned int mod = 0;
487 if(nsmod & NSShiftKeyMask) {
488 mod |= SGL_MOD_SHIFT;
489 }
490 if(nsmod & NSControlKeyMask) {
491 mod |= SGL_MOD_CONTROL;
492 }
493 if(nsmod & NSAlternateKeyMask) {
494 mod |= SGL_MOD_ALT;
495 }
496 return mod;
497 }
500 /* event handling and friends */
501 static void set_event(int idx, int enable)
502 {
503 }
505 static int process_events(void)
506 {
507 NSAutoreleasePool *pool;
508 NSRunLoop *runloop;
509 NSDate *block, *nonblock, *limdate;
511 sgl_idle_callback_t idle;
512 sgl_display_callback_t disp;
513 struct window *win;
515 pool = [[NSAutoreleasePool alloc] init];
517 idle = sgl_get_callback(SGL_IDLE);
518 disp = sgl_get_callback(SGL_DISPLAY);
520 win = winlist;
521 while(win) {
522 if(win->needs_redisplay && disp) {
523 activate_window(win);
524 disp();
525 win->needs_redisplay = 0;
526 }
527 win = win->next;
528 }
530 runloop = [[NSRunLoop currentRunLoop] retain];
531 block = [runloop limitDateForMode: NSDefaultRunLoopMode];
532 nonblock = [[NSDate distantPast] retain];
533 limdate = idle ? nonblock : block;
535 while(!quit_main_loop) {
536 NSEvent *ev = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: limdate
537 inMode: NSDefaultRunLoopMode dequeue: YES];
538 if(!ev) break;
540 [NSApp sendEvent: ev];
541 if(limdate == block) {
542 limdate = nonblock;
543 }
544 }
546 if(idle) {
547 idle();
548 }
550 [runloop release];
551 [pool drain];
553 /*sgl_log("%s returning: %d\n", __func__, quit_main_loop ? -1 : 0);*/
554 return quit_main_loop ? -1 : 0;
555 }
557 static void select_event_window(NSEvent *ev)
558 {
559 NSWindow *nswin;
560 WinDelegate *del;
561 struct window *win;
563 if(!ev || !(nswin = [ev window])) {
564 sgl_log("%s failed\n", __func__);
565 return;
566 }
567 del = [nswin delegate];
568 win = del->win;
570 activate_window(win);
571 }
573 static void handle_key(NSEvent *ev, int state)
574 {
575 NSString *str;
576 sgl_keyboard_callback_t func = sgl_get_callback(SGL_KEYBOARD);
578 if(func) {
579 str = [ev characters];
580 if([str length]) {
581 unichar c = [str characterAtIndex: 0];
583 select_event_window(ev);
584 func(c, state);
585 }
586 }
587 }
589 static void handle_mouse(NSEvent *ev, int state)
590 {
591 int bn;
592 NSPoint pt;
593 sgl_mouse_callback_t func = sgl_get_callback(SGL_MOUSE);
595 if(func) {
596 bn = [ev buttonNumber];
597 if(bn == 2) {
598 bn = 1;
599 } else if(bn == 1) {
600 bn = 2;
601 }
602 pt = [ev locationInWindow];
604 select_event_window(ev);
605 func(0, bn, state, pt.x, pt.y - 1);
606 }
607 }
609 static void handle_motion(NSEvent *ev)
610 {
611 NSPoint pt;
612 sgl_motion_callback_t func = sgl_get_callback(SGL_MOTION);
614 if(func) {
615 pt = [ev locationInWindow];
617 select_event_window(ev);
618 func(0, pt.x, pt.y - 1);
619 }
620 }
622 static void fill_attr(NSOpenGLPixelFormatAttribute *attr, unsigned int flags)
623 {
624 int i = 0;
626 /* this is very important. makes pixelformat selection behave like GLX
627 * where any non-zero value will denote "choose highest possible". This
628 * is pretty much what we intend, as the user doesn't actually pass any
629 * of these numbers.
630 */
631 attr[i++] = NSOpenGLPFAMaximumPolicy;
633 attr[i++] = NSOpenGLPFAColorSize;
634 attr[i++] = 1;
636 if(flags & SGL_DOUBLE) {
637 attr[i++] = NSOpenGLPFADoubleBuffer;
638 }
639 if(flags & SGL_DEPTH) {
640 attr[i++] = NSOpenGLPFADepthSize;
641 attr[i++] = 1;
642 }
643 if(flags & SGL_STENCIL) {
644 attr[i++] = NSOpenGLPFAStencilSize;
645 attr[i++] = 8; /* max-policy has no effect on stencil selection */
646 }
647 if(flags & SGL_STEREO) {
648 attr[i++] = NSOpenGLPFAStereo;
649 }
650 if(flags & SGL_MULTISAMPLE) {
651 attr[i++] = NSOpenGLPFASampleBuffers;
652 attr[i++] = 1;
653 attr[i++] = NSOpenGLPFASamples;
654 attr[i++] = 4; /* TODO don't hardcode, query */
655 }
656 attr[i++] = 0;
657 }
659 #else
660 int sgl_wsys_cocoa_silence_the_fucking_empty_file_warnings;
661 #endif /* USE_WSYS_MODULE_COCOA */