nuclear@19: /* SimplyGL window system module for Cocoa */ nuclear@13: /* mac-framework: -framework Cocoa */ nuclear@13: nuclear@13: #include "config.h" nuclear@13: nuclear@13: #ifdef USE_WSYS_MODULE_COCOA nuclear@13: nuclear@15: #import nuclear@13: #include "sgl.h" nuclear@15: #include "wsys.h" nuclear@31: #include "log.h" nuclear@31: nuclear@15: @interface OpenGLView : NSOpenGLView nuclear@13: { nuclear@15: int foo; nuclear@13: } nuclear@13: nuclear@17: -(id) initWithFrame: (NSRect) frame pixelFormat: (NSOpenGLPixelFormat*) pf; nuclear@13: nuclear@13: -(void) drawRect: (NSRect) rect; nuclear@13: -(void) reshape; nuclear@33: -(void) keyDown: (NSEvent*) ev; nuclear@13: -(void) keyUp: (NSEvent*) ev; nuclear@13: -(void) mouseDown: (NSEvent*) ev; nuclear@13: -(void) mouseUp: (NSEvent*) ev; nuclear@13: -(void) rightMouseDown: (NSEvent*) ev; nuclear@13: -(void) rightMouseUp: (NSEvent*) ev; nuclear@13: -(void) otherMouseDown: (NSEvent*) ev; nuclear@13: -(void) otherMouseUp: (NSEvent*) ev; nuclear@13: -(void) mouseDragged: (NSEvent*) ev; nuclear@13: -(void) rightMouseDragged: (NSEvent*) ev; nuclear@33: -(void) otherMouseDragged: (NSEvent*) ev; nuclear@13: nuclear@13: -(BOOL) acceptsFirstResponder; nuclear@13: @end nuclear@13: nuclear@17: nuclear@17: @interface AppDelegate : NSObject nuclear@16: { nuclear@33: int foo; nuclear@16: } nuclear@31: nuclear@31: -(void) applicationWillFinishLaunching: (NSNotification*) notification; nuclear@31: -(void) applicationDidFinishLaunching: (NSNotification*) notification; nuclear@31: nuclear@17: -(BOOL) applicationShouldTerminate: (NSApplication*) app; nuclear@31: -(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) app; nuclear@31: -(void) applicationWillTerminate: (NSNotification*) notification; nuclear@32: @end nuclear@32: nuclear@32: struct window; nuclear@32: nuclear@32: @interface WinDelegate : NSObject nuclear@32: { nuclear@33: @public nuclear@32: struct window *win; nuclear@32: } nuclear@32: -(id) init; nuclear@32: -(void) dealloc; nuclear@32: nuclear@32: -(void) windowDidExpose: (NSNotification*) notification; nuclear@32: -(void) windowDidResize: (NSNotification*) notification; nuclear@31: -(BOOL) windowShouldClose: (id) win; nuclear@31: -(void) windowWillClose: (NSNotification*) notification; nuclear@16: @end nuclear@16: nuclear@17: nuclear@13: struct window { nuclear@13: int wid; nuclear@15: int width, height; nuclear@13: NSWindow *win; nuclear@15: OpenGLView *view; nuclear@15: NSOpenGLContext *ctx; nuclear@15: int needs_redisplay; nuclear@13: struct window *next; nuclear@13: }; nuclear@13: nuclear@13: nuclear@13: static int init(void); nuclear@13: static void shutdown(void); nuclear@13: nuclear@13: /* video mode switching */ nuclear@13: static int set_vidmode(int xsz, int ysz); nuclear@13: static int get_vidmode(int *xsz, int *ysz); nuclear@13: nuclear@13: /* create/destroy windows */ nuclear@13: static int create_window(int xsz, int ysz, unsigned int flags); nuclear@13: static void close_window(int wid); nuclear@13: nuclear@13: /* window management */ nuclear@13: static int set_active(int wid); nuclear@13: static struct window *find_window(int wid); nuclear@13: static int activate_window(struct window *win); nuclear@13: static int set_title(const char *str); nuclear@13: static void redisplay(void); nuclear@13: static void swap_buffers(void); nuclear@13: nuclear@13: static int get_modifiers(void); nuclear@13: nuclear@13: /* event handling and friends */ nuclear@13: static void set_event(int idx, int enable); nuclear@13: static int process_events(void); nuclear@17: nuclear@33: static void select_event_window(NSEvent *ev); nuclear@33: static void handle_key(NSEvent *ev, int state); nuclear@33: static void handle_mouse(NSEvent *ev, int state); nuclear@33: static void handle_motion(NSEvent *ev); nuclear@33: nuclear@17: static void fill_attr(NSOpenGLPixelFormatAttribute *attr, unsigned int flags); nuclear@13: nuclear@13: nuclear@13: static struct wsys_module ws = { nuclear@13: "cocoa", 0, nuclear@13: init, nuclear@13: shutdown, nuclear@13: set_vidmode, nuclear@13: get_vidmode, nuclear@13: create_window, nuclear@13: close_window, nuclear@13: set_active, nuclear@13: set_title, nuclear@13: redisplay, nuclear@13: swap_buffers, nuclear@13: get_modifiers, nuclear@13: set_event, nuclear@13: process_events, nuclear@13: 0 nuclear@13: }; nuclear@13: nuclear@13: static struct window *winlist, *active_win; nuclear@17: static int quit_main_loop; nuclear@13: nuclear@31: static NSAutoreleasePool *global_pool; nuclear@13: nuclear@13: void sgl_register_cocoa(void) nuclear@13: { nuclear@15: sgl_register_module(&ws); nuclear@13: } nuclear@13: nuclear@13: nuclear@15: @implementation OpenGLView nuclear@16: nuclear@17: -(id) initWithFrame: (NSRect) frame pixelFormat: (NSOpenGLPixelFormat*) pf nuclear@16: { nuclear@17: self = [super initWithFrame: frame pixelFormat: pf]; nuclear@17: return self; nuclear@16: } nuclear@13: nuclear@13: -(void) drawRect: (NSRect) rect nuclear@13: { nuclear@13: sgl_display_callback_t func = sgl_get_callback(SGL_DISPLAY); nuclear@13: if(func) { nuclear@13: func(); nuclear@13: } nuclear@13: } nuclear@13: nuclear@13: -(void) reshape nuclear@13: { nuclear@15: NSSize sz; nuclear@15: sgl_reshape_callback_t func; nuclear@15: nuclear@15: sz = [self bounds].size; nuclear@15: nuclear@15: if((func = sgl_get_callback(SGL_RESHAPE)) && (sz.width != active_win->width || nuclear@15: sz.height != active_win->height)) { nuclear@15: active_win->width = sz.width; nuclear@15: active_win->height = sz.height; nuclear@15: func(sz.width, sz.height); nuclear@13: } nuclear@13: } nuclear@13: nuclear@33: -(void) keyDown: (NSEvent*) ev nuclear@33: { nuclear@33: handle_key(ev, 1); nuclear@33: } nuclear@33: nuclear@33: -(void) keyUp: (NSEvent*) ev nuclear@33: { nuclear@33: handle_key(ev, 0); nuclear@33: } nuclear@33: nuclear@33: -(void) mouseDown: (NSEvent*) ev nuclear@33: { nuclear@33: handle_mouse(ev, 1); nuclear@33: } nuclear@33: nuclear@33: -(void) mouseUp: (NSEvent*) ev nuclear@33: { nuclear@33: handle_mouse(ev, 0); nuclear@33: } nuclear@33: nuclear@33: -(void) rightMouseDown: (NSEvent*) ev nuclear@33: { nuclear@33: handle_mouse(ev, 1); nuclear@33: } nuclear@33: nuclear@33: -(void) rightMouseUp: (NSEvent*) ev nuclear@33: { nuclear@33: handle_mouse(ev, 0); nuclear@33: } nuclear@33: nuclear@33: -(void) otherMouseDown: (NSEvent*) ev nuclear@33: { nuclear@33: handle_mouse(ev, 1); nuclear@33: } nuclear@33: nuclear@33: -(void) otherMouseUp: (NSEvent*) ev nuclear@33: { nuclear@33: handle_mouse(ev, 0); nuclear@33: } nuclear@33: nuclear@33: -(void) mouseDragged: (NSEvent*) ev nuclear@33: { nuclear@33: handle_motion(ev); nuclear@33: } nuclear@33: nuclear@33: -(void) rightMouseDragged: (NSEvent*) ev nuclear@33: { nuclear@33: handle_motion(ev); nuclear@33: } nuclear@33: nuclear@33: -(void) otherMouseDragged: (NSEvent*) ev nuclear@33: { nuclear@33: handle_motion(ev); nuclear@33: } nuclear@33: nuclear@33: nuclear@13: -(BOOL) acceptsFirstResponder nuclear@13: { nuclear@13: return YES; nuclear@13: } nuclear@13: @end nuclear@13: nuclear@17: @implementation AppDelegate nuclear@31: -(void) applicationWillFinishLaunching: (NSNotification*) notification nuclear@31: { nuclear@31: } nuclear@31: nuclear@31: -(void) applicationDidFinishLaunching: (NSNotification*) notification nuclear@31: { nuclear@31: } nuclear@31: nuclear@17: -(BOOL) applicationShouldTerminate: (NSApplication*) app nuclear@17: { nuclear@17: return NSTerminateNow; nuclear@17: } nuclear@17: nuclear@17: -(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) app nuclear@16: { nuclear@16: return YES; nuclear@16: } nuclear@17: nuclear@17: -(void) applicationWillTerminate: (NSNotification*) notification nuclear@17: { nuclear@32: /*[NSApp setDelegate: nil]; nuclear@32: [global_pool drain];*/ nuclear@32: } nuclear@32: @end nuclear@32: nuclear@32: @implementation WinDelegate nuclear@32: -(id) init nuclear@32: { nuclear@32: self = [super init]; nuclear@32: return self; nuclear@32: } nuclear@32: nuclear@32: -(void) dealloc nuclear@32: { nuclear@32: [super dealloc]; nuclear@32: } nuclear@32: nuclear@32: -(void) windowDidExpose: (NSNotification*) notification nuclear@32: { nuclear@32: } nuclear@32: nuclear@32: -(void) windowDidResize: (NSNotification*) notification nuclear@32: { nuclear@17: } nuclear@17: nuclear@17: -(BOOL) windowShouldClose: (id) win nuclear@17: { nuclear@32: assert(self->win); nuclear@32: close_window(self->win->wid); nuclear@17: return YES; nuclear@17: } nuclear@17: nuclear@17: -(void) windowWillClose: (NSNotification*) notification nuclear@17: { nuclear@31: /*[NSApp terminate: nil];*/ nuclear@17: } nuclear@16: @end nuclear@16: nuclear@13: static int init(void) nuclear@13: { nuclear@17: AppDelegate *delegate; nuclear@16: nuclear@31: global_pool = [[NSAutoreleasePool alloc] init]; nuclear@31: nuclear@16: [NSApplication sharedApplication]; nuclear@17: nuclear@17: delegate = [[AppDelegate alloc] init]; nuclear@16: [NSApp setDelegate: delegate]; nuclear@15: return 0; nuclear@13: } nuclear@13: nuclear@13: static void shutdown(void) nuclear@13: { nuclear@13: while(winlist) { nuclear@31: close_window(winlist->wid); nuclear@13: } nuclear@16: nuclear@17: quit_main_loop = 1; nuclear@32: [NSApp terminate: nil]; nuclear@13: } nuclear@13: nuclear@13: nuclear@13: /* video mode switching */ nuclear@13: static int set_vidmode(int xsz, int ysz) nuclear@13: { nuclear@13: return 0; /* TODO */ nuclear@13: } nuclear@13: nuclear@13: static int get_vidmode(int *xsz, int *ysz) nuclear@13: { nuclear@13: return 0; /* TODO */ nuclear@13: } nuclear@13: nuclear@13: nuclear@13: /* create/destroy windows */ nuclear@13: static int create_window(int xsz, int ysz, unsigned int flags) nuclear@13: { nuclear@17: NSAutoreleasePool *pool; nuclear@32: WinDelegate *delegate; nuclear@13: NSWindow *nswin; nuclear@13: NSRect rect; nuclear@15: OpenGLView *view; nuclear@17: NSOpenGLPixelFormat *pf; nuclear@17: NSOpenGLPixelFormatAttribute attr[32]; nuclear@13: unsigned int style; nuclear@15: struct window *win; nuclear@13: static int next_id = 1; nuclear@13: nuclear@13: if(!(win = malloc(sizeof *win))) { nuclear@13: return -1; nuclear@13: } nuclear@13: nuclear@17: pool = [[NSAutoreleasePool alloc] init]; nuclear@17: nuclear@17: /* create the view */ nuclear@17: fill_attr(attr, flags); nuclear@17: pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes: attr] autorelease]; nuclear@17: view = [[OpenGLView alloc] initWithFrame: rect pixelFormat: pf]; nuclear@17: nuclear@13: /* create the window and attach the OpenGL view */ nuclear@15: rect.origin.x = rect.origin.y = 0; nuclear@15: rect.size.width = xsz; nuclear@15: rect.size.height = ysz; nuclear@13: nuclear@15: style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | nuclear@15: NSResizableWindowMask; nuclear@13: nuclear@13: nswin = [[NSWindow alloc] initWithContentRect: rect styleMask: style nuclear@13: backing: NSBackingStoreBuffered defer: YES]; nuclear@17: nuclear@32: delegate = [[WinDelegate alloc] init]; nuclear@32: nuclear@32: [nswin setDelegate: delegate]; nuclear@16: [nswin setTitle: @"OpenGL/Cocoa"]; nuclear@17: [nswin setReleasedWhenClosed: YES]; nuclear@13: [nswin setContentView: view]; nuclear@16: [nswin makeFirstResponder: view]; nuclear@16: [nswin makeKeyAndOrderFront: nil]; nuclear@15: [view release]; nuclear@13: nuclear@13: win->win = nswin; nuclear@13: win->view = view; nuclear@15: win->ctx = [view openGLContext]; nuclear@13: win->wid = next_id++; nuclear@15: win->needs_redisplay = 1; nuclear@13: win->next = winlist; nuclear@13: winlist = win; nuclear@13: nuclear@32: delegate->win = win; nuclear@32: nuclear@15: if(!active_win) { nuclear@15: activate_window(win); nuclear@15: } nuclear@17: nuclear@17: [pool drain]; nuclear@13: return win->wid; nuclear@13: } nuclear@13: nuclear@13: static void close_window(int wid) nuclear@13: { nuclear@13: struct window *win, *prev, dummy; nuclear@13: sgl_close_callback_t close_func; nuclear@13: nuclear@13: dummy.next = win = winlist; nuclear@13: prev = &dummy; nuclear@13: nuclear@13: while(win) { nuclear@13: if(win->wid == wid) { nuclear@32: if((close_func = sgl_get_callback(SGL_CLOSE))) { nuclear@13: close_func(wid); nuclear@13: } nuclear@13: [win->win close]; nuclear@13: nuclear@31: prev->next = win->next; nuclear@31: nuclear@31: if(!dummy.next) { nuclear@31: winlist = 0; nuclear@31: } nuclear@31: nuclear@13: if(active_win == win) { nuclear@13: activate_window(winlist); nuclear@13: } nuclear@13: free(win); nuclear@31: break; nuclear@13: } nuclear@13: prev = win; nuclear@13: win = win->next; nuclear@13: } nuclear@31: nuclear@32: if(!dummy.next) { nuclear@32: winlist = 0; nuclear@31: shutdown(); nuclear@31: } nuclear@13: } nuclear@13: nuclear@13: nuclear@13: /* window management */ nuclear@13: static int set_active(int wid) nuclear@13: { nuclear@15: struct window *win = find_window(wid); nuclear@15: return activate_window(win); nuclear@13: } nuclear@13: nuclear@13: static struct window *find_window(int wid) nuclear@13: { nuclear@13: struct window *win = winlist; nuclear@13: nuclear@13: while(win) { nuclear@15: if(win->wid == wid) { nuclear@13: return win; nuclear@13: } nuclear@13: win = win->next; nuclear@13: } nuclear@13: return 0; nuclear@13: } nuclear@13: nuclear@13: static int activate_window(struct window *win) nuclear@13: { nuclear@13: if(!win) { nuclear@13: return -1; nuclear@13: } nuclear@13: [win->ctx makeCurrentContext]; nuclear@13: active_win = win; nuclear@13: return 0; nuclear@13: } nuclear@13: nuclear@13: static int set_title(const char *str) nuclear@13: { nuclear@13: NSString *nsstr; nuclear@13: nuclear@13: nsstr = [[NSString alloc] initWithCString: str encoding: NSASCIIStringEncoding]; nuclear@13: [active_win->win setTitle: nsstr]; nuclear@13: [nsstr release]; nuclear@15: return 0; nuclear@13: } nuclear@13: nuclear@13: static void redisplay(void) nuclear@13: { nuclear@15: active_win->needs_redisplay = 1; nuclear@13: } nuclear@13: nuclear@13: static void swap_buffers(void) nuclear@13: { nuclear@15: [active_win->ctx flushBuffer]; nuclear@13: } nuclear@13: nuclear@13: nuclear@13: static int get_modifiers(void) nuclear@13: { nuclear@33: unsigned int nsmod = [NSEvent modifierFlags]; nuclear@33: unsigned int mod = 0; nuclear@33: nuclear@33: if(nsmod & NSShiftKeyMask) { nuclear@33: mod |= SGL_MOD_SHIFT; nuclear@33: } nuclear@33: if(nsmod & NSControlKeyMask) { nuclear@33: mod |= SGL_MOD_CONTROL; nuclear@33: } nuclear@33: if(nsmod & NSAlternateKeyMask) { nuclear@33: mod |= SGL_MOD_ALT; nuclear@33: } nuclear@33: return mod; nuclear@13: } nuclear@13: nuclear@13: nuclear@13: /* event handling and friends */ nuclear@13: static void set_event(int idx, int enable) nuclear@13: { nuclear@13: } nuclear@13: nuclear@13: static int process_events(void) nuclear@13: { nuclear@13: NSAutoreleasePool *pool; nuclear@17: NSRunLoop *runloop; nuclear@17: NSDate *block, *nonblock, *limdate; nuclear@17: nuclear@14: sgl_idle_callback_t idle; nuclear@15: sgl_display_callback_t disp; nuclear@15: struct window *win; nuclear@15: nuclear@15: pool = [[NSAutoreleasePool alloc] init]; nuclear@16: nuclear@16: idle = sgl_get_callback(SGL_IDLE); nuclear@15: disp = sgl_get_callback(SGL_DISPLAY); nuclear@13: nuclear@15: win = winlist; nuclear@15: while(win) { nuclear@15: if(win->needs_redisplay && disp) { nuclear@15: activate_window(win); nuclear@15: disp(); nuclear@15: win->needs_redisplay = 0; nuclear@15: } nuclear@15: win = win->next; nuclear@15: } nuclear@15: nuclear@17: runloop = [[NSRunLoop currentRunLoop] retain]; nuclear@17: block = [runloop limitDateForMode: NSDefaultRunLoopMode]; nuclear@17: nonblock = [[NSDate distantPast] retain]; nuclear@16: limdate = idle ? nonblock : block; nuclear@15: nuclear@17: while(!quit_main_loop) { nuclear@16: NSEvent *ev = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: limdate nuclear@16: inMode: NSDefaultRunLoopMode dequeue: YES]; nuclear@16: if(!ev) break; nuclear@16: nuclear@16: [NSApp sendEvent: ev]; nuclear@16: if(limdate == block) { nuclear@16: limdate = nonblock; nuclear@13: } nuclear@15: } nuclear@15: nuclear@15: if(idle) { nuclear@15: idle(); nuclear@15: } nuclear@15: nuclear@17: [runloop release]; nuclear@15: [pool drain]; nuclear@31: nuclear@31: /*sgl_log("%s returning: %d\n", __func__, quit_main_loop ? -1 : 0);*/ nuclear@17: return quit_main_loop ? -1 : 0; nuclear@13: } nuclear@13: nuclear@33: static void select_event_window(NSEvent *ev) nuclear@33: { nuclear@33: NSWindow *nswin; nuclear@33: WinDelegate *del; nuclear@33: struct window *win; nuclear@33: nuclear@33: if(!ev || !(nswin = [ev window])) { nuclear@33: sgl_log("%s failed\n", __func__); nuclear@33: return; nuclear@33: } nuclear@33: del = [nswin delegate]; nuclear@33: win = del->win; nuclear@33: nuclear@33: activate_window(win); nuclear@33: } nuclear@33: nuclear@33: static void handle_key(NSEvent *ev, int state) nuclear@33: { nuclear@33: NSString *str; nuclear@33: sgl_keyboard_callback_t func = sgl_get_callback(SGL_KEYBOARD); nuclear@33: nuclear@33: if(func) { nuclear@33: str = [ev characters]; nuclear@33: if([str length]) { nuclear@33: unichar c = [str characterAtIndex: 0]; nuclear@33: nuclear@33: select_event_window(ev); nuclear@33: func(c, state); nuclear@33: } nuclear@33: } nuclear@33: } nuclear@33: nuclear@33: static void handle_mouse(NSEvent *ev, int state) nuclear@33: { nuclear@33: int bn; nuclear@33: NSPoint pt; nuclear@33: sgl_mouse_callback_t func = sgl_get_callback(SGL_MOUSE); nuclear@33: nuclear@33: if(func) { nuclear@33: bn = [ev buttonNumber]; nuclear@33: if(bn == 2) { nuclear@33: bn = 1; nuclear@33: } else if(bn == 1) { nuclear@33: bn = 2; nuclear@33: } nuclear@33: pt = [ev locationInWindow]; nuclear@33: nuclear@33: select_event_window(ev); nuclear@33: func(0, bn, state, pt.x, pt.y - 1); nuclear@33: } nuclear@33: } nuclear@33: nuclear@33: static void handle_motion(NSEvent *ev) nuclear@33: { nuclear@33: NSPoint pt; nuclear@33: sgl_motion_callback_t func = sgl_get_callback(SGL_MOTION); nuclear@33: nuclear@33: if(func) { nuclear@33: pt = [ev locationInWindow]; nuclear@33: nuclear@33: select_event_window(ev); nuclear@33: func(0, pt.x, pt.y - 1); nuclear@33: } nuclear@33: } nuclear@33: nuclear@17: static void fill_attr(NSOpenGLPixelFormatAttribute *attr, unsigned int flags) nuclear@13: { nuclear@17: int i = 0; nuclear@14: nuclear@17: /* this is very important. makes pixelformat selection behave like GLX nuclear@17: * where any non-zero value will denote "choose highest possible". This nuclear@17: * is pretty much what we intend, as the user doesn't actually pass any nuclear@17: * of these numbers. nuclear@17: */ nuclear@17: attr[i++] = NSOpenGLPFAMaximumPolicy; nuclear@17: nuclear@17: attr[i++] = NSOpenGLPFAColorSize; nuclear@17: attr[i++] = 1; nuclear@17: nuclear@17: if(flags & SGL_DOUBLE) { nuclear@17: attr[i++] = NSOpenGLPFADoubleBuffer; nuclear@15: } nuclear@17: if(flags & SGL_DEPTH) { nuclear@17: attr[i++] = NSOpenGLPFADepthSize; nuclear@17: attr[i++] = 1; nuclear@17: } nuclear@17: if(flags & SGL_STENCIL) { nuclear@17: attr[i++] = NSOpenGLPFAStencilSize; nuclear@17: attr[i++] = 8; /* max-policy has no effect on stencil selection */ nuclear@17: } nuclear@17: if(flags & SGL_STEREO) { nuclear@17: attr[i++] = NSOpenGLPFAStereo; nuclear@17: } nuclear@17: if(flags & SGL_MULTISAMPLE) { nuclear@17: attr[i++] = NSOpenGLPFASampleBuffers; nuclear@17: attr[i++] = 1; nuclear@17: attr[i++] = NSOpenGLPFASamples; nuclear@17: attr[i++] = 4; /* TODO don't hardcode, query */ nuclear@17: } nuclear@17: attr[i++] = 0; nuclear@13: } nuclear@13: nuclear@20: #else nuclear@20: int sgl_wsys_cocoa_silence_the_fucking_empty_file_warnings; nuclear@13: #endif /* USE_WSYS_MODULE_COCOA */