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