1/* 2 * Copyright (c) 2012-2015 Hypertriton, Inc. <http://hypertriton.com/> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 23 * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26/* 27 * Driver for Cocoa framework. This is a multiple display driver (one 28 * Cocoa window is created for each Agar window). 29 */ 30 31#include <agar/core/core.h> 32#include <agar/core/config.h> 33 34#include <agar/gui/gui.h> 35#include <agar/gui/window.h> 36#include <agar/gui/gui_math.h> 37#include <agar/gui/text.h> 38#include <agar/gui/cursors.h> 39 40#include <ApplicationServices/ApplicationServices.h> 41#include <Cocoa/Cocoa.h> 42#include <OpenGL/CGLTypes.h> 43#include <OpenGL/OpenGL.h> 44#include <OpenGL/CGLRenderers.h> 45#include <OpenGL/gl.h> 46 47#include <agar/gui/drv_gl_common.h> 48#include <agar/gui/drv_cocoa_keymap.h> 49 50static int nDrivers = 0; /* Drivers open */ 51static AG_DriverEventQ cocEventQ; /* Private event queue */ 52static AG_EventSink *cocEventSpinner = NULL; /* Standard event sink */ 53static AG_EventSink *cocEventEpilogue = NULL; /* Standard event epilogue */ 54struct ag_driver_cocoa; 55 56/* 57 * Application delegate 58 */ 59 60@interface AG_AppDelegate : NSObject 61- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; 62@end 63 64@implementation AG_AppDelegate : NSObject 65- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; 66{ 67 AG_QuitGUI(); 68 return (NSTerminateCancel); 69} 70@end 71 72/* 73 * Window interface 74 */ 75@interface AG_CocoaWindow : NSWindow { 76@public 77 AG_Window *_agarWindow; /* Corresponding Agar window */ 78} 79 80- (BOOL)canBecomeKeyWindow; 81- (BOOL)canBecomeMainWindow; 82@end 83 84@implementation AG_CocoaWindow 85- (BOOL)canBecomeKeyWindow 86{ 87 return (_agarWindow->flags & AG_WINDOW_DENYFOCUS) ? NO : YES; 88} 89 90- (BOOL)canBecomeMainWindow 91{ 92 return (_agarWindow->flags & AG_WINDOW_MAIN) ? YES : NO; 93} 94@end 95 96/* 97 * View interface 98 */ 99@interface AG_CocoaView : NSView { 100@public 101 AG_Window *_agarWindow; /* Corresponding Agar window */ 102} 103- (void)rightMouseDown:(NSEvent *)theEvent; 104- (BOOL)preservesContentDuringLiveResize; 105- (void)viewWillStartLiveResize; 106- (void)viewDidEndLiveResize; 107@end 108 109@implementation AG_CocoaView 110- (void)rightMouseDown:(NSEvent *)theEvent 111{ 112 [[self nextResponder] rightMouseDown:theEvent]; 113} 114 115- (BOOL)preservesContentDuringLiveResize 116{ 117 return (YES); 118} 119 120- (void)viewWillStartLiveResize 121{ 122 [super viewWillStartLiveResize]; 123} 124 125- (void)viewDidEndLiveResize 126{ 127 [super viewDidEndLiveResize]; 128} 129@end 130 131/* 132 * Event listener interface 133 */ 134#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 135@interface AG_CocoaListener : NSResponder <NSWindowDelegate> { 136#else 137@interface AG_CocoaListener : NSResponder { 138#endif 139 AG_Window *_window; 140 struct ag_driver_cocoa *_driver; 141} 142 143-(void) listen:(struct ag_driver_cocoa *) driver; 144-(void) close; 145 146/* Window delegate functionality */ 147-(BOOL) windowShouldClose:(id) sender; 148-(void) windowDidExpose:(NSNotification *) aNotification; 149-(void) windowDidMove:(NSNotification *) aNotification; 150-(void) windowDidResize:(NSNotification *) aNotification; 151-(void) windowDidMiniaturize:(NSNotification *) aNotification; 152-(void) windowDidDeminiaturize:(NSNotification *) aNotification; 153-(void) windowDidBecomeKey:(NSNotification *) aNotification; 154-(void) windowDidResignKey:(NSNotification *) aNotification; 155 156@end 157 158/* Driver instance data */ 159typedef struct ag_driver_cocoa { 160 struct ag_driver_mw _inherit; 161 AG_CocoaWindow *win; /* Cocoa window */ 162 AG_CocoaListener *evListener; /* Cocoa event listener */ 163 NSOpenGLContext *glCtx; 164 AG_GL_Context gl; 165 AG_Mutex lock; /* Protect Cocoa calls */ 166 Uint modFlags; /* Last modifier state */ 167#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 168 NSTrackingArea *trackArea; /* For mouse motion events */ 169#endif 170} AG_DriverCocoa; 171 172AG_DriverMwClass agDriverCocoa; 173 174#define AGDRIVER_IS_COCOA(drv) \ 175 (AGDRIVER_CLASS(drv) == (AG_DriverClass *)&agDriverCocoa) 176 177 178static int COCOA_PendingEvents(void *); 179static int COCOA_GetNextEvent(void *, AG_DriverEvent *); 180static int COCOA_ProcessEvent(void *, AG_DriverEvent *); 181static void COCOA_PostResizeCallback(AG_Window *, AG_SizeAlloc *); 182static void COCOA_PostMoveCallback(AG_Window *, AG_SizeAlloc *); 183static int COCOA_RaiseWindow(AG_Window *); 184static int COCOA_SetInputFocus(AG_Window *); 185#if 0 186static void COCOA_FreeWidgetResources(AG_Widget *); 187#endif 188 189static __inline__ void 190ConvertNSRect(NSRect *r) 191{ 192 r->origin.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - 193 r->origin.y - r->size.height; 194} 195 196@implementation AG_CocoaListener 197 198- (void) listen:(AG_DriverCocoa *)co 199{ 200 NSNotificationCenter *nc; 201 NSWindow *win = co->win; 202 NSView *view = [win contentView]; 203 204 _driver = co; 205 _window = AGDRIVER_MW(co)->win; 206 207 nc = [NSNotificationCenter defaultCenter]; 208 209 if ([win delegate] != nil) { 210 [nc addObserver:self selector:@selector(windowDidExpose:) name:NSWindowDidExposeNotification object:win]; 211 [nc addObserver:self selector:@selector(windowDidMove:) name:NSWindowDidMoveNotification object:win]; 212 [nc addObserver:self selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:win]; 213 [nc addObserver:self selector:@selector(windowDidMiniaturize:) name:NSWindowDidMiniaturizeNotification object:win]; 214 [nc addObserver:self selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification object:win]; 215 [nc addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:win]; 216 [nc addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:win]; 217 } else { 218 [win setDelegate:self]; 219 } 220 221 [win setNextResponder:self]; 222#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 223 [win setAcceptsMouseMovedEvents:YES]; 224#endif 225 [view setNextResponder:self]; 226 227#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 228 if ([view respondsToSelector:@selector(setAcceptsTouchEvents:)]) 229 [view setAcceptsTouchEvents:YES]; 230#endif 231} 232 233- (void)close 234{ 235 NSNotificationCenter *nc; 236 AG_DriverCocoa *co = _driver; 237 NSWindow *win = co->win; 238 NSView *view = [win contentView]; 239 240 nc = [NSNotificationCenter defaultCenter]; 241 242 if ([win delegate] != self) { 243 [nc removeObserver:self name:NSWindowDidExposeNotification object:win]; 244 [nc removeObserver:self name:NSWindowDidMoveNotification object:win]; 245 [nc removeObserver:self name:NSWindowDidResizeNotification object:win]; 246 [nc removeObserver:self name:NSWindowDidMiniaturizeNotification object:win]; 247 [nc removeObserver:self name:NSWindowDidDeminiaturizeNotification object:win]; 248 [nc removeObserver:self name:NSWindowDidBecomeKeyNotification object:win]; 249 [nc removeObserver:self name:NSWindowDidResignKeyNotification object:win]; 250 } else { 251 [win setDelegate:nil]; 252 } 253 254 if ([win nextResponder] == self) { 255 [win setNextResponder:nil]; 256 } 257 if ([view nextResponder] == self) { 258 [view setNextResponder:nil]; 259 } 260} 261 262- (BOOL)windowShouldClose:(id)sender 263{ 264 AG_DriverEvent *dev; 265 266 if ((dev = TryMalloc(sizeof(AG_DriverEvent))) != NULL) { 267 dev->type = AG_DRIVER_CLOSE; 268 dev->win = _window; 269 TAILQ_INSERT_TAIL(&cocEventQ, dev, events); 270 } 271 return NO; 272} 273 274- (void)windowDidExpose:(NSNotification *)aNotification 275{ 276 AG_DriverEvent *dev; 277 278 if ((dev = TryMalloc(sizeof(AG_DriverEvent))) != NULL) { 279 dev->type = AG_DRIVER_EXPOSE; 280 dev->win = _window; 281 TAILQ_INSERT_TAIL(&cocEventQ, dev, events); 282 } 283} 284 285- (void)windowDidMove:(NSNotification *)aNotification 286{ 287 AG_DriverCocoa *co = _driver; 288 AG_Window *win = _window; 289 NSRect rect; 290 AG_SizeAlloc a; 291 292 rect = [co->win contentRectForFrameRect:[co->win frame]]; 293 ConvertNSRect(&rect); 294 295 a.x = (int)rect.origin.x; 296 a.y = (int)rect.origin.y; 297 a.w = WIDGET(win)->w; 298 a.h = WIDGET(win)->h; 299 COCOA_PostMoveCallback(win, &a); 300} 301 302- (void)windowDidResize:(NSNotification *)aNotification 303{ 304 AG_Window *win = _window; 305 AG_DriverCocoa *co = _driver; 306 AG_SizeAlloc a; 307 NSRect rect; 308 309 rect = [co->win contentRectForFrameRect:[co->win frame]]; 310 ConvertNSRect(&rect); 311 312 /* 313 * Since the event loop will not run during a live resize 314 * operation, we can't use AG_DRIVER_VIDEORESIZE, and we 315 * must redraw the window immediately. 316 */ 317 a.x = (int)rect.origin.x; 318 a.y = (int)rect.origin.y; 319 a.w = (int)rect.size.width; 320 a.h = (int)rect.size.height; 321 322 AG_MutexLock(&co->lock); 323 AG_ObjectLock(win); 324 325 if (a.w != WIDTH(win) || a.h != HEIGHT(win)) { 326 COCOA_PostResizeCallback(win, &a); 327 } else { 328 COCOA_PostMoveCallback(win, &a); 329 } 330 if (win->visible) { 331 AG_BeginRendering(_driver); 332 AG_WindowDraw(win); 333 AG_EndRendering(_driver); 334 } 335 336 AG_ObjectUnlock(win); 337 AG_MutexUnlock(&co->lock); 338} 339 340- (void)windowDidMiniaturize:(NSNotification *)aNotification 341{ 342 _window->flags |= AG_WINDOW_MINIMIZED; 343} 344 345- (void)windowDidDeminiaturize:(NSNotification *)aNotification 346{ 347 _window->flags &= ~(AG_WINDOW_MINIMIZED); 348} 349 350- (void)windowDidBecomeKey:(NSNotification *)aNotification 351{ 352 AG_DriverEvent *dev; 353 354 agWindowFocused = _window; 355 356 if ((dev = TryMalloc(sizeof(AG_DriverEvent))) != NULL) { 357 dev->type = AG_DRIVER_FOCUS_IN; 358 dev->win = _window; 359 TAILQ_INSERT_TAIL(&cocEventQ, dev, events); 360 } 361} 362 363- (void)windowDidResignKey:(NSNotification *)aNotification 364{ 365 AG_DriverEvent *dev; 366 367 if (agWindowFocused != _window) { 368 return; 369 } 370 agWindowFocused = NULL; 371 372 if ((dev = TryMalloc(sizeof(AG_DriverEvent))) != NULL) { 373 dev->type = AG_DRIVER_FOCUS_OUT; 374 dev->win = _window; 375 TAILQ_INSERT_TAIL(&cocEventQ, dev, events); 376 } 377} 378 379@end 380 381static void 382Init(void *obj) 383{ 384 AG_DriverCocoa *co = obj; 385 386 co->win = NULL; 387 co->glCtx = NULL; 388 co->modFlags = 0; 389#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 390 co->trackArea = nil; 391#endif 392 AG_MutexInitRecursive(&co->lock); 393} 394 395static void 396Destroy(void *obj) 397{ 398 AG_DriverCocoa *co = obj; 399 400 AG_MutexDestroy(&co->lock); 401} 402 403/* 404 * Standard AG_EventLoop() event sink. 405 */ 406static int 407COCOA_EventSink(AG_EventSink *es, AG_Event *event) 408{ 409 AG_DriverEvent dev; 410 411 if (COCOA_GetNextEvent(NULL, &dev) == 1) { 412 return COCOA_ProcessEvent(NULL, &dev); 413 } 414 return (0); 415} 416static int 417COCOA_EventEpilogue(AG_EventSink *es, AG_Event *event) 418{ 419 AG_WindowDrawQueued(); 420 AG_WindowProcessQueued(); 421 return (0); 422} 423 424static int 425COCOA_Open(void *obj, const char *spec) 426{ 427 AG_Driver *drv = obj; 428 AG_DriverCocoa *co = obj; 429 NSAutoreleasePool *pool; 430 431 /* Driver manages rendering of window background. */ 432 drv->flags |= AG_DRIVER_WINDOW_BG; 433 434 /* Initialize NSApp if needed. */ 435 pool = [[NSAutoreleasePool alloc] init]; 436 if (NSApp == nil) { 437 NSApp = [NSApplication sharedApplication]; 438 [NSApp finishLaunching]; 439 } 440 if ([NSApp delegate] == nil) { 441 [NSApp setDelegate:[[AG_AppDelegate alloc] init]]; 442 } 443 [pool release]; 444 445 /* Initialize the core mouse and keyboard */ 446 /* TODO: touch handling */ 447 if ((drv->mouse = AG_MouseNew(co, "X mouse")) == NULL || 448 (drv->kbd = AG_KeyboardNew(co, "X keyboard")) == NULL) { 449 goto fail; 450 } 451 if (nDrivers == 0) { 452 TAILQ_INIT(&cocEventQ); 453 454 /* Set up event filters for standard AG_EventLoop(). */ 455 if ((cocEventSpinner = AG_AddEventSpinner(COCOA_EventSink, NULL)) == NULL || 456 (cocEventEpilogue = AG_AddEventEpilogue(COCOA_EventEpilogue, NULL)) == NULL) 457 goto fail; 458 } 459 nDrivers++; 460 return (0); 461fail: 462 if (cocEventSpinner != NULL) { AG_DelEventSpinner(cocEventSpinner); cocEventSpinner = NULL; } 463 if (cocEventEpilogue != NULL) { AG_DelEventEpilogue(cocEventEpilogue); cocEventEpilogue = NULL; } 464 if (drv->kbd != NULL) { AG_ObjectDelete(drv->kbd); drv->kbd = NULL; } 465 if (drv->mouse != NULL) { AG_ObjectDelete(drv->mouse); drv->mouse = NULL; } 466 return (-1); 467} 468 469static void 470COCOA_Close(void *obj) 471{ 472 AG_Driver *drv = obj; 473 474#ifdef AG_DEBUG 475 if (nDrivers == 0) { AG_FatalError("Driver close without open"); } 476#endif 477 if (--nDrivers == 0) { 478 AG_DriverEvent *dev, *devNext; 479 480 AG_DelEventSink(cocEventSpinner); cocEventSpinner = NULL; 481 AG_DelEventEpilogue(cocEventEpilogue); cocEventEpilogue = NULL; 482 483 for (dev = TAILQ_FIRST(&cocEventQ); 484 dev != TAILQ_LAST(&cocEventQ, ag_driver_eventq); 485 dev = devNext) { 486 devNext = TAILQ_NEXT(dev, events); 487 Free(dev); 488 } 489 TAILQ_INIT(&cocEventQ); 490 } 491 492 AG_ObjectDelete(drv->mouse); drv->mouse = NULL; 493 AG_ObjectDelete(drv->kbd); drv->kbd = NULL; 494} 495 496static int 497COCOA_GetDisplaySize(Uint *w, Uint *h) 498{ 499 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 500 NSScreen *screen; 501 NSRect sr; 502 503 /* XXX this is probably wrong */ 504 screen = [NSScreen mainScreen]; 505 sr = [screen frame]; 506 *w = sr.size.width; 507 *h = sr.size.height; 508 [pool release]; 509 return (0); 510} 511 512static int 513COCOA_PendingEvents(void *drvCaller) 514{ 515 NSAutoreleasePool *pool; 516 NSEvent *ev; 517 int rv; 518 519 if (!TAILQ_EMPTY(&cocEventQ)) 520 return (1); 521 522 pool = [[NSAutoreleasePool alloc] init]; 523 ev = [NSApp nextEventMatchingMask:NSAnyEventMask 524 untilDate:[NSDate distantPast] 525 inMode:NSDefaultRunLoopMode 526 dequeue:NO]; 527 rv = (ev != nil); 528 529 [pool release]; 530 return (rv); 531} 532 533/* Convert a NSEvent mouse button number to AG_MouseButton. */ 534static AG_MouseButton 535GetMouseButton(int which) 536{ 537 switch (which) { 538 case 0: return (AG_MOUSE_LEFT); 539 case 1: return (AG_MOUSE_RIGHT); 540 case 2: return (AG_MOUSE_MIDDLE); 541 default: return (which+1); 542 } 543} 544 545static AG_MouseButton 546GetScrollWheelButton(float x, float y) 547{ 548 if (x > 0) { 549 return (AG_MOUSE_X1); 550 } else if (x < 0) { 551 return (AG_MOUSE_X2); 552 } 553 if (y > 0) { 554 return (AG_MOUSE_WHEELUP); 555 } else if (y < 0) { 556 return (AG_MOUSE_WHEELDOWN); 557 } 558 return (AG_MOUSE_NONE); 559} 560 561/* Add a keyboard event to the queue. */ 562static void 563QueueKeyEvent(AG_DriverCocoa *co, enum ag_driver_event_type type, 564 AG_KeySym ks, Uint32 ucs) 565{ 566 AG_DriverEvent *dev; 567 568 if ((dev = TryMalloc(sizeof(AG_DriverEvent))) == NULL) { 569 AG_Verbose("Out of memory for keymod event\n"); 570 return; 571 } 572 dev->type = type; 573 dev->win = AGDRIVER_MW(co)->win; 574 dev->data.key.ks = ks; 575 dev->data.key.ucs = ucs; 576 TAILQ_INSERT_TAIL(&cocEventQ, dev, events); 577} 578 579static int 580COCOA_GetNextEvent(void *drvCaller, AG_DriverEvent *dev) 581{ 582 NSAutoreleasePool *pool; 583 AG_CocoaWindow *coWin; 584 AG_Window *win; 585 AG_Driver *drv; 586 AG_DriverCocoa *co; 587 AG_DriverEvent *devFirst; 588 NSEvent *event; 589 int rv = 0; 590 591 if (!TAILQ_EMPTY(&cocEventQ)) 592 goto out_dequeue; 593 594 pool = [[NSAutoreleasePool alloc] init]; 595 event = [NSApp nextEventMatchingMask:NSAnyEventMask 596 untilDate:[NSDate distantPast] 597 inMode:NSDefaultRunLoopMode 598 dequeue:YES]; 599 600 if (event == nil) { 601 goto out; 602 } 603 604 if ((coWin = (AG_CocoaWindow *)[event window]) == nil) { 605 [NSApp sendEvent:event]; 606 goto out; 607 } 608 win = coWin->_agarWindow; 609 610 AG_LockVFS(&agDrivers); 611 drv = WIDGET(win)->drv; 612 co = (AG_DriverCocoa *)drv; 613 614 switch ([event type]) { 615 case NSMouseMoved: 616 case NSLeftMouseDragged: 617 case NSRightMouseDragged: 618 case NSOtherMouseDragged: 619 { 620 NSPoint point = [event locationInWindow]; 621 int x = (int)point.x; 622 int y = (int)(WIDGET(win)->h - point.y); 623 624 AG_MouseMotionUpdate(drv->mouse, x, y); 625 dev->type = AG_DRIVER_MOUSE_MOTION; 626 dev->win = win; 627 dev->data.motion.x = x; 628 dev->data.motion.y = y; 629 rv = 1; 630 break; 631 } 632 case NSScrollWheel: 633 { 634 float x = [event deltaX]; 635 float y = [event deltaY]; 636 AG_MouseButton btn = GetScrollWheelButton(x, y); 637 638 if (btn == AG_MOUSE_NONE) { 639 break; 640 } 641 AG_MouseButtonUpdate(drv->mouse, AG_BUTTON_PRESSED, btn); 642 dev->type = AG_DRIVER_MOUSE_BUTTON_DOWN; 643 dev->win = win; 644 dev->data.button.which = btn; 645 dev->data.button.x = drv->mouse->x; 646 dev->data.button.y = drv->mouse->y; 647 rv = 1; 648 break; 649 } 650 case NSLeftMouseDown: 651 case NSOtherMouseDown: 652 case NSRightMouseDown: 653 { 654 AG_MouseButton btn = GetMouseButton([event buttonNumber]); 655 656 AG_MouseButtonUpdate(drv->mouse, AG_BUTTON_PRESSED, btn); 657 dev->type = AG_DRIVER_MOUSE_BUTTON_DOWN; 658 dev->win = win; 659 dev->data.button.which = btn; 660 dev->data.button.x = drv->mouse->x; 661 dev->data.button.y = drv->mouse->y; 662 rv = 1; 663 break; 664 } 665 case NSLeftMouseUp: 666 case NSOtherMouseUp: 667 case NSRightMouseUp: 668 { 669 AG_MouseButton btn = GetMouseButton([event buttonNumber]); 670 671 AG_MouseButtonUpdate(drv->mouse, AG_BUTTON_RELEASED, btn); 672 dev->type = AG_DRIVER_MOUSE_BUTTON_UP; 673 dev->win = win; 674 dev->data.button.which = btn; 675 dev->data.button.x = drv->mouse->x; 676 dev->data.button.y = drv->mouse->y; 677 rv = 1; 678 break; 679 } 680 case NSMouseEntered: 681 if ([co->win isKeyWindow]) { 682 dev->type = AG_DRIVER_MOUSE_ENTER; 683 dev->win = win; 684 rv = 1; 685 } else { 686 rv = 0; 687 } 688 break; 689 case NSMouseExited: 690 if ([co->win isKeyWindow]) { 691 dev->type = AG_DRIVER_MOUSE_LEAVE; 692 dev->win = win; 693 rv = 1; 694 } else { 695 rv = 0; 696 } 697 break; 698 case NSKeyDown: 699 if ([event isARepeat]) { 700 /* Agar implements its own key repeat */ 701 goto out; 702 } 703 /* FALLTHROUGH */ 704 case NSKeyUp: 705 { 706 NSString *characters = [event characters]; 707 enum ag_driver_event_type evType; 708 enum ag_keyboard_action kbdAction; 709 AG_KeySym ks; 710 unichar c; 711 NSUInteger i; 712 713 if ([characters length] == 0) 714 goto out; 715 716 if ([event type] == NSKeyDown) { 717 evType = AG_DRIVER_KEY_DOWN; 718 kbdAction = AG_KEY_PRESSED; 719 } else { 720 evType = AG_DRIVER_KEY_UP; 721 kbdAction = AG_KEY_RELEASED; 722 } 723 724 /* Look for matching function keys first. */ 725 c = [characters characterAtIndex: 0]; 726 ks = AG_KEY_NONE; 727 for (i = 0; i < agCocoaFunctionKeysSize; i++) { 728 const struct ag_cocoa_function_key *fnKey = 729 &agCocoaFunctionKeys[i]; 730 731 if (fnKey->uc == c) { 732 ks = fnKey->keySym; 733 break; 734 } 735 } 736 if (ks != AG_KEY_NONE) { 737 AG_KeyboardUpdate(drv->kbd, kbdAction, ks, 0); 738 dev->type = evType; 739 dev->win = win; 740 dev->data.key.ks = ks; 741 if (ks == AG_KEY_RETURN) { 742 dev->data.key.ucs = '\n'; 743 } else { 744 dev->data.key.ucs = 0; 745 } 746 rv = 1; 747 goto out; 748 } 749 750 /* Process as a character sequence. */ 751 for (i = 0; i < [characters length]; i++) { 752 AG_KeySym ks; 753 754 c = [characters characterAtIndex: i]; 755 ks = (c <= AG_KEY_ASCII_END) ? 756 (AG_KeySym)c : AG_KEY_NONE; 757 AG_KeyboardUpdate(drv->kbd, kbdAction, ks, 758 (Uint32)c); 759 760 if (i == 0) { 761 dev->type = evType; 762 dev->win = win; 763 dev->data.key.ks = ks; 764 dev->data.key.ucs = (Uint32)c; 765 rv = 1; 766 } else { 767 QueueKeyEvent(co, evType, ks, 768 (Uint32)c); 769 } 770 } 771 if (rv == 1) 772 goto out; 773 } 774 case NSFlagsChanged: 775 { 776 Uint modFlags = [event modifierFlags]; 777 int i, nChanged = 0; 778 779 for (i = 0; i < agCocoaKeymodSize; i++) { 780 const struct ag_cocoa_keymod_entry *kmEnt = 781 &agCocoaKeymod[i]; 782 783 if ((modFlags & kmEnt->keyMask) && 784 !(co->modFlags & kmEnt->keyMask)) { 785 AG_KeyboardUpdate(drv->kbd, AG_KEY_PRESSED, kmEnt->keySym, 0); 786 QueueKeyEvent(co, AG_DRIVER_KEY_DOWN, kmEnt->keySym, 0); 787 nChanged++; 788 } else if (!(modFlags & kmEnt->keyMask) && 789 (co->modFlags & kmEnt->keyMask)) { 790 AG_KeyboardUpdate(drv->kbd, AG_KEY_RELEASED, kmEnt->keySym, 0); 791 QueueKeyEvent(co, AG_DRIVER_KEY_UP, kmEnt->keySym, 0); 792 nChanged++; 793 } 794 } 795 co->modFlags = modFlags; 796 797 if (nChanged > 0) { 798 goto out_dequeue; 799 } 800 break; 801 } 802 default: 803 break; 804 } 805 [NSApp sendEvent:event]; 806out: 807 [pool release]; 808 return (rv); 809out_dequeue: 810 devFirst = TAILQ_FIRST(&cocEventQ); 811 TAILQ_REMOVE(&cocEventQ, devFirst, events); 812 memcpy(dev, devFirst, sizeof(AG_DriverEvent)); 813 Free(devFirst); 814 return (1); 815} 816 817static int 818COCOA_ProcessEvent(void *drvCaller, AG_DriverEvent *dev) 819{ 820 AG_Driver *drv; 821 int rv = 1; 822 823 if (dev->win == NULL || 824 dev->win->flags & AG_WINDOW_DETACHING) 825 return (0); 826 827 AG_LockVFS(&agDrivers); 828 drv = WIDGET(dev->win)->drv; 829 830 switch (dev->type) { 831 case AG_DRIVER_MOUSE_MOTION: 832 AG_ProcessMouseMotion(dev->win, 833 dev->data.motion.x, dev->data.motion.y, 834 drv->mouse->xRel, drv->mouse->yRel, 835 drv->mouse->btnState); 836 AG_MouseCursorUpdate(dev->win, 837 dev->data.motion.x, dev->data.motion.y); 838 break; 839 case AG_DRIVER_MOUSE_BUTTON_DOWN: 840 AG_ProcessMouseButtonDown(dev->win, 841 dev->data.button.x, dev->data.button.y, 842 dev->data.button.which); 843 break; 844 case AG_DRIVER_MOUSE_BUTTON_UP: 845 AG_ProcessMouseButtonUp(dev->win, 846 dev->data.button.x, dev->data.button.y, 847 dev->data.button.which); 848 break; 849 case AG_DRIVER_KEY_UP: 850 AG_ProcessKey(drv->kbd, dev->win, AG_KEY_RELEASED, 851 dev->data.key.ks, dev->data.key.ucs); 852 break; 853 case AG_DRIVER_KEY_DOWN: 854 AG_ProcessKey(drv->kbd, dev->win, AG_KEY_PRESSED, 855 dev->data.key.ks, dev->data.key.ucs); 856 break; 857 case AG_DRIVER_MOUSE_ENTER: 858 AG_PostEvent(NULL, dev->win, "window-enter", NULL); 859 break; 860 case AG_DRIVER_MOUSE_LEAVE: 861 AG_PostEvent(NULL, dev->win, "window-leave", NULL); 862 break; 863 case AG_DRIVER_FOCUS_IN: 864 agWindowFocused = dev->win; 865 AG_PostEvent(NULL, dev->win, "window-gainfocus", NULL); 866 break; 867 case AG_DRIVER_FOCUS_OUT: 868 AG_PostEvent(NULL, dev->win, "window-lostfocus", NULL); 869 break; 870 case AG_DRIVER_CLOSE: 871 AG_PostEvent(NULL, dev->win, "window-close", NULL); 872 break; 873 case AG_DRIVER_EXPOSE: 874 dev->win->dirty = 1; 875 break; 876 default: 877 rv = 0; 878 break; 879 } 880 AG_UnlockVFS(&agDrivers); 881 return (rv); 882} 883 884/* Select the window's OpenGL context. */ 885static __inline__ void 886COCOA_GL_MakeCurrent(AG_DriverCocoa *co, AG_Window *win) 887{ 888 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 889 890 [co->glCtx setView:[co->win contentView]]; 891 [co->glCtx update]; 892 [co->glCtx makeCurrentContext]; 893 894 [pool release]; 895} 896 897static void 898COCOA_BeginRendering(void *obj) 899{ 900 AG_DriverCocoa *co = obj; 901 902 AG_MutexLock(&co->lock); 903 COCOA_GL_MakeCurrent(co, AGDRIVER_MW(co)->win); 904} 905 906static void 907COCOA_RenderWindow(AG_Window *win) 908{ 909 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 910 AG_GL_Context *gl = &co->gl; 911 AG_Color c = WCOLOR(win,0); 912 913 gl->clipStates[0] = glIsEnabled(GL_CLIP_PLANE0); glEnable(GL_CLIP_PLANE0); 914 gl->clipStates[1] = glIsEnabled(GL_CLIP_PLANE1); glEnable(GL_CLIP_PLANE1); 915 gl->clipStates[2] = glIsEnabled(GL_CLIP_PLANE2); glEnable(GL_CLIP_PLANE2); 916 gl->clipStates[3] = glIsEnabled(GL_CLIP_PLANE3); glEnable(GL_CLIP_PLANE3); 917 918 glClearColor(c.r/255.0, 919 c.g/255.0, 920 c.b/255.0, 1.0); 921 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 922 923 AG_WidgetDraw(win); 924} 925 926static void 927COCOA_EndRendering(void *obj) 928{ 929 AG_DriverCocoa *co = obj; 930 AG_GL_Context *gl = &co->gl; 931 Uint i; 932 933 [co->glCtx flushBuffer]; 934 935 /* Remove textures and display lists queued for deletion. */ 936 glDeleteTextures(gl->nTextureGC, (const GLuint *)gl->textureGC); 937 for (i = 0; i < gl->nListGC; i++) { 938 glDeleteLists(gl->listGC[i], 1); 939 } 940 gl->nTextureGC = 0; 941 gl->nListGC = 0; 942 AG_MutexUnlock(&co->lock); 943} 944 945/* 946 * Window operations 947 */ 948 949static void 950SetBackgroundColor(AG_DriverCocoa *co, AG_Color C) 951{ 952 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 953 NSColor *bgColor; 954 CGFloat r, g, b, a; 955 956 r = (CGFloat)(C.r / 255.0); 957 g = (CGFloat)(C.g / 255.0); 958 b = (CGFloat)(C.b / 255.0); 959 a = (CGFloat)(C.a / 255.0); 960 bgColor = [NSColor colorWithCalibratedRed:r green:g blue:b alpha:a]; 961 [co->win setBackgroundColor:bgColor]; 962 963 [pool release]; 964} 965 966static int 967COCOA_OpenWindow(AG_Window *win, AG_Rect r, int depthReq, Uint mwFlags) 968{ 969 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 970 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 971 AG_Driver *drv = WIDGET(win)->drv; 972 Uint winStyle = 0; 973 NSRect winRect, scrRect; 974 NSArray *screens; 975 NSScreen *screen, *selScreen = nil; 976 AG_CocoaView *contentView; 977 NSOpenGLPixelFormatAttribute pfAttr[32]; 978 NSOpenGLPixelFormat *pf; 979 int i, count; 980 AG_SizeAlloc a; 981 982 AG_MutexLock(&co->lock); 983 984 /* Set the window style. */ 985 if (win->flags & AG_WINDOW_NOBORDERS) { 986 winStyle = NSBorderlessWindowMask; 987 } else { 988 if (!(win->flags & AG_WINDOW_NOTITLE)) { winStyle |= NSTitledWindowMask; } 989 if (!(win->flags & AG_WINDOW_NOCLOSE)) { winStyle |= NSClosableWindowMask; } 990 if (!(win->flags & AG_WINDOW_NOMINIMIZE)) { winStyle |= NSMiniaturizableWindowMask; } 991 if (!(win->flags & AG_WINDOW_NORESIZE)) { winStyle |= NSResizableWindowMask; } 992 } 993 994 /* Set the window coordinates. */ 995 winRect.origin.x = r.x; 996 winRect.origin.y = r.y; 997 winRect.size.width = r.w; 998 winRect.size.height = r.h; 999 ConvertNSRect(&winRect); 1000 1001 /* Select a screen. */ 1002 screens = [NSScreen screens]; 1003 count = [screens count]; 1004 for (i = 0; i < count; i++) { 1005 screen = [screens objectAtIndex:i]; 1006 scrRect = [screen frame]; 1007 1008 if (winRect.origin.x >= scrRect.origin.x && 1009 winRect.origin.y >= scrRect.origin.y && 1010 winRect.origin.x < scrRect.origin.x + scrRect.size.width && 1011 winRect.origin.y < scrRect.origin.y + scrRect.size.height) { 1012 selScreen = screen; 1013 winRect.origin.x -= scrRect.origin.x; 1014 winRect.origin.y -= scrRect.origin.y; 1015 break; 1016 } 1017 } 1018 1019 /* Create the window. */ 1020 if (selScreen == nil) { 1021 co->win = [[AG_CocoaWindow alloc] initWithContentRect:winRect 1022 styleMask:winStyle 1023 backing:NSBackingStoreBuffered 1024 defer:YES]; 1025 } else { 1026 co->win = [[AG_CocoaWindow alloc] initWithContentRect:winRect 1027 styleMask:winStyle 1028 backing:NSBackingStoreBuffered 1029 defer:YES 1030 screen:selScreen]; 1031 } 1032 co->win->_agarWindow = win; 1033 SetBackgroundColor(co, WCOLOR(win,0)); 1034 1035 if (win->flags & AG_WINDOW_MAIN) 1036 [co->win makeMainWindow]; 1037 1038 /* Create an event listener. */ 1039 co->evListener = [[AG_CocoaListener alloc] init]; 1040 1041 /* Obtain the effective window coordinates; set up our contentView. */ 1042 winRect = [co->win contentRectForFrameRect:[co->win frame]]; 1043 contentView = [[AG_CocoaView alloc] initWithFrame:winRect]; 1044 contentView->_agarWindow = win; 1045 [co->win setContentView: contentView]; 1046 [contentView release]; 1047 1048 /* Set our event listener. */ 1049 [co->evListener listen:co]; 1050 1051 /* Retrieve the effective style flags. */ 1052 winStyle = [co->win styleMask]; 1053 if (winStyle == NSBorderlessWindowMask) { 1054 win->flags |= AG_WINDOW_NOBORDERS; 1055 } else { 1056 if (!(winStyle & NSTitledWindowMask)) { win->flags |= AG_WINDOW_NOTITLE; } 1057 if (!(winStyle & NSClosableWindowMask)) { win->flags |= AG_WINDOW_NOCLOSE; } 1058 if (!(winStyle & NSMiniaturizableWindowMask)) { win->flags |= AG_WINDOW_NOMINIMIZE; } 1059 if (!(winStyle & NSResizableWindowMask)) { win->flags |= AG_WINDOW_NORESIZE; } 1060 } 1061 1062 /* Retrieve the effective maximize/minimize and focus state. */ 1063 if (!(win->flags & AG_WINDOW_NORESIZE) && [co->win isZoomed]) { 1064 win->flags |= AG_WINDOW_MAXIMIZED; 1065 } else { 1066 win->flags &= ~(AG_WINDOW_MAXIMIZED); 1067 } 1068 if ([co->win isMiniaturized]) { 1069 win->flags |= AG_WINDOW_MINIMIZED; 1070 } else { 1071 win->flags &= ~(AG_WINDOW_MINIMIZED); 1072 } 1073 if ([co->win isKeyWindow]) 1074 agWindowFocused = win; 1075 1076 /* Create an OpenGL rendering context. */ 1077 i = 0; 1078 if (depthReq != 0) { 1079 pfAttr[i++] = NSOpenGLPFADepthSize; 1080 pfAttr[i++] = depthReq; 1081 } 1082 pfAttr[i++] = NSOpenGLPFADoubleBuffer; 1083 if (agStereo) { pfAttr[i++] = NSOpenGLPFAStereo; } 1084 pfAttr[i] = 0; 1085 pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:pfAttr]; 1086 if (pf == nil) { 1087 AG_SetError("Cannot create NSOpenGLPixelFormat"); 1088 goto fail; 1089 } 1090 co->glCtx = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil]; 1091 [pf release]; 1092 if (co->glCtx == nil) { 1093 AG_SetError("Cannot create NSOpenGLContext"); 1094 goto fail; 1095 } 1096 [co->glCtx update]; 1097 [co->glCtx makeCurrentContext]; 1098 if (AG_GL_InitContext(co, &co->gl) == -1) 1099 goto fail; 1100 1101 /* Set the preferred Agar pixel formats. */ 1102 /* XXX XXX XXX: retrieve effective depth */ 1103 drv->videoFmt = AG_PixelFormatRGB(depthReq != 0 ? depthReq : 16, 1104#if AG_BYTEORDER == AG_BIG_ENDIAN 1105 0xff000000, 0x00ff0000, 0x0000ff00 1106#else 1107 0x000000ff, 0x0000ff00, 0x00ff0000 1108#endif 1109 ); 1110 if (drv->videoFmt == NULL) 1111 goto fail_ctx; 1112 1113 /* 1114 * Set the effective window geometry, initialize the viewport 1115 * and tracking rectangle. 1116 */ 1117 ConvertNSRect(&winRect); 1118 a.x = winRect.origin.x; 1119 a.y = winRect.origin.y; 1120 a.w = winRect.size.width; 1121 a.h = winRect.size.height; 1122 COCOA_PostResizeCallback(win, &a); 1123 1124 AG_MutexUnlock(&co->lock); 1125 [pool release]; 1126 return (0); 1127fail_ctx: 1128 AG_GL_DestroyContext(co); 1129fail: 1130 [NSOpenGLContext clearCurrentContext]; 1131 [co->evListener close]; 1132 [co->evListener release]; 1133 [co->win close]; 1134 if (drv->videoFmt) { 1135 AG_PixelFormatFree(drv->videoFmt); 1136 drv->videoFmt = NULL; 1137 } 1138 AG_MutexUnlock(&co->lock); 1139 [pool release]; 1140 return (-1); 1141} 1142 1143static void 1144COCOA_CloseWindow(AG_Window *win) 1145{ 1146 AG_Driver *drv = WIDGET(win)->drv; 1147 AG_DriverCocoa *co = (AG_DriverCocoa *)drv; 1148 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1149 1150 AG_MutexLock(&co->lock); 1151 1152#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 1153 if (co->trackArea != nil) { 1154 [[co->win contentView] removeTrackingArea:co->trackArea]; 1155 [co->trackArea release]; 1156 co->trackArea = nil; 1157 } 1158#endif 1159 /* Destroy our OpenGL rendering context. */ 1160 COCOA_GL_MakeCurrent(co, win); 1161 AG_GL_DestroyContext(drv); 1162 [co->glCtx clearDrawable]; 1163 [co->glCtx release]; 1164 1165 /* Close the window. */ 1166 [co->evListener close]; 1167 [co->evListener release]; 1168 [co->win close]; 1169 1170 AG_PixelFormatFree(drv->videoFmt); 1171 drv->videoFmt = NULL; 1172 1173 AG_MutexUnlock(&co->lock); 1174 [pool release]; 1175} 1176 1177static int 1178COCOA_MapWindow(AG_Window *win) 1179{ 1180 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1181 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1182 1183 AG_MutexLock(&co->lock); 1184 if (![co->win isMiniaturized]) { 1185 [co->win makeKeyAndOrderFront:nil]; 1186 } 1187 AG_MutexUnlock(&co->lock); 1188 1189 [pool release]; 1190 return (0); 1191} 1192 1193static int 1194COCOA_UnmapWindow(AG_Window *win) 1195{ 1196 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1197 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1198 1199 AG_MutexLock(&co->lock); 1200 [co->win orderOut:nil]; 1201 AG_MutexUnlock(&co->lock); 1202 1203 [pool release]; 1204 return (0); 1205} 1206 1207static int 1208COCOA_RaiseWindow(AG_Window *win) 1209{ 1210 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1211 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1212 1213 AG_MutexLock(&co->lock); 1214 [co->win makeKeyAndOrderFront:nil]; 1215 AG_MutexUnlock(&co->lock); 1216 1217 [pool release]; 1218 return (0); 1219} 1220 1221static int 1222COCOA_LowerWindow(AG_Window *win) 1223{ 1224 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1225 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1226 1227 AG_MutexLock(&co->lock); 1228 if (![co->win isMiniaturized]) { 1229 [co->win orderBack:nil]; 1230 } 1231 AG_MutexUnlock(&co->lock); 1232 1233 [pool release]; 1234 return (0); 1235} 1236 1237static int 1238COCOA_ReparentWindow(AG_Window *win, AG_Window *winParent, int x, int y) 1239{ 1240 /* TODO */ 1241 AG_SetError("Reparent window not implemented"); 1242 return (-1); 1243} 1244 1245static int 1246COCOA_GetInputFocus(AG_Window **rv) 1247{ 1248 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1249 AG_DriverCocoa *co = NULL; 1250 1251 AGOBJECT_FOREACH_CHILD(co, &agDrivers, ag_driver_cocoa) { 1252 if (!AGDRIVER_IS_COCOA(co)) { 1253 continue; 1254 } 1255 AG_MutexLock(&co->lock); 1256 if ([co->win isKeyWindow]) { 1257 AG_MutexUnlock(&co->lock); 1258 break; 1259 } 1260 AG_MutexUnlock(&co->lock); 1261 } 1262 if (co == NULL) { 1263 AG_SetError("Input focus is external to this application"); 1264 goto fail; 1265 } 1266 *rv = AGDRIVER_MW(co)->win; 1267 [pool release]; 1268 return (0); 1269fail: 1270 [pool release]; 1271 return (-1); 1272} 1273 1274static int 1275COCOA_SetInputFocus(AG_Window *win) 1276{ 1277 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1278 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1279 1280 AG_MutexLock(&co->lock); 1281 /* XXX use makeKeyWindow? */ 1282 [co->win makeKeyAndOrderFront:nil]; 1283 AG_MutexUnlock(&co->lock); 1284 1285 [pool release]; 1286 return (0); 1287} 1288 1289static void 1290COCOA_PreResizeCallback(AG_Window *win) 1291{ 1292#if 0 1293 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1294 1295 /* 1296 * Backup all GL resources since it is not portable to assume that a 1297 * display resize will not cause a change in GL contexts 1298 * (XXX TODO test for platforms where this is unnecessary) 1299 * (XXX is this correctly done?) 1300 */ 1301 COCOA_GL_MakeCurrent(co, win); 1302 COCOA_FreeWidgetResources(WIDGET(win)); 1303 AG_TextClearGlyphCache(co); 1304#endif 1305} 1306 1307static void 1308COCOA_PostResizeCallback(AG_Window *win, AG_SizeAlloc *a) 1309{ 1310 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1311 AG_Driver *drv = WIDGET(win)->drv; 1312 AG_DriverCocoa *co = (AG_DriverCocoa *)drv; 1313 int x = a->x; 1314 int y = a->y; 1315 NSRect trackRect; 1316 1317 AG_MutexLock(&co->lock); 1318 1319 /* Update per-widget coordinate information. */ 1320 a->x = 0; 1321 a->y = 0; 1322 (void)AG_WidgetSizeAlloc(win, a); 1323 AG_WidgetUpdateCoords(win, 0, 0); 1324 1325 /* The viewport coordinates have changed. */ 1326 [co->glCtx makeCurrentContext]; 1327 AG_GL_SetViewport(&co->gl, AG_RECT(0, 0, WIDTH(win), HEIGHT(win))); 1328 1329#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 1330 /* Update the tracking rectangle. */ 1331 if (co->trackArea != nil) { 1332 [[co->win contentView] removeTrackingArea:co->trackArea]; 1333 [co->trackArea release]; 1334 co->trackArea = nil; 1335 } 1336 trackRect.origin.x = 0; 1337 trackRect.origin.y = 0; 1338 trackRect.size.width = WIDTH(win); 1339 trackRect.size.height = HEIGHT(win); 1340 co->trackArea = [[NSTrackingArea alloc] initWithRect:trackRect 1341 options: (NSTrackingMouseEnteredAndExited| 1342 NSTrackingMouseMoved| 1343 NSTrackingActiveAlways) 1344 owner:co->win userInfo:nil]; 1345 [[co->win contentView] addTrackingArea:co->trackArea]; 1346#endif /* >= 10.5 */ 1347 1348 AG_MutexUnlock(&co->lock); 1349 1350 /* Save the new effective window position. */ 1351 WIDGET(win)->x = a->x = x; 1352 WIDGET(win)->y = a->y = y; 1353 1354 [pool release]; 1355} 1356 1357static void 1358COCOA_PostMoveCallback(AG_Window *win, AG_SizeAlloc *a) 1359{ 1360 AG_Driver *drv = WIDGET(win)->drv; 1361 AG_DriverCocoa *co = (AG_DriverCocoa *)drv; 1362 AG_SizeAlloc aNew; 1363 int xRel, yRel; 1364 1365 AG_MutexLock(&co->lock); 1366 1367 xRel = a->x - WIDGET(win)->x; 1368 yRel = a->y - WIDGET(win)->y; 1369 1370 /* Update the window coordinates. */ 1371 aNew.x = 0; 1372 aNew.y = 0; 1373 aNew.w = a->w; 1374 aNew.h = a->h; 1375 AG_WidgetSizeAlloc(win, &aNew); 1376 AG_WidgetUpdateCoords(win, 0, 0); 1377 WIDGET(win)->x = a->x; 1378 WIDGET(win)->y = a->y; 1379 win->dirty = 1; 1380 1381 /* Move other windows pinned to this one. */ 1382 AG_WindowMovePinned(win, xRel, yRel); 1383 1384 AG_MutexUnlock(&co->lock); 1385} 1386 1387static int 1388COCOA_MoveWindow(AG_Window *win, int x, int y) 1389{ 1390 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1391 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1392 NSScreen *screen = [co->win screen]; 1393 NSRect scrRect = [screen frame]; 1394 NSPoint pt; 1395 1396 pt.x = x; 1397 pt.y = scrRect.size.height - y; 1398 1399 AG_MutexLock(&co->lock); 1400 [co->win setFrameTopLeftPoint:pt]; 1401 AG_MutexUnlock(&co->lock); 1402 1403 [pool release]; 1404 return (0); 1405} 1406 1407#if 0 1408/* Save/restore associated widget GL resources (for GL context changes). */ 1409static void 1410COCOA_FreeWidgetResources(AG_Widget *wid) 1411{ 1412 AG_Widget *chld; 1413 1414 OBJECT_FOREACH_CHILD(chld, wid, ag_widget) { 1415 COCOA_FreeWidgetResources(chld); 1416 } 1417 AG_WidgetFreeResourcesGL(wid); 1418} 1419#endif 1420 1421static int 1422COCOA_ResizeWindow(AG_Window *win, Uint w, Uint h) 1423{ 1424 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1425 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1426 NSSize sz; 1427 1428 sz.width = w; 1429 sz.height = h; 1430 1431 AG_MutexLock(&co->lock); 1432 [co->win setContentSize:sz]; 1433 AG_MutexUnlock(&co->lock); 1434 1435 [pool release]; 1436 return (0); 1437} 1438 1439static int 1440COCOA_MoveResizeWindow(AG_Window *win, AG_SizeAlloc *a) 1441{ 1442 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1443 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1444 NSScreen *screen = [co->win screen]; 1445 NSRect scrRect = [screen frame]; 1446 NSPoint pt; 1447 NSSize sz; 1448 1449 pt.x = a->x; 1450 pt.y = scrRect.size.height - a->y; 1451 sz.width = a->w; 1452 sz.height = a->h; 1453 1454 AG_MutexLock(&co->lock); 1455 [co->win setFrameTopLeftPoint:pt]; 1456 [co->win setContentSize:sz]; 1457 AG_MutexUnlock(&co->lock); 1458 1459 [pool release]; 1460 return (0); 1461} 1462 1463static int 1464COCOA_SetBorderWidth(AG_Window *win, Uint width) 1465{ 1466 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1467 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1468 1469 AG_MutexLock(&co->lock); 1470 if (([co->win styleMask] & NSTexturedBackgroundWindowMask) == 0) { 1471 [co->win setContentBorderThickness:width forEdge:NSMaxYEdge]; 1472 } 1473 [co->win setContentBorderThickness:width forEdge:NSMinYEdge]; 1474 AG_MutexUnlock(&co->lock); 1475 1476 [pool release]; 1477 return (0); 1478} 1479 1480static int 1481COCOA_SetWindowCaption(AG_Window *win, const char *s) 1482{ 1483 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1484 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1485 NSString *string; 1486 1487 if (win->caption[0] != '\0') { 1488 string = [[NSString alloc] initWithUTF8String:win->caption]; 1489 } else { 1490 string = [[NSString alloc] init]; 1491 } 1492 1493 AG_MutexLock(&co->lock); 1494 [co->win setTitle:string]; 1495 AG_MutexUnlock(&co->lock); 1496 1497 [string release]; 1498 [pool release]; 1499 return (0); 1500} 1501 1502static int 1503COCOA_SetOpacity(AG_Window *win, float f) 1504{ 1505 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 1506 AG_DriverCocoa *co = (AG_DriverCocoa *)WIDGET(win)->drv; 1507 1508 AG_MutexLock(&co->lock); 1509 if (f > 0.99) { 1510 [co->win setOpaque:YES]; 1511 [co->win setAlphaValue:1.0]; 1512 } else { 1513 [co->win setOpaque:NO]; 1514 [co->win setAlphaValue:f]; 1515 } 1516 AG_MutexUnlock(&co->lock); 1517 1518 [pool release]; 1519 return (0); 1520} 1521 1522static void 1523COCOA_TweakAlignment(AG_Window *win, AG_SizeAlloc *a, Uint wMax, Uint hMax) 1524{ 1525 /* XXX TODO */ 1526 switch (win->alignment) { 1527 case AG_WINDOW_TL: 1528 case AG_WINDOW_TC: 1529 case AG_WINDOW_TR: 1530 a->y += 50; 1531 if (a->y+a->h > hMax) { a->y = 0; } 1532 break; 1533 case AG_WINDOW_BL: 1534 case AG_WINDOW_BC: 1535 case AG_WINDOW_BR: 1536 a->y -= 100; 1537 if (a->y < 0) { a->y = 0; } 1538 break; 1539 default: 1540 break; 1541 } 1542} 1543 1544AG_DriverMwClass agDriverCocoa = { 1545 { 1546 { 1547 "AG_Driver:AG_DriverMw:AG_DriverCocoa", 1548 sizeof(AG_DriverCocoa), 1549 { 1,5 }, 1550 Init, 1551 NULL, /* reinit */ 1552 Destroy, 1553 NULL, /* load */ 1554 NULL, /* save */ 1555 NULL, /* edit */ 1556 }, 1557 "cocoa", 1558 AG_VECTOR, 1559 AG_WM_MULTIPLE, 1560 AG_DRIVER_OPENGL|AG_DRIVER_TEXTURES, 1561 COCOA_Open, 1562 COCOA_Close, 1563 COCOA_GetDisplaySize, 1564 NULL, /* beginEventProcessing */ 1565 COCOA_PendingEvents, 1566 COCOA_GetNextEvent, 1567 COCOA_ProcessEvent, 1568 NULL, /* genericEventLoop */ 1569 NULL, /* endEventProcessing */ 1570 NULL, /* terminate */ 1571 COCOA_BeginRendering, 1572 COCOA_RenderWindow, 1573 COCOA_EndRendering, 1574 AG_GL_FillRect, 1575 NULL, /* updateRegion */ 1576 AG_GL_StdUploadTexture, 1577 AG_GL_StdUpdateTexture, 1578 AG_GL_StdDeleteTexture, 1579 NULL, /* setRefreshRate */ 1580 AG_GL_StdPushClipRect, 1581 AG_GL_StdPopClipRect, 1582 AG_GL_StdPushBlendingMode, 1583 AG_GL_StdPopBlendingMode, 1584 NULL, /* createCursor */ 1585 NULL, /* freeCursor */ 1586 NULL, /* setCursor */ 1587 NULL, /* unsetCursor */ 1588 NULL, /* getCursorVisibility */ 1589 NULL, /* setCursorVisibility */ 1590 AG_GL_BlitSurface, 1591 AG_GL_BlitSurfaceFrom, 1592 AG_GL_BlitSurfaceGL, 1593 AG_GL_BlitSurfaceFromGL, 1594 AG_GL_BlitSurfaceFlippedGL, 1595 AG_GL_BackupSurfaces, 1596 AG_GL_RestoreSurfaces, 1597 AG_GL_RenderToSurface, 1598 AG_GL_PutPixel, 1599 AG_GL_PutPixel32, 1600 AG_GL_PutPixelRGB, 1601 AG_GL_BlendPixel, 1602 AG_GL_DrawLine, 1603 AG_GL_DrawLineH, 1604 AG_GL_DrawLineV, 1605 AG_GL_DrawLineBlended, 1606 AG_GL_DrawArrowUp, 1607 AG_GL_DrawArrowDown, 1608 AG_GL_DrawArrowLeft, 1609 AG_GL_DrawArrowRight, 1610 AG_GL_DrawBoxRounded, 1611 AG_GL_DrawBoxRoundedTop, 1612 AG_GL_DrawCircle, 1613 AG_GL_DrawCircleFilled, 1614 AG_GL_DrawRectFilled, 1615 AG_GL_DrawRectBlended, 1616 AG_GL_DrawRectDithered, 1617 AG_GL_UpdateGlyph, 1618 AG_GL_DrawGlyph, 1619 AG_GL_StdDeleteList 1620 }, 1621 COCOA_OpenWindow, 1622 COCOA_CloseWindow, 1623 COCOA_MapWindow, 1624 COCOA_UnmapWindow, 1625 COCOA_RaiseWindow, 1626 COCOA_LowerWindow, 1627 COCOA_ReparentWindow, 1628 COCOA_GetInputFocus, 1629 COCOA_SetInputFocus, 1630 COCOA_MoveWindow, 1631 COCOA_ResizeWindow, 1632 COCOA_MoveResizeWindow, 1633 COCOA_PreResizeCallback, 1634 COCOA_PostResizeCallback, 1635 NULL, /* captureWindow */ 1636 COCOA_SetBorderWidth, 1637 COCOA_SetWindowCaption, 1638 NULL, /* setTransientFor */ 1639 COCOA_SetOpacity, 1640 COCOA_TweakAlignment 1641}; 1642