/* MiniGLUT - minimal GLUT subset without dependencies Copyright (C) 2020-2023 John Tsiombikas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #import #include "miniglut.h" @interface OpenGLView : NSOpenGLView { int foo; } -(id) initWithFrame: (NSRect) frame pixelFormat: (NSOpenGLPixelFormat*) pf; -(void) drawRect: (NSRect) rect; -(void) reshape; -(void) keyDown: (NSEvent*) ev; -(void) keyUp: (NSEvent*) ev; -(void) mouseDown: (NSEvent*) ev; -(void) mouseUp: (NSEvent*) ev; -(void) rightMouseDown: (NSEvent*) ev; -(void) rightMouseUp: (NSEvent*) ev; -(void) otherMouseDown: (NSEvent*) ev; -(void) otherMouseUp: (NSEvent*) ev; -(void) mouseDragged: (NSEvent*) ev; -(void) rightMouseDragged: (NSEvent*) ev; -(void) otherMouseDragged: (NSEvent*) ev; -(BOOL) acceptsFirstResponder; @end @interface AppDelegate : NSObject { int foo; } -(void) applicationWillFinishLaunching: (NSNotification*) notification; -(void) applicationDidFinishLaunching: (NSNotification*) notification; -(BOOL) applicationShouldTerminate: (NSApplication*) app; -(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) app; -(void) applicationWillTerminate: (NSNotification*) notification; @end struct window; @interface WinDelegate : NSObject { @public struct window *win; } -(id) init; -(void) dealloc; -(void) windowDidExpose: (NSNotification*) notification; -(void) windowDidResize: (NSNotification*) notification; -(BOOL) windowShouldClose: (id) win; -(void) windowWillClose: (NSNotification*) notification; @end static int init(void); static void shutdown(void); /* video mode switching */ static int set_vidmode(int xsz, int ysz); static int get_vidmode(int *xsz, int *ysz); /* create/destroy windows */ static int create_window(int xsz, int ysz, unsigned int flags); static void close_window(void); /* window management */ static int set_title(const char *str); static void redisplay(void); static void swap_buffers(void); static int get_modifiers(void); /* event handling and friends */ static void set_event(int idx, int enable); static int process_events(void); static void select_event_window(NSEvent *ev); static void handle_key(NSEvent *ev, int state); static void handle_mouse(NSEvent *ev, int state); static void handle_motion(NSEvent *ev); static void fill_attr(NSOpenGLPixelFormatAttribute *attr, unsigned int flags); static glut_cb cb_display; static glut_cb cb_idle; static glut_cb_reshape cb_reshape; static glut_cb_state cb_vis, cb_entry; static glut_cb_keyb cb_keydown, cb_keyup; static glut_cb_special cb_skeydown, cb_skeyup; static glut_cb_mouse cb_mouse; static glut_cb_motion cb_motion, cb_passive; static glut_cb_sbmotion cb_sball_motion, cb_sball_rotate; static glut_cb_sbbutton cb_sball_button; static int win_width, win_height; static NSWindow *glwin; static OpenGLView *glview; static NSOpenGLContext *glctx; static int needs_redisplay; static int quit_main_loop; static NSAutoreleasePool *global_pool; void glutInit(int *argc, char **argv) { } @implementation OpenGLView -(id) initWithFrame: (NSRect) frame pixelFormat: (NSOpenGLPixelFormat*) pf { self = [super initWithFrame: frame pixelFormat: pf]; return self; } -(void) drawRect: (NSRect) rect { cb_display(); } -(void) reshape { NSSize sz = [self bounds].size; if(cb_reshape && (sz.width != win_width || sz.height != win_height)) { win_width = sz.width; win_height = sz.height; cb_reshape(sz.width, sz.height); } } -(void) keyDown: (NSEvent*) ev { handle_key(ev, 1); } -(void) keyUp: (NSEvent*) ev { handle_key(ev, 0); } -(void) mouseDown: (NSEvent*) ev { handle_mouse(ev, 1); } -(void) mouseUp: (NSEvent*) ev { handle_mouse(ev, 0); } -(void) rightMouseDown: (NSEvent*) ev { handle_mouse(ev, 1); } -(void) rightMouseUp: (NSEvent*) ev { handle_mouse(ev, 0); } -(void) otherMouseDown: (NSEvent*) ev { handle_mouse(ev, 1); } -(void) otherMouseUp: (NSEvent*) ev { handle_mouse(ev, 0); } -(void) mouseDragged: (NSEvent*) ev { handle_motion(ev); } -(void) rightMouseDragged: (NSEvent*) ev { handle_motion(ev); } -(void) otherMouseDragged: (NSEvent*) ev { handle_motion(ev); } -(BOOL) acceptsFirstResponder { return YES; } @end @implementation AppDelegate -(void) applicationWillFinishLaunching: (NSNotification*) notification { } -(void) applicationDidFinishLaunching: (NSNotification*) notification { } -(BOOL) applicationShouldTerminate: (NSApplication*) app { return NSTerminateNow; } -(BOOL) applicationShouldTerminateAfterLastWindowClosed: (NSApplication*) app { return YES; } -(void) applicationWillTerminate: (NSNotification*) notification { /*[NSApp setDelegate: nil]; [global_pool drain];*/ } @end @implementation WinDelegate -(id) init { self = [super init]; return self; } -(void) dealloc { [super dealloc]; } -(void) windowDidExpose: (NSNotification*) notification { } -(void) windowDidResize: (NSNotification*) notification { } -(BOOL) windowShouldClose: (id) win { close_window(); return YES; } -(void) windowWillClose: (NSNotification*) notification { /*[NSApp terminate: nil];*/ } @end static int init(void) { AppDelegate *delegate; global_pool = [[NSAutoreleasePool alloc] init]; [NSApplication sharedApplication]; delegate = [[AppDelegate alloc] init]; [NSApp setDelegate: delegate]; return 0; } static void shutdown(void) { close_window(); quit_main_loop = 1; [NSApp terminate: nil]; } /* create/destroy windows */ static int create_window(int xsz, int ysz, unsigned int flags) { NSAutoreleasePool *pool; WinDelegate *delegate; NSWindow *nswin; NSRect rect; OpenGLView *view; NSOpenGLPixelFormat *pf; NSOpenGLPixelFormatAttribute attr[32]; unsigned int style; pool = [[NSAutoreleasePool alloc] init]; /* create the view */ fill_attr(attr, flags); pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes: attr] autorelease]; view = [[OpenGLView alloc] initWithFrame: rect pixelFormat: pf]; /* create the window and attach the OpenGL view */ rect.origin.x = rect.origin.y = 0; rect.size.width = xsz; rect.size.height = ysz; style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask; nswin = [[NSWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: YES]; delegate = [[WinDelegate alloc] init]; [nswin setDelegate: delegate]; [nswin setTitle: @"OpenGL/Cocoa"]; [nswin setReleasedWhenClosed: YES]; [nswin setContentView: view]; [nswin makeFirstResponder: view]; [nswin makeKeyAndOrderFront: nil]; [view release]; glwin = nswin; glview = view; glctx = [view openGLContext]; needs_redisplay = 1; delegate->win = glwin; [glctx makeCurrentContext]; [pool drain]; return win->wid; } static void close_window(void) { [glwin close]; shutdown(); } static int set_title(const char *str) { NSString *nsstr; nsstr = [[NSString alloc] initWithCString: str encoding: NSASCIIStringEncoding]; [glwin setTitle: nsstr]; [nsstr release]; return 0; } static void redisplay(void) { needs_redisplay = 1; } static void swap_buffers(void) { [glctx flushBuffer]; } static int get_modifiers(void) { unsigned int nsmod = [NSEvent modifierFlags]; unsigned int mod = 0; if(nsmod & NSShiftKeyMask) { mod |= GLUT_ACTIVE_SHIFT; } if(nsmod & NSControlKeyMask) { mod |= GLUT_ACTIVE_CTRL; } if(nsmod & NSAlternateKeyMask) { mod |= GLUT_ACTIVE_ALT; } return mod; } static int process_events(void) { NSAutoreleasePool *pool; NSRunLoop *runloop; NSDate *block, *nonblock, *limdate; pool = [[NSAutoreleasePool alloc] init]; if(needs_redisplay) { needs_redisplay = 0; cb_display(); } runloop = [[NSRunLoop currentRunLoop] retain]; block = [runloop limitDateForMode: NSDefaultRunLoopMode]; nonblock = [[NSDate distantPast] retain]; limdate = idle ? nonblock : block; while(!quit_main_loop) { NSEvent *ev = [NSApp nextEventMatchingMask: NSAnyEventMask untilDate: limdate inMode: NSDefaultRunLoopMode dequeue: YES]; if(!ev) break; [NSApp sendEvent: ev]; if(limdate == block) { limdate = nonblock; } } if(cb_idle) { cb_idle(); } [runloop release]; [pool drain]; return quit_main_loop ? -1 : 0; } static void handle_key(NSEvent *ev, int state) { NSPoint pt; NSString *str; unichar c; str = [ev characters]; if(![str length]) { return; } pt = [ev locationInWindow]; c = [str characterAtIndex: 0]; if(state) { if(c < 256) { if(cb_keydown) cb_keydown(c, pt.x, pt.y); } else { if(cb_skeydown) cb_skeydown(c, pt.x, pt.y); } } else { if(c < 256) { if(cb_keyup) cb_keyup(c, pt.x, pt.y); } else { if(cb_skeyup) cb_skeyup(c, pt.x, pt.y); } } } static void handle_mouse(NSEvent *ev, int state) { int bn; NSPoint pt; if(cb_mouse) { bn = [ev buttonNumber]; if(bn == 2) { bn = 1; } else if(bn == 1) { bn = 2; } pt = [ev locationInWindow]; cb_mouse(0, bn, state, pt.x, pt.y - 1); } } static void handle_motion(NSEvent *ev) { NSPoint pt; if(cb_motion) { pt = [ev locationInWindow]; cb_motion(0, pt.x, pt.y - 1); } } static void fill_attr(NSOpenGLPixelFormatAttribute *attr, unsigned int flags) { int i = 0; /* this is very important. makes pixelformat selection behave like GLX * where any non-zero value will denote "choose highest possible". This * is pretty much what we intend, as the user doesn't actually pass any * of these numbers. */ attr[i++] = NSOpenGLPFAMaximumPolicy; attr[i++] = NSOpenGLPFAColorSize; attr[i++] = 1; if(flags & SGL_DOUBLE) { attr[i++] = NSOpenGLPFADoubleBuffer; } if(flags & SGL_DEPTH) { attr[i++] = NSOpenGLPFADepthSize; attr[i++] = 1; } if(flags & SGL_STENCIL) { attr[i++] = NSOpenGLPFAStencilSize; attr[i++] = 8; /* max-policy has no effect on stencil selection */ } if(flags & SGL_STEREO) { attr[i++] = NSOpenGLPFAStereo; } if(flags & SGL_MULTISAMPLE) { attr[i++] = NSOpenGLPFASampleBuffers; attr[i++] = 1; attr[i++] = NSOpenGLPFASamples; attr[i++] = 4; /* TODO don't hardcode, query */ } attr[i++] = 0; }