1/*
2  Copyright 2012-2017 David Robillard <http://drobilla.net>
3  Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
4
5  Permission to use, copy, modify, and/or distribute this software for any
6  purpose with or without fee is hereby granted, provided that the above
7  copyright notice and this permission notice appear in all copies.
8
9  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*/
17
18/**
19   @file pugl_osx.m OSX/Cocoa Pugl Implementation.
20*/
21
22#include <stdlib.h>
23
24#import <Cocoa/Cocoa.h>
25
26#include "pugl/cairo_gl.h"
27#include "pugl/gl.h"
28#include "pugl/pugl_internal.h"
29
30@class PuglOpenGLView;
31
32struct PuglInternalsImpl {
33	NSApplication*   app;
34	PuglOpenGLView*  glview;
35	id               window;
36	NSEvent*         nextEvent;
37	unsigned         mods;
38#ifdef PUGL_HAVE_CAIRO
39	cairo_surface_t* surface;
40	cairo_t*         cr;
41	PuglCairoGL      cairo_gl;
42#endif
43};
44
45@interface PuglWindow : NSWindow
46{
47@public
48	PuglView* puglview;
49}
50
51- (id) initWithContentRect:(NSRect)contentRect
52                 styleMask:(unsigned int)aStyle
53                   backing:(NSBackingStoreType)bufferingType
54                     defer:(BOOL)flag;
55- (void) setPuglview:(PuglView*)view;
56- (BOOL) windowShouldClose:(id)sender;
57- (BOOL) canBecomeKeyWindow:(id)sender;
58@end
59
60@implementation PuglWindow
61
62- (id)initWithContentRect:(NSRect)contentRect
63                styleMask:(unsigned int)aStyle
64                  backing:(NSBackingStoreType)bufferingType
65                    defer:(BOOL)flag
66{
67	NSWindow* result = [super initWithContentRect:contentRect
68					    styleMask:aStyle
69					      backing:bufferingType
70						defer:NO];
71
72	[result setAcceptsMouseMovedEvents:YES];
73	return (PuglWindow*)result;
74}
75
76- (void)setPuglview:(PuglView*)view
77{
78	puglview = view;
79	[self setContentSize:NSMakeSize(view->width, view->height)];
80}
81
82- (BOOL)windowShouldClose:(id)sender
83{
84	const PuglEventClose ev = {
85		PUGL_CLOSE,
86		puglview,
87		0
88	};
89	puglDispatchEvent(puglview, (PuglEvent*)&ev);
90
91	return YES;
92}
93
94- (BOOL) canBecomeKeyWindow
95{
96	return YES;
97}
98
99- (BOOL) canBecomeMainWindow
100{
101	return YES;
102}
103
104- (BOOL) canBecomeKeyWindow:(id)sender
105{
106	return NO;
107}
108
109@end
110
111@interface PuglOpenGLView : NSOpenGLView
112{
113@public
114	PuglView* puglview;
115
116	NSTrackingArea* trackingArea;
117}
118
119- (id) initWithFrame:(NSRect)frame;
120- (void) reshape;
121- (void) drawRect:(NSRect)rect;
122- (NSPoint) eventLocation:(NSEvent*)event;
123- (void) mouseEntered:(NSEvent*)event;
124- (void) mouseExited:(NSEvent*)event;
125- (void) mouseMoved:(NSEvent*)event;
126- (void) mouseDragged:(NSEvent*)event;
127- (void) rightMouseDragged:(NSEvent*)event;
128- (void) mouseDown:(NSEvent*)event;
129- (void) mouseUp:(NSEvent*)event;
130- (void) rightMouseDown:(NSEvent*)event;
131- (void) rightMouseUp:(NSEvent*)event;
132- (void) otherMouseDragged:(NSEvent*)event;
133- (void) otherMouseDown:(NSEvent*)event;
134- (void) otherMouseUp:(NSEvent*)event;
135- (void) scrollWheel:(NSEvent*)event;
136- (void) keyDown:(NSEvent*)event;
137- (void) keyUp:(NSEvent*)event;
138- (void) flagsChanged:(NSEvent*)event;
139
140@end
141
142@implementation PuglOpenGLView
143
144- (id) initWithFrame:(NSRect)frame
145{
146	NSOpenGLPixelFormatAttribute pixelAttribs[16] = {
147		NSOpenGLPFADoubleBuffer,
148		NSOpenGLPFAAccelerated,
149		NSOpenGLPFAColorSize, 32,
150		NSOpenGLPFADepthSize, 32,
151		0
152	};
153
154	NSOpenGLPixelFormat* pixelFormat = [
155		[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs];
156
157	if (pixelFormat) {
158		self = [super initWithFrame:frame pixelFormat:pixelFormat];
159		[pixelFormat release];
160	} else {
161		self = [super initWithFrame:frame];
162	}
163
164	if (self) {
165		[[self openGLContext] makeCurrentContext];
166		[self reshape];
167	}
168	return self;
169}
170
171- (void) reshape
172{
173	[[self openGLContext] update];
174
175	if (!puglview) {
176		return;
177	}
178
179	const NSRect             bounds = [self bounds];
180	const PuglEventConfigure ev     =  {
181		PUGL_CONFIGURE,
182		puglview,
183		0,
184		bounds.origin.x,
185		bounds.origin.y,
186		bounds.size.width,
187		bounds.size.height,
188	};
189
190#ifdef PUGL_HAVE_CAIRO
191	PuglInternals* impl = puglview->impl;
192	if (puglview->ctx_type & PUGL_CAIRO) {
193		cairo_surface_destroy(impl->surface);
194		cairo_destroy(impl->cr);
195		impl->surface = pugl_cairo_gl_create(
196			&impl->cairo_gl, ev.width, ev.height, 4);
197		impl->cr = cairo_create(impl->surface);
198		pugl_cairo_gl_configure(&impl->cairo_gl, ev.width, ev.height);
199	}
200#endif
201
202	puglDispatchEvent(puglview, (PuglEvent*)&ev);
203}
204
205- (void) drawRect:(NSRect)rect
206{
207	const PuglEventExpose ev =  {
208		PUGL_EXPOSE,
209		puglview,
210		0,
211		rect.origin.x,
212		rect.origin.y,
213		rect.size.width,
214		rect.size.height,
215		0
216	};
217
218	puglDispatchEvent(puglview, (const PuglEvent*)&ev);
219
220#ifdef PUGL_HAVE_CAIRO
221	if (puglview->ctx_type & PUGL_CAIRO) {
222		pugl_cairo_gl_draw(
223			&puglview->impl->cairo_gl, puglview->width, puglview->height);
224	}
225#endif
226}
227
228- (BOOL) acceptsFirstResponder
229{
230	return YES;
231}
232
233static unsigned
234getModifiers(PuglView* view, NSEvent* ev)
235{
236	const unsigned modifierFlags = [ev modifierFlags];
237
238	unsigned mods = 0;
239	mods |= (modifierFlags & NSShiftKeyMask)     ? PUGL_MOD_SHIFT : 0;
240	mods |= (modifierFlags & NSControlKeyMask)   ? PUGL_MOD_CTRL  : 0;
241	mods |= (modifierFlags & NSAlternateKeyMask) ? PUGL_MOD_ALT   : 0;
242	mods |= (modifierFlags & NSCommandKeyMask)   ? PUGL_MOD_SUPER : 0;
243	return mods;
244}
245
246static PuglKey
247keySymToSpecial(PuglView* view, NSEvent* ev)
248{
249	NSString* chars = [ev charactersIgnoringModifiers];
250	if ([chars length] == 1) {
251		switch ([chars characterAtIndex:0]) {
252		case NSF1FunctionKey:         return PUGL_KEY_F1;
253		case NSF2FunctionKey:         return PUGL_KEY_F2;
254		case NSF3FunctionKey:         return PUGL_KEY_F3;
255		case NSF4FunctionKey:         return PUGL_KEY_F4;
256		case NSF5FunctionKey:         return PUGL_KEY_F5;
257		case NSF6FunctionKey:         return PUGL_KEY_F6;
258		case NSF7FunctionKey:         return PUGL_KEY_F7;
259		case NSF8FunctionKey:         return PUGL_KEY_F8;
260		case NSF9FunctionKey:         return PUGL_KEY_F9;
261		case NSF10FunctionKey:        return PUGL_KEY_F10;
262		case NSF11FunctionKey:        return PUGL_KEY_F11;
263		case NSF12FunctionKey:        return PUGL_KEY_F12;
264                case 127:                     return PUGL_KEY_BACKSPACE;
265                case NSDeleteFunctionKey:     return PUGL_KEY_DELETE;
266		case NSLeftArrowFunctionKey:  return PUGL_KEY_LEFT;
267		case NSUpArrowFunctionKey:    return PUGL_KEY_UP;
268		case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT;
269		case NSDownArrowFunctionKey:  return PUGL_KEY_DOWN;
270		case NSPageUpFunctionKey:     return PUGL_KEY_PAGE_UP;
271		case NSPageDownFunctionKey:   return PUGL_KEY_PAGE_DOWN;
272		case NSHomeFunctionKey:       return PUGL_KEY_HOME;
273		case NSEndFunctionKey:        return PUGL_KEY_END;
274		case NSInsertFunctionKey:     return PUGL_KEY_INSERT;
275		}
276		// SHIFT, CTRL, ALT, and SUPER are handled in [flagsChanged]
277	}
278	return (PuglKey)0;
279}
280
281-(void)updateTrackingAreas
282{
283	if (trackingArea != nil) {
284		[self removeTrackingArea:trackingArea];
285		[trackingArea release];
286	}
287
288	const int opts = (NSTrackingMouseEnteredAndExited |
289	                  NSTrackingMouseMoved |
290	                  NSTrackingActiveAlways);
291	trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
292	                                             options:opts
293	                                               owner:self
294	                                            userInfo:nil];
295	[self addTrackingArea:trackingArea];
296}
297
298- (NSPoint) eventLocation:(NSEvent*)event
299{
300	return [self convertPoint:[event locationInWindow] fromView:nil];
301}
302
303- (void)mouseEntered:(NSEvent*)theEvent
304{
305	[self updateTrackingAreas];
306}
307
308- (void)mouseExited:(NSEvent*)theEvent
309{
310}
311
312- (void) mouseMoved:(NSEvent*)event
313{
314	const NSPoint         wloc = [self eventLocation:event];
315	const NSPoint         rloc = [NSEvent mouseLocation];
316	const PuglEventMotion ev   =  {
317		PUGL_MOTION_NOTIFY,
318		puglview,
319		0,
320		[event timestamp],
321		wloc.x,
322		puglview->height - wloc.y,
323		rloc.x,
324		[[NSScreen mainScreen] frame].size.height - rloc.y,
325		getModifiers(puglview, event),
326		0,
327		1
328	};
329	puglDispatchEvent(puglview, (PuglEvent*)&ev);
330}
331
332- (void) mouseDragged:(NSEvent*)event
333{
334	[self mouseMoved: event];
335}
336
337- (void) rightMouseDragged:(NSEvent*)event
338{
339	[self mouseMoved: event];
340}
341
342- (void) otherMouseDragged:(NSEvent*)event
343{
344	[self mouseMoved: event];
345}
346
347- (void) mouseDown:(NSEvent*)event
348{
349	const NSPoint         wloc = [self eventLocation:event];
350	const NSPoint         rloc = [NSEvent mouseLocation];
351	const PuglEventButton ev   =  {
352		PUGL_BUTTON_PRESS,
353		puglview,
354		0,
355		[event timestamp],
356		wloc.x,
357		puglview->height - wloc.y,
358		rloc.x,
359		[[NSScreen mainScreen] frame].size.height - rloc.y,
360		getModifiers(puglview, event),
361		(unsigned)[event buttonNumber] + 1
362	};
363	puglDispatchEvent(puglview, (PuglEvent*)&ev);
364}
365
366- (void) mouseUp:(NSEvent*)event
367{
368	const NSPoint         wloc = [self eventLocation:event];
369	const NSPoint         rloc = [NSEvent mouseLocation];
370	const PuglEventButton ev   =  {
371		PUGL_BUTTON_RELEASE,
372		puglview,
373		0,
374		[event timestamp],
375		wloc.x,
376		puglview->height - wloc.y,
377		rloc.x,
378		[[NSScreen mainScreen] frame].size.height - rloc.y,
379		getModifiers(puglview, event),
380		(unsigned)[event buttonNumber] + 1
381	};
382	puglDispatchEvent(puglview, (PuglEvent*)&ev);
383	[self updateTrackingAreas];
384}
385
386- (void) rightMouseDown:(NSEvent*)event
387{
388	[self mouseDown: event];
389}
390
391- (void) rightMouseUp:(NSEvent*)event
392{
393	[self mouseUp: event];
394}
395
396- (void) otherMouseDown:(NSEvent*)event
397{
398	[self mouseDown: event];
399}
400
401- (void) otherMouseUp:(NSEvent*)event
402{
403	[self mouseUp: event];
404}
405
406- (void) scrollWheel:(NSEvent*)event
407{
408	[self updateTrackingAreas];
409
410	const NSPoint         wloc = [self eventLocation:event];
411	const NSPoint         rloc = [NSEvent mouseLocation];
412	const PuglEventScroll ev   =  {
413		PUGL_SCROLL,
414		puglview,
415		0,
416		[event timestamp],
417		wloc.x,
418		puglview->height - wloc.y,
419		rloc.x,
420		[[NSScreen mainScreen] frame].size.height - rloc.y,
421		getModifiers(puglview, event),
422		[event deltaX],
423		[event deltaY]
424	};
425	puglDispatchEvent(puglview, (PuglEvent*)&ev);
426	[self updateTrackingAreas];
427}
428
429- (void) keyDown:(NSEvent*)event
430{
431	if (puglview->ignoreKeyRepeat && [event isARepeat]) {
432		return;
433	}
434
435	const NSPoint      wloc  = [self eventLocation:event];
436	const NSPoint      rloc  = [NSEvent mouseLocation];
437	const NSString*    chars = [event characters];
438	const char*        str   = [chars UTF8String];
439        const PuglKey      special = keySymToSpecial(puglview, event);
440        const uint32_t     code  = special ? 0 : puglDecodeUTF8((const uint8_t*)str);
441	PuglEventKey       ev    =  {
442		PUGL_KEY_PRESS,
443		puglview,
444		0,
445		[event timestamp],
446		wloc.x,
447		puglview->height - wloc.y,
448		rloc.x,
449		[[NSScreen mainScreen] frame].size.height - rloc.y,
450		getModifiers(puglview, event),
451		[event keyCode],
452		(code != 0xFFFD) ? code : 0,
453		special,
454		{ 0, 0, 0, 0, 0, 0, 0, 0 },
455		false
456	};
457	strncpy((char*)ev.utf8, str, 8);
458	puglDispatchEvent(puglview, (PuglEvent*)&ev);
459}
460
461- (void) keyUp:(NSEvent*)event
462{
463	const NSPoint      wloc  = [self eventLocation:event];
464	const NSPoint      rloc  = [NSEvent mouseLocation];
465	const NSString*    chars = [event characters];
466	const char*        str   = [chars UTF8String];
467        const PuglKey      special = keySymToSpecial(puglview, event);
468        const uint32_t     code  = special ? 0 : puglDecodeUTF8((const uint8_t*)str);
469	const PuglEventKey ev    =  {
470		PUGL_KEY_RELEASE,
471		puglview,
472		0,
473		[event timestamp],
474		wloc.x,
475		puglview->height - wloc.y,
476		rloc.x,
477		[[NSScreen mainScreen] frame].size.height - rloc.y,
478		getModifiers(puglview, event),
479		[event keyCode],
480		(code != 0xFFFD) ? code : 0,
481		keySymToSpecial(puglview, event),
482		{ 0, 0, 0, 0, 0, 0, 0, 0 },
483		false,
484	};
485	strncpy((char*)ev.utf8, str, 8);
486	puglDispatchEvent(puglview, (PuglEvent*)&ev);
487}
488
489- (void) flagsChanged:(NSEvent*)event
490{
491	const unsigned mods    = getModifiers(puglview, event);
492	PuglEventType  type    = PUGL_NOTHING;
493	PuglKey        special = 0;
494
495	if ((mods & PUGL_MOD_SHIFT) != (puglview->impl->mods & PUGL_MOD_SHIFT)) {
496		type = mods & PUGL_MOD_SHIFT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
497		special = PUGL_KEY_SHIFT;
498	} else if ((mods & PUGL_MOD_CTRL) != (puglview->impl->mods & PUGL_MOD_CTRL)) {
499		type = mods & PUGL_MOD_CTRL ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
500		special = PUGL_KEY_CTRL;
501	} else if ((mods & PUGL_MOD_ALT) != (puglview->impl->mods & PUGL_MOD_ALT)) {
502		type = mods & PUGL_MOD_ALT ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
503		special = PUGL_KEY_ALT;
504	} else if ((mods & PUGL_MOD_SUPER) != (puglview->impl->mods & PUGL_MOD_SUPER)) {
505		type = mods & PUGL_MOD_SUPER ? PUGL_KEY_PRESS : PUGL_KEY_RELEASE;
506		special = PUGL_KEY_SUPER;
507	}
508
509	if (special != 0) {
510		const NSPoint wloc = [self eventLocation:event];
511		const NSPoint rloc = [NSEvent mouseLocation];
512		PuglEventKey  ev   = {
513			type,
514			puglview,
515			0,
516			[event timestamp],
517			wloc.x,
518			puglview->height - wloc.y,
519			rloc.x,
520			[[NSScreen mainScreen] frame].size.height - rloc.y,
521			mods,
522			[event keyCode],
523			0,
524			special,
525			{ 0, 0, 0, 0, 0, 0, 0, 0 },
526			false
527		};
528		puglDispatchEvent(puglview, (PuglEvent*)&ev);
529	}
530
531	puglview->impl->mods = mods;
532}
533
534@end
535
536PuglInternals*
537puglInitInternals(void)
538{
539	return (PuglInternals*)calloc(1, sizeof(PuglInternals));
540}
541
542void
543puglEnterContext(PuglView* view)
544{
545	[[view->impl->glview openGLContext] makeCurrentContext];
546}
547
548void
549puglLeaveContext(PuglView* view, bool flush)
550{
551#ifdef PUGL_HAVE_CAIRO
552	if (view->ctx_type & PUGL_CAIRO) {
553		pugl_cairo_gl_draw(&view->impl->cairo_gl, view->width, view->height);
554	}
555#endif
556
557	if (flush) {
558		[[view->impl->glview openGLContext] flushBuffer];
559	}
560}
561
562static NSLayoutConstraint*
563puglConstraint(id item, NSLayoutAttribute attribute, float constant)
564{
565	return [NSLayoutConstraint
566		       constraintWithItem: item
567		                attribute: attribute
568		                relatedBy: NSLayoutRelationGreaterThanOrEqual
569		                   toItem: nil
570		                attribute: NSLayoutAttributeNotAnAttribute
571		               multiplier: 1.0
572		                 constant: constant];
573}
574
575int
576puglCreateWindow(PuglView* view, const char* title)
577{
578	PuglInternals* impl = view->impl;
579
580	[NSAutoreleasePool new];
581	impl->app = [NSApplication sharedApplication];
582
583	impl->glview           = [PuglOpenGLView new];
584	impl->glview->puglview = view;
585
586	[impl->glview setFrameSize:NSMakeSize(view->width, view->height)];
587	[impl->glview addConstraint:
588		     puglConstraint(impl->glview, NSLayoutAttributeWidth, view->min_width)];
589	[impl->glview addConstraint:
590		     puglConstraint(impl->glview, NSLayoutAttributeHeight, view->min_height)];
591	if (!view->resizable) {
592		[impl->glview setAutoresizingMask:NSViewNotSizable];
593	}
594
595	if (view->parent) {
596		NSView* pview = (NSView*)view->parent;
597		[pview addSubview:impl->glview];
598		[impl->glview setHidden:NO];
599	} else {
600		NSString* titleString = [[NSString alloc]
601			                        initWithBytes:title
602			                               length:strlen(title)
603			                             encoding:NSUTF8StringEncoding];
604		NSRect frame = NSMakeRect(0, 0, view->min_width, view->min_height);
605		unsigned style = NSClosableWindowMask | NSTitledWindowMask;
606		if (view->resizable) {
607			style |= NSResizableWindowMask;
608		}
609
610		id window = [[[PuglWindow alloc]
611			initWithContentRect:frame
612			          styleMask:style
613			            backing:NSBackingStoreBuffered
614			              defer:NO
615		              ] retain];
616		[window setPuglview:view];
617		[window setTitle:titleString];
618		if (view->min_width || view->min_height) {
619			[window setContentMinSize:NSMakeSize(view->min_width,
620			                                     view->min_height)];
621		}
622		impl->window = window;
623
624		[window setContentView:impl->glview];
625		[impl->app activateIgnoringOtherApps:YES];
626		[window makeFirstResponder:impl->glview];
627		[window makeKeyAndOrderFront:window];
628
629                if (view->transient_parent) {
630                        // modal dialogs are usually centered on macOS
631                        // (this window is not really modal, but at least centered)
632                        [window center];
633                }
634	}
635
636	return 0;
637}
638
639void
640puglShowWindow(PuglView* view)
641{
642	[view->impl->window setIsVisible:YES];
643	view->visible = true;
644}
645
646void
647puglHideWindow(PuglView* view)
648{
649	[view->impl->window setIsVisible:NO];
650	view->visible = false;
651}
652
653void
654puglDestroy(PuglView* view)
655{
656#ifdef PUGL_HAVE_CAIRO
657	pugl_cairo_gl_free(&view->impl->cairo_gl);
658#endif
659	view->impl->glview->puglview = NULL;
660	[view->impl->glview removeFromSuperview];
661	if (view->impl->window) {
662		[view->impl->window close];
663	}
664	[view->impl->glview release];
665	if (view->impl->window) {
666		[view->impl->window release];
667	}
668	free(view->windowClass);
669	free(view->impl);
670	free(view);
671}
672
673void
674puglGrabFocus(PuglView* view)
675{
676	// TODO
677}
678
679PuglStatus
680puglWaitForEvent(PuglView* view)
681{
682	/* OSX supposedly has queue: and untilDate: selectors that can be used for
683	   a blocking non-queueing event check, but if used here cause an
684	   unsupported selector error at runtime.  I have no idea why, so just get
685	   the event and keep it around until the call to puglProcessEvents. */
686	if (!view->impl->nextEvent) {
687		view->impl->nextEvent = [view->impl->window
688		                            nextEventMatchingMask: NSAnyEventMask];
689	}
690
691	return PUGL_SUCCESS;
692}
693
694PuglStatus
695puglProcessEvents(PuglView* view)
696{
697	while (true) {
698		// Get the next event, or use the cached one from puglWaitForEvent
699		if (!view->impl->nextEvent) {
700			view->impl->nextEvent = [view->impl->window
701			                            nextEventMatchingMask: NSAnyEventMask
702                                                    untilDate:nil
703                                                    inMode:NSDefaultRunLoopMode
704                                                    dequeue:YES];
705		}
706
707		if (!view->impl->nextEvent) {
708			break;  // No events to process, done
709		}
710
711		// Dispatch event
712		[view->impl->app sendEvent: view->impl->nextEvent];
713		view->impl->nextEvent = NULL;
714	}
715
716	return PUGL_SUCCESS;
717}
718
719static void
720puglResize(PuglView* view)
721{
722	int set_hints; // ignored
723	view->resize = false;
724	if (!view->resizeFunc) { return; }
725
726	[[view->impl->glview openGLContext] makeCurrentContext];
727	view->resizeFunc(view, &view->width, &view->height, &set_hints);
728	if (view->impl->window) {
729		[view->impl->window setContentSize:NSMakeSize(view->width, view->height) ];
730	} else {
731		[view->impl->glview setFrameSize:NSMakeSize(view->width, view->height)];
732	}
733	[view->impl->glview reshape];
734	[NSOpenGLContext clearCurrentContext];
735}
736
737void
738puglPostResize(PuglView* view)
739{
740	view->resize = true;
741	puglResize(view);
742}
743
744void
745puglPostRedisplay(PuglView* view)
746{
747	//view->redisplay = true; // unused
748	[view->impl->glview setNeedsDisplay: YES];
749}
750
751PuglNativeWindow
752puglGetNativeWindow(PuglView* view)
753{
754	return (PuglNativeWindow)view->impl->glview;
755}
756
757void*
758puglGetContext(PuglView* view)
759{
760#ifdef PUGL_HAVE_CAIRO
761	if (view->ctx_type & PUGL_CAIRO) {
762		return view->impl->cr;
763	}
764#endif
765	return NULL;
766}
767