Blender V4.5
GHOST_SystemCocoa.mm
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
10#include "GHOST_EventKey.hh"
11#include "GHOST_EventString.hh"
13#include "GHOST_EventWheel.hh"
14#include "GHOST_TimerManager.hh"
15#include "GHOST_TimerTask.hh"
16#include "GHOST_WindowCocoa.hh"
18
19/* Don't generate OpenGL deprecation warning. This is a known thing, and is not something easily
20 * solvable in a short term. */
21#ifdef __clang__
22# pragma clang diagnostic ignored "-Wdeprecated-declarations"
23#endif
24
25#ifdef WITH_METAL_BACKEND
26# include "GHOST_ContextCGL.hh"
27#endif
28
29#ifdef WITH_VULKAN_BACKEND
30# include "GHOST_ContextVK.hh"
31#endif
32
33#ifdef WITH_INPUT_NDOF
35#endif
36
37#include "AssertMacros.h"
38
39#import <Cocoa/Cocoa.h>
40
41/* For the currently not ported to Cocoa keyboard layout functions (64bit & 10.6 compatible) */
42#include <Carbon/Carbon.h>
43
44#include <sys/sysctl.h>
45#include <sys/time.h>
46#include <sys/types.h>
47
48#include <mach/mach_time.h>
49
50/* --------------------------------------------------------------------
51 * Keymaps, mouse converters.
52 */
53
54static GHOST_TButton convertButton(int button)
55{
56 switch (button) {
57 case 0:
59 case 1:
61 case 2:
63 case 3:
65 case 4:
67 case 5:
69 case 6:
71 default:
73 }
74}
75
83static GHOST_TKey convertKey(int rawCode, unichar recvChar)
84{
85 // printf("\nrecvchar %c 0x%x",recvChar,recvChar);
86 switch (rawCode) {
87 /* Physical key-codes: (not used due to map changes in international keyboards). */
88#if 0
89 case kVK_ANSI_A: return GHOST_kKeyA;
90 case kVK_ANSI_B: return GHOST_kKeyB;
91 case kVK_ANSI_C: return GHOST_kKeyC;
92 case kVK_ANSI_D: return GHOST_kKeyD;
93 case kVK_ANSI_E: return GHOST_kKeyE;
94 case kVK_ANSI_F: return GHOST_kKeyF;
95 case kVK_ANSI_G: return GHOST_kKeyG;
96 case kVK_ANSI_H: return GHOST_kKeyH;
97 case kVK_ANSI_I: return GHOST_kKeyI;
98 case kVK_ANSI_J: return GHOST_kKeyJ;
99 case kVK_ANSI_K: return GHOST_kKeyK;
100 case kVK_ANSI_L: return GHOST_kKeyL;
101 case kVK_ANSI_M: return GHOST_kKeyM;
102 case kVK_ANSI_N: return GHOST_kKeyN;
103 case kVK_ANSI_O: return GHOST_kKeyO;
104 case kVK_ANSI_P: return GHOST_kKeyP;
105 case kVK_ANSI_Q: return GHOST_kKeyQ;
106 case kVK_ANSI_R: return GHOST_kKeyR;
107 case kVK_ANSI_S: return GHOST_kKeyS;
108 case kVK_ANSI_T: return GHOST_kKeyT;
109 case kVK_ANSI_U: return GHOST_kKeyU;
110 case kVK_ANSI_V: return GHOST_kKeyV;
111 case kVK_ANSI_W: return GHOST_kKeyW;
112 case kVK_ANSI_X: return GHOST_kKeyX;
113 case kVK_ANSI_Y: return GHOST_kKeyY;
114 case kVK_ANSI_Z: return GHOST_kKeyZ;
115#endif
116 /* Numbers keys: mapped to handle some international keyboard (e.g. French). */
117 case kVK_ANSI_1:
118 return GHOST_kKey1;
119 case kVK_ANSI_2:
120 return GHOST_kKey2;
121 case kVK_ANSI_3:
122 return GHOST_kKey3;
123 case kVK_ANSI_4:
124 return GHOST_kKey4;
125 case kVK_ANSI_5:
126 return GHOST_kKey5;
127 case kVK_ANSI_6:
128 return GHOST_kKey6;
129 case kVK_ANSI_7:
130 return GHOST_kKey7;
131 case kVK_ANSI_8:
132 return GHOST_kKey8;
133 case kVK_ANSI_9:
134 return GHOST_kKey9;
135 case kVK_ANSI_0:
136 return GHOST_kKey0;
137
138 case kVK_ANSI_Keypad0:
139 return GHOST_kKeyNumpad0;
140 case kVK_ANSI_Keypad1:
141 return GHOST_kKeyNumpad1;
142 case kVK_ANSI_Keypad2:
143 return GHOST_kKeyNumpad2;
144 case kVK_ANSI_Keypad3:
145 return GHOST_kKeyNumpad3;
146 case kVK_ANSI_Keypad4:
147 return GHOST_kKeyNumpad4;
148 case kVK_ANSI_Keypad5:
149 return GHOST_kKeyNumpad5;
150 case kVK_ANSI_Keypad6:
151 return GHOST_kKeyNumpad6;
152 case kVK_ANSI_Keypad7:
153 return GHOST_kKeyNumpad7;
154 case kVK_ANSI_Keypad8:
155 return GHOST_kKeyNumpad8;
156 case kVK_ANSI_Keypad9:
157 return GHOST_kKeyNumpad9;
158 case kVK_ANSI_KeypadDecimal:
160 case kVK_ANSI_KeypadEnter:
162 case kVK_ANSI_KeypadPlus:
164 case kVK_ANSI_KeypadMinus:
166 case kVK_ANSI_KeypadMultiply:
168 case kVK_ANSI_KeypadDivide:
170 case kVK_ANSI_KeypadClear:
171 return GHOST_kKeyUnknown;
172
173 case kVK_F1:
174 return GHOST_kKeyF1;
175 case kVK_F2:
176 return GHOST_kKeyF2;
177 case kVK_F3:
178 return GHOST_kKeyF3;
179 case kVK_F4:
180 return GHOST_kKeyF4;
181 case kVK_F5:
182 return GHOST_kKeyF5;
183 case kVK_F6:
184 return GHOST_kKeyF6;
185 case kVK_F7:
186 return GHOST_kKeyF7;
187 case kVK_F8:
188 return GHOST_kKeyF8;
189 case kVK_F9:
190 return GHOST_kKeyF9;
191 case kVK_F10:
192 return GHOST_kKeyF10;
193 case kVK_F11:
194 return GHOST_kKeyF11;
195 case kVK_F12:
196 return GHOST_kKeyF12;
197 case kVK_F13:
198 return GHOST_kKeyF13;
199 case kVK_F14:
200 return GHOST_kKeyF14;
201 case kVK_F15:
202 return GHOST_kKeyF15;
203 case kVK_F16:
204 return GHOST_kKeyF16;
205 case kVK_F17:
206 return GHOST_kKeyF17;
207 case kVK_F18:
208 return GHOST_kKeyF18;
209 case kVK_F19:
210 return GHOST_kKeyF19;
211 case kVK_F20:
212 return GHOST_kKeyF20;
213
214 case kVK_UpArrow:
215 return GHOST_kKeyUpArrow;
216 case kVK_DownArrow:
217 return GHOST_kKeyDownArrow;
218 case kVK_LeftArrow:
219 return GHOST_kKeyLeftArrow;
220 case kVK_RightArrow:
222
223 case kVK_Return:
224 return GHOST_kKeyEnter;
225 case kVK_Delete:
226 return GHOST_kKeyBackSpace;
227 case kVK_ForwardDelete:
228 return GHOST_kKeyDelete;
229 case kVK_Escape:
230 return GHOST_kKeyEsc;
231 case kVK_Tab:
232 return GHOST_kKeyTab;
233 case kVK_Space:
234 return GHOST_kKeySpace;
235
236 case kVK_Home:
237 return GHOST_kKeyHome;
238 case kVK_End:
239 return GHOST_kKeyEnd;
240 case kVK_PageUp:
241 return GHOST_kKeyUpPage;
242 case kVK_PageDown:
243 return GHOST_kKeyDownPage;
244#if 0
245 /* These constants with "ANSI" in the name are labeled according to the key position on an
246 * ANSI-standard US keyboard. Therefore they may not match the physical key label on other
247 * keyboard layouts. */
248 case kVK_ANSI_Minus: return GHOST_kKeyMinus;
249 case kVK_ANSI_Equal: return GHOST_kKeyEqual;
250 case kVK_ANSI_Comma: return GHOST_kKeyComma;
251 case kVK_ANSI_Period: return GHOST_kKeyPeriod;
252 case kVK_ANSI_Slash: return GHOST_kKeySlash;
253 case kVK_ANSI_Semicolon: return GHOST_kKeySemicolon;
254 case kVK_ANSI_Quote: return GHOST_kKeyQuote;
255 case kVK_ANSI_Backslash: return GHOST_kKeyBackslash;
256 case kVK_ANSI_LeftBracket: return GHOST_kKeyLeftBracket;
257 case kVK_ANSI_RightBracket: return GHOST_kKeyRightBracket;
258 case kVK_ANSI_Grave: return GHOST_kKeyAccentGrave;
259 case kVK_ISO_Section: return GHOST_kKeyUnknown;
260#endif
261 case kVK_VolumeUp:
262 case kVK_VolumeDown:
263 case kVK_Mute:
264 return GHOST_kKeyUnknown;
265
266 default: {
267 /* Alphanumerical or punctuation key that is remappable in international keyboards. */
268 if ((recvChar >= 'A') && (recvChar <= 'Z')) {
269 return (GHOST_TKey)(recvChar - 'A' + GHOST_kKeyA);
270 }
271
272 if ((recvChar >= 'a') && (recvChar <= 'z')) {
273 return (GHOST_TKey)(recvChar - 'a' + GHOST_kKeyA);
274 }
275 else {
276 /* Leopard and Snow Leopard 64bit compatible API. */
277 const TISInputSourceRef kbdTISHandle = TISCopyCurrentKeyboardLayoutInputSource();
278 /* The keyboard layout. */
279 const CFDataRef uchrHandle = static_cast<CFDataRef>(
280 TISGetInputSourceProperty(kbdTISHandle, kTISPropertyUnicodeKeyLayoutData));
281 CFRelease(kbdTISHandle);
282
283 /* Get actual character value of the "remappable" keys in international keyboards,
284 * if keyboard layout is not correctly reported (e.g. some non Apple keyboards in Tiger),
285 * then fall back on using the received #charactersIgnoringModifiers. */
286 if (uchrHandle) {
287 UInt32 deadKeyState = 0;
288 UniCharCount actualStrLength = 0;
289
290 UCKeyTranslate((UCKeyboardLayout *)CFDataGetBytePtr(uchrHandle),
291 rawCode,
292 kUCKeyActionDown,
293 0,
294 LMGetKbdType(),
295 kUCKeyTranslateNoDeadKeysMask,
296 &deadKeyState,
297 1,
298 &actualStrLength,
299 &recvChar);
300 }
301
302 switch (recvChar) {
303 case '-':
304 return GHOST_kKeyMinus;
305 case '+':
306 return GHOST_kKeyPlus;
307 case '=':
308 return GHOST_kKeyEqual;
309 case ',':
310 return GHOST_kKeyComma;
311 case '.':
312 return GHOST_kKeyPeriod;
313 case '/':
314 return GHOST_kKeySlash;
315 case ';':
316 return GHOST_kKeySemicolon;
317 case '\'':
318 return GHOST_kKeyQuote;
319 case '\\':
320 return GHOST_kKeyBackslash;
321 case '[':
323 case ']':
325 case '`':
326 case '<': /* The position of '`' is equivalent to this symbol in the French layout. */
328 default:
329 return GHOST_kKeyUnknown;
330 }
331 }
332 }
333 }
334 return GHOST_kKeyUnknown;
335}
336
337/* --------------------------------------------------------------------
338 * Utility functions.
339 */
340
341#define FIRSTFILEBUFLG 512
342static bool g_hasFirstFile = false;
344
345/* TODO: Need to investigate this.
346 * Function called too early in creator.c to have g_hasFirstFile == true */
348{
349 if (g_hasFirstFile) {
350 memcpy(buf, g_firstFileBuf, FIRSTFILEBUFLG);
351 buf[FIRSTFILEBUFLG - 1] = '\0';
352 return 1;
353 }
354 return 0;
355}
356
357/* --------------------------------------------------------------------
358 * Cocoa objects.
359 */
360
365@interface CocoaAppDelegate : NSObject <NSApplicationDelegate>
366
367@property(nonatomic, readonly, assign) GHOST_SystemCocoa *systemCocoa;
368
369- (instancetype)initWithSystemCocoa:(GHOST_SystemCocoa *)systemCocoa;
370- (void)dealloc;
371- (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
372- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
373- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
374- (void)applicationWillTerminate:(NSNotification *)aNotification;
375- (void)applicationWillBecomeActive:(NSNotification *)aNotification;
376- (void)toggleFullScreen:(NSNotification *)notification;
377- (void)windowWillClose:(NSNotification *)notification;
378
379- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app;
380
381@end
382
383@implementation CocoaAppDelegate : NSObject
384
385@synthesize systemCocoa = m_systemCocoa;
386
387- (instancetype)initWithSystemCocoa:(GHOST_SystemCocoa *)systemCocoa
388{
389 self = [super init];
390
391 if (self) {
392 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
393 [center addObserver:self
394 selector:@selector(windowWillClose:)
395 name:NSWindowWillCloseNotification
396 object:nil];
397 m_systemCocoa = systemCocoa;
398 }
399
400 return self;
401}
402
403- (void)dealloc
404{
405 @autoreleasepool {
406 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
407 [center removeObserver:self name:NSWindowWillCloseNotification object:nil];
408 [super dealloc];
409 }
410}
411
412- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
413{
414 if (m_systemCocoa->m_windowFocus) {
415 /* Raise application to front, convenient when starting from the terminal
416 * and important for launching the animation player. we call this after the
417 * application finishes launching, as doing it earlier can make us end up
418 * with a front-most window but an inactive application. */
419 [NSApp activateIgnoringOtherApps:YES];
420 }
421
422 [NSEvent setMouseCoalescingEnabled:NO];
423}
424
425- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
426{
427 return m_systemCocoa->handleOpenDocumentRequest(filename);
428}
429
430- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
431{
432 /* TODO: implement graceful termination through Cocoa mechanism
433 * to avoid session log off to be canceled. */
434 /* Note that Command-Q is already handled by key-handler. */
435 m_systemCocoa->handleQuitRequest();
436 return NSTerminateCancel;
437}
438
439/* To avoid canceling a log off process, we must use Cocoa termination process
440 * And this function is the only chance to perform clean up
441 * So WM_exit needs to be called directly, as the event loop will never run before termination. */
442- (void)applicationWillTerminate:(NSNotification *)aNotification
443{
444#if 0
445 WM_exit(C, EXIT_SUCCESS);
446#endif
447}
448
449- (void)applicationWillBecomeActive:(NSNotification *)aNotification
450{
451 m_systemCocoa->handleApplicationBecomeActiveEvent();
452}
453
454- (void)toggleFullScreen:(NSNotification *)notification
455{
456}
457
458/* The purpose of this function is to make sure closing "About" window does not
459 * leave Blender with no key windows. This is needed due to a custom event loop
460 * nature of the application: for some reason only using [NSApp run] will ensure
461 * correct behavior in this case.
462 *
463 * This is similar to an issue solved in SDL:
464 * https://bugzilla.libsdl.org/show_bug.cgi?id=1825
465 *
466 * Our solution is different, since we want Blender to keep track of what is
467 * the key window during normal operation. In order to do so we exploit the
468 * fact that "About" window is never in the orderedWindows array: we only force
469 * key window from here if the closing one is not in the orderedWindows. This
470 * saves lack of key windows when closing "About", but does not interfere with
471 * Blender's window manager when closing Blender's windows.
472 *
473 * NOTE: It also receives notifiers when menus are closed on macOS 14.
474 * Presumably it considers menus to be windows. */
475- (void)windowWillClose:(NSNotification *)notification
476{
477 @autoreleasepool {
478 NSWindow *closing_window = (NSWindow *)[notification object];
479
480 if (![closing_window isKeyWindow]) {
481 /* If the window wasn't key then its either none of the windows are key or another window
482 * is a key. The former situation is a bit strange, but probably forcing a key window is not
483 * something desirable. The latter situation is when we definitely do not want to change the
484 * key window.
485 *
486 * Ignoring non-key windows also avoids the code which ensures ordering below from running
487 * when the notifier is received for menus on macOS 14. */
488 return;
489 }
490
491 const NSInteger index = [[NSApp orderedWindows] indexOfObject:closing_window];
492 if (index != NSNotFound) {
493 return;
494 }
495 /* Find first suitable window from the current space. */
496 for (NSWindow *current_window in [NSApp orderedWindows]) {
497 if (current_window == closing_window) {
498 continue;
499 }
500 if (current_window.isOnActiveSpace && current_window.canBecomeKeyWindow) {
501 [current_window makeKeyAndOrderFront:nil];
502 return;
503 }
504 }
505 /* If that didn't find any windows, we try to find any suitable window of the application. */
506 for (NSNumber *window_number in [NSWindow windowNumbersWithOptions:0]) {
507 NSWindow *current_window = [NSApp windowWithWindowNumber:[window_number integerValue]];
508 if (current_window == closing_window) {
509 continue;
510 }
511 if ([current_window canBecomeKeyWindow]) {
512 [current_window makeKeyAndOrderFront:nil];
513 return;
514 }
515 }
516 }
517}
518
519/* Explicitly opt-in to the secure coding for the restorable state.
520 *
521 * This is something that only has affect on macOS 12+, and is implicitly
522 * enabled on macOS 14.
523 *
524 * For the details see
525 * https://sector7.computest.nl/post/2022-08-process-injection-breaking-all-macos-security-layers-with-a-single-vulnerability/
526 */
527- (BOOL)applicationSupportsSecureRestorableState:(NSApplication *)app
528{
529 return YES;
530}
531
532@end
533
534/* --------------------------------------------------------------------
535 * Initialization / Finalization.
536 */
537
549
551{
552 /* The application delegate integrates the Cocoa application with the GHOST system.
553 *
554 * Since the GHOST system is about to be fully destroyed release the application delegate as
555 * well, so it does not point back to a freed system, forcing the delegate to be created with the
556 * new GHOST system in init(). */
557 @autoreleasepool {
558 CocoaAppDelegate *appDelegate = (CocoaAppDelegate *)[NSApp delegate];
559 if (appDelegate) {
560 [NSApp setDelegate:nil];
561 [appDelegate release];
562 }
563 }
564}
565
567{
569 if (success) {
570
571#ifdef WITH_INPUT_NDOF
572 m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
573#endif
574
575 // ProcessSerialNumber psn;
576
577 /* Carbon stuff to move window & menu to foreground. */
578#if 0
579 if (!GetCurrentProcess(&psn)) {
580 TransformProcessType(&psn, kProcessTransformToForegroundApplication);
581 SetFrontProcess(&psn);
582 }
583#endif
584
585 @autoreleasepool {
586 [NSApplication sharedApplication]; /* initializes `NSApp`. */
587
588 if ([NSApp mainMenu] == nil) {
589 NSMenu *mainMenubar = [[NSMenu alloc] init];
590 NSMenuItem *menuItem;
591 NSMenu *windowMenu;
592 NSMenu *appMenu;
593
594 /* Create the application menu. */
595 appMenu = [[NSMenu alloc] initWithTitle:@"Blender"];
596
597 [appMenu addItemWithTitle:@"About Blender"
598 action:@selector(orderFrontStandardAboutPanel:)
599 keyEquivalent:@""];
600 [appMenu addItem:[NSMenuItem separatorItem]];
601
602 menuItem = [appMenu addItemWithTitle:@"Hide Blender"
603 action:@selector(hide:)
604 keyEquivalent:@"h"];
605 menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand;
606
607 menuItem = [appMenu addItemWithTitle:@"Hide Others"
608 action:@selector(hideOtherApplications:)
609 keyEquivalent:@"h"];
610 menuItem.keyEquivalentModifierMask = (NSEventModifierFlagOption |
611 NSEventModifierFlagCommand);
612
613 [appMenu addItemWithTitle:@"Show All"
614 action:@selector(unhideAllApplications:)
615 keyEquivalent:@""];
616
617 menuItem = [appMenu addItemWithTitle:@"Quit Blender"
618 action:@selector(terminate:)
619 keyEquivalent:@"q"];
620 menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand;
621
622 menuItem = [[NSMenuItem alloc] init];
623 menuItem.submenu = appMenu;
624
625 [mainMenubar addItem:menuItem];
626 [menuItem release];
627 [appMenu release];
628
629 /* Create the window menu. */
630 windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
631
632 menuItem = [windowMenu addItemWithTitle:@"Minimize"
633 action:@selector(performMiniaturize:)
634 keyEquivalent:@"m"];
635 menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand;
636
637 [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
638
639 menuItem = [windowMenu addItemWithTitle:@"Enter Full Screen"
640 action:@selector(toggleFullScreen:)
641 keyEquivalent:@"f"];
642 menuItem.keyEquivalentModifierMask = NSEventModifierFlagControl |
643 NSEventModifierFlagCommand;
644
645 menuItem = [windowMenu addItemWithTitle:@"Close"
646 action:@selector(performClose:)
647 keyEquivalent:@"w"];
648 menuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand;
649
650 menuItem = [[NSMenuItem alloc] init];
651 menuItem.submenu = windowMenu;
652
653 [mainMenubar addItem:menuItem];
654 [menuItem release];
655
656 [NSApp setMainMenu:mainMenubar];
657 [NSApp setWindowsMenu:windowMenu];
658 [windowMenu release];
659 }
660
661 if ([NSApp delegate] == nil) {
662 CocoaAppDelegate *appDelegate = [[CocoaAppDelegate alloc] initWithSystemCocoa:this];
663 [NSApp setDelegate:appDelegate];
664 }
665
666 /* AppKit provides automatic window tabbing. Blender is a single-tabbed
667 * application without a macOS tab bar, and should explicitly opt-out of this.
668 * This is also controlled by the macOS user default #NSWindowTabbingEnabled. */
669 NSWindow.allowsAutomaticWindowTabbing = NO;
670
671 [NSApp finishLaunching];
672 }
673 }
674 return success;
675}
676
677/* --------------------------------------------------------------------
678 * Window management.
679 */
680
682{
683 /* For comparing to NSEvent timestamp, this particular API function matches. */
684 return (uint64_t)([[NSProcessInfo processInfo] systemUptime] * 1000);
685}
686
688{
689 @autoreleasepool {
690 return [[NSScreen screens] count];
691 }
692}
693
694void GHOST_SystemCocoa::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
695{
696 @autoreleasepool {
697 /* Get visible frame, that is frame excluding dock and top menu bar. */
698 const NSRect frame = [GHOST_WindowCocoa::getPrimaryScreen() visibleFrame];
699
700 /* Returns max window contents (excluding title bar...). */
701 const NSRect contentRect = [NSWindow
702 contentRectForFrameRect:frame
703 styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
704 NSWindowStyleMaskMiniaturizable)];
705
706 width = contentRect.size.width;
707 height = contentRect.size.height;
708 }
709}
710
711void GHOST_SystemCocoa::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
712{
713 /* TODO! */
714 getMainDisplayDimensions(width, height);
715}
716
719 int32_t top,
720 uint32_t width,
721 uint32_t height,
723 GHOST_GPUSettings gpuSettings,
724 const bool /*exclusive*/,
725 const bool is_dialog,
726 const GHOST_IWindow *parentWindow)
727{
728 GHOST_IWindow *window = nullptr;
729 @autoreleasepool {
730 /* Get the available rect for including window contents. */
731 const NSRect primaryScreenFrame = [GHOST_WindowCocoa::getPrimaryScreen() visibleFrame];
732 const NSRect primaryScreenContentRect = [NSWindow
733 contentRectForFrameRect:primaryScreenFrame
734 styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
735 NSWindowStyleMaskMiniaturizable)];
736
737 const int32_t bottom = primaryScreenContentRect.size.height - top - height;
738
739 window = new GHOST_WindowCocoa(this,
740 title,
741 left,
742 bottom,
743 width,
744 height,
745 state,
746 gpuSettings.context_type,
747 gpuSettings.flags & GHOST_gpuStereoVisual,
748 gpuSettings.flags & GHOST_gpuDebugContext,
749 is_dialog,
750 (GHOST_WindowCocoa *)parentWindow,
751 gpuSettings.preferred_device);
752
753 if (window->getValid()) {
754 /* Store the pointer to the window. */
755 GHOST_ASSERT(m_windowManager, "m_windowManager not initialized");
756 m_windowManager->addWindow(window);
757 m_windowManager->setActiveWindow(window);
758 /* Need to tell window manager the new window is the active one
759 * (Cocoa does not send the event activate upon window creation). */
762 }
763 else {
764 GHOST_PRINT("GHOST_SystemCocoa::createWindow(): window invalid\n");
765 delete window;
766 window = nullptr;
767 }
768 }
769 return window;
770}
771
778{
779 const bool debug_context = (gpuSettings.flags & GHOST_gpuDebugContext) != 0;
780
781 switch (gpuSettings.context_type) {
782#ifdef WITH_VULKAN_BACKEND
783 case GHOST_kDrawingContextTypeVulkan: {
784 GHOST_Context *context = new GHOST_ContextVK(
785 false, nullptr, 1, 2, debug_context, gpuSettings.preferred_device);
786 if (context->initializeDrawingContext()) {
787 return context;
788 }
789 delete context;
790 return nullptr;
791 }
792#endif
793
794#ifdef WITH_METAL_BACKEND
795 case GHOST_kDrawingContextTypeMetal: {
796 /* TODO(fclem): Remove OpenGL support and rename context to ContextMTL */
797 GHOST_Context *context = new GHOST_ContextCGL(false, nullptr, nullptr, debug_context);
798 if (context->initializeDrawingContext()) {
799 return context;
800 }
801 delete context;
802 return nullptr;
803 }
804#endif
805
806 default:
807 /* Unsupported backend. */
808 return nullptr;
809 }
810}
811
818{
819 delete context;
820
821 return GHOST_kSuccess;
822}
823
825{
826 const NSPoint scr_co = NSMakePoint(x, y);
827
828 @autoreleasepool {
829 const int windowNumberAtPoint = [NSWindow windowNumberAtPoint:scr_co
830 belowWindowWithWindowNumber:0];
831 NSWindow *nswindow = [NSApp windowWithWindowNumber:windowNumberAtPoint];
832
833 if (nswindow == nil) {
834 return nil;
835 }
836
837 return m_windowManager->getWindowAssociatedWithOSWindow((const void *)nswindow);
838 }
839}
840
845{
846 const NSPoint mouseLoc = [NSEvent mouseLocation];
847
848 /* Returns the mouse location in screen coordinates. */
849 x = int32_t(mouseLoc.x);
850 y = int32_t(mouseLoc.y);
851 return GHOST_kSuccess;
852}
853
858{
859 GHOST_WindowCocoa *window = (GHOST_WindowCocoa *)m_windowManager->getActiveWindow();
860 if (!window) {
861 return GHOST_kFailure;
862 }
863
864 /* Cursor and mouse dissociation placed here not to interfere with continuous grab
865 * (in cont. grab setMouseCursorPosition is directly called). */
866 CGAssociateMouseAndMouseCursorPosition(false);
868 CGAssociateMouseAndMouseCursorPosition(true);
869
870 /* Force mouse move event (not pushed by Cocoa). */
874
875 return GHOST_kSuccess;
876}
877
879{
880 @autoreleasepool {
881 NSColorSampler *sampler = [[NSColorSampler alloc] init];
882 __block BOOL selectCompleted = NO;
883 __block BOOL samplingSucceeded = NO;
884
885 [sampler showSamplerWithSelectionHandler:^(NSColor *selectedColor) {
886 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
887 dispatch_get_main_queue(),
888 ^{
889 if (selectedColor != nil) {
890 NSColor *rgbColor = [selectedColor
891 colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
892 if (rgbColor) {
893 r_color[0] = [rgbColor redComponent];
894 r_color[1] = [rgbColor greenComponent];
895 r_color[2] = [rgbColor blueComponent];
896 }
897 samplingSucceeded = YES;
898 }
899 selectCompleted = YES;
900 });
901 }];
902
903 while (!selectCompleted) {
904 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
905 beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.05]];
906 }
907
908 return samplingSucceeded ? GHOST_kSuccess : GHOST_kFailure;
909 }
910}
911
913{
914 float xf = float(x), yf = float(y);
915 GHOST_WindowCocoa *window = (GHOST_WindowCocoa *)m_windowManager->getActiveWindow();
916 if (!window) {
917 return GHOST_kFailure;
918 }
919
920 @autoreleasepool {
921 NSScreen *windowScreen = window->getScreen();
922 const NSRect screenRect = windowScreen.frame;
923
924 /* Set position relative to current screen. */
925 xf -= screenRect.origin.x;
926 yf -= screenRect.origin.y;
927
928 /* Quartz Display Services uses the old coordinates (top left origin). */
929 yf = screenRect.size.height - yf;
930
931 CGDisplayMoveCursorToPoint((CGDirectDisplayID)[[[windowScreen deviceDescription]
932 objectForKey:@"NSScreenNumber"] unsignedIntValue],
933 CGPointMake(xf, yf));
934
935 /* See https://stackoverflow.com/a/17559012. By default, hardware events
936 * will be suppressed for 500ms after a synthetic mouse event. For unknown
937 * reasons CGEventSourceSetLocalEventsSuppressionInterval does not work,
938 * however calling CGAssociateMouseAndMouseCursorPosition also removes the
939 * delay, even if this is undocumented. */
940 CGAssociateMouseAndMouseCursorPosition(true);
941 }
942 return GHOST_kSuccess;
943}
944
946{
947 keys.set(GHOST_kModifierKeyLeftOS, (m_modifierMask & NSEventModifierFlagCommand) ? true : false);
948 keys.set(GHOST_kModifierKeyLeftAlt, (m_modifierMask & NSEventModifierFlagOption) ? true : false);
950 (m_modifierMask & NSEventModifierFlagShift) ? true : false);
952 (m_modifierMask & NSEventModifierFlagControl) ? true : false);
953
954 return GHOST_kSuccess;
955}
956
958{
959 const UInt32 button_state = GetCurrentEventButtonState();
960
961 buttons.clear();
962 buttons.set(GHOST_kButtonMaskLeft, button_state & (1 << 0));
963 buttons.set(GHOST_kButtonMaskRight, button_state & (1 << 1));
964 buttons.set(GHOST_kButtonMaskMiddle, button_state & (1 << 2));
965 buttons.set(GHOST_kButtonMaskButton4, button_state & (1 << 3));
966 buttons.set(GHOST_kButtonMaskButton5, button_state & (1 << 4));
967 return GHOST_kSuccess;
968}
969
971{
974 ~(
975 /* Cocoa has no support for a primary selection clipboard. */
977 /* Cocoa doesn't define a Hyper modifier key,
978 * it's possible another modifier could be optionally used in it's place. */
980}
981
982/* --------------------------------------------------------------------
983 * Event handlers.
984 */
985
989bool GHOST_SystemCocoa::processEvents(bool /*waitForEvent*/)
990{
991 bool anyProcessed = false;
992 NSEvent *event;
993
994 /* TODO: implement timer? */
995#if 0
996 do {
998
999 if (waitForEvent) {
1000 uint64_t next = timerMgr->nextFireTime();
1001 double timeOut;
1002
1003 if (next == GHOST_kFireTimeNever) {
1004 timeOut = kEventDurationForever;
1005 }
1006 else {
1007 timeOut = (double)(next - getMilliSeconds())/1000.0;
1008 if (timeOut < 0.0)
1009 timeOut = 0.0;
1010 }
1011
1012 ::ReceiveNextEvent(0, nullptr, timeOut, false, &event);
1013 }
1014
1015 if (timerMgr->fireTimers(getMilliSeconds())) {
1016 anyProcessed = true;
1017 }
1018#endif
1019 do {
1020 @autoreleasepool {
1021 event = [NSApp nextEventMatchingMask:NSEventMaskAny
1022 untilDate:[NSDate distantPast]
1023 inMode:NSDefaultRunLoopMode
1024 dequeue:YES];
1025 if (event == nil) {
1026 break;
1027 }
1028
1029 anyProcessed = true;
1030
1031 /* Send event to NSApp to ensure Mac wide events are handled,
1032 * this will send events to BlenderWindow which will call back
1033 * to handleKeyEvent, handleMouseEvent and handleTabletEvent. */
1034
1035 /* There is on special exception for Control+(Shift)+Tab.
1036 * We do not get keyDown events delivered to the view because they are
1037 * special hotkeys to switch between views, so override directly */
1038
1039 if (event.type == NSEventTypeKeyDown && event.keyCode == kVK_Tab &&
1040 (event.modifierFlags & NSEventModifierFlagControl))
1041 {
1042 handleKeyEvent(event);
1043 }
1044 else {
1045 /* For some reason NSApp is swallowing the key up events when modifier
1046 * key is pressed, even if there seems to be no apparent reason to do
1047 * so, as a workaround we always handle these up events. */
1048 if (event.type == NSEventTypeKeyUp &&
1049 (event.modifierFlags & (NSEventModifierFlagCommand | NSEventModifierFlagOption)))
1050 {
1051 handleKeyEvent(event);
1052 }
1053
1054 [NSApp sendEvent:event];
1055 }
1056 }
1057 } while (event != nil);
1058#if 0
1059 } while (waitForEvent && !anyProcessed); /* Needed only for timer implementation. */
1060#endif
1061
1064 }
1065
1068 return true;
1069 }
1070
1072
1073 return anyProcessed;
1074}
1075
1076/* NOTE: called from #NSApplication delegate. */
1078{
1079 @autoreleasepool {
1080 for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) {
1081 GHOST_WindowCocoa *window = (GHOST_WindowCocoa *)iwindow;
1082 if (window->isDialog()) {
1083 [window->getViewWindow() makeKeyAndOrderFront:nil];
1084 }
1085 }
1086
1087 /* Update the modifiers key mask, as its status may have changed when the application
1088 * was not active (that is when update events are sent to another application). */
1089 GHOST_IWindow *window = m_windowManager->getActiveWindow();
1090
1091 if (!window) {
1093 return GHOST_kFailure;
1094 }
1095
1097
1098 const unsigned int modifiers = [[[NSApplication sharedApplication] currentEvent]
1099 modifierFlags];
1100
1101 if ((modifiers & NSEventModifierFlagShift) != (m_modifierMask & NSEventModifierFlagShift)) {
1103 (modifiers & NSEventModifierFlagShift) ? GHOST_kEventKeyDown :
1105 window,
1107 false));
1108 }
1109 if ((modifiers & NSEventModifierFlagControl) != (m_modifierMask & NSEventModifierFlagControl))
1110 {
1112 (modifiers & NSEventModifierFlagControl) ? GHOST_kEventKeyDown :
1114 window,
1116 false));
1117 }
1118 if ((modifiers & NSEventModifierFlagOption) != (m_modifierMask & NSEventModifierFlagOption)) {
1120 (modifiers & NSEventModifierFlagOption) ? GHOST_kEventKeyDown :
1122 window,
1124 false));
1125 }
1126 if ((modifiers & NSEventModifierFlagCommand) != (m_modifierMask & NSEventModifierFlagCommand))
1127 {
1129 (modifiers & NSEventModifierFlagCommand) ? GHOST_kEventKeyDown :
1131 window,
1133 false));
1134 }
1135
1136 m_modifierMask = modifiers;
1137
1139 }
1140 return GHOST_kSuccess;
1141}
1142
1144{
1145 for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) {
1146 GHOST_WindowCocoa *window = (GHOST_WindowCocoa *)iwindow;
1147 if (window->isDialog()) {
1148 return true;
1149 }
1150 }
1151 return false;
1152}
1153
1158
1159/* NOTE: called from #NSWindow delegate. */
1161 GHOST_WindowCocoa *window)
1162{
1163 if (!validWindow(window)) {
1164 return GHOST_kFailure;
1165 }
1166 switch (eventType) {
1169 break;
1171 m_windowManager->setActiveWindow(window);
1172 window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
1174 break;
1176 m_windowManager->setWindowInactive(window);
1178 break;
1180 if (m_nativePixel) {
1181 window->setNativePixelSize();
1183 }
1185 break;
1188 break;
1191 /* Enforce only one resize message per event loop
1192 * (coalescing all the live resize messages). */
1193 window->updateDrawingContext();
1195 /* Mouse up event is trapped by the resizing event loop,
1196 * so send it anyway to the window manager. */
1199 window,
1202 // m_ignoreWindowSizedMessages = true;
1203 }
1204 break;
1206
1207 if (m_nativePixel) {
1208 window->setNativePixelSize();
1210 }
1211
1212 default:
1213 return GHOST_kFailure;
1214 break;
1215 }
1216
1218 return GHOST_kSuccess;
1219}
1220
1226static NSSize getNSImagePixelSize(NSImage *image)
1227{
1228 /* Assuming the NSImage instance only contains one single image. */
1229 @autoreleasepool {
1230 NSImageRep *imageRepresentation = [[image representations] firstObject];
1231 return NSMakeSize(imageRepresentation.pixelsWide, imageRepresentation.pixelsHigh);
1232 }
1233}
1234
1240static ImBuf *NSImageToImBuf(NSImage *image)
1241{
1242 const NSSize imageSize = getNSImagePixelSize(image);
1243 ImBuf *ibuf = IMB_allocImBuf(imageSize.width, imageSize.height, 32, IB_byte_data);
1244
1245 if (!ibuf) {
1246 return nullptr;
1247 }
1248
1249 @autoreleasepool {
1250 NSBitmapImageRep *bitmapImage = nil;
1251 for (NSImageRep *representation in [image representations]) {
1252 if ([representation isKindOfClass:[NSBitmapImageRep class]]) {
1253 bitmapImage = (NSBitmapImageRep *)representation;
1254 break;
1255 }
1256 }
1257
1258 if (bitmapImage == nil || bitmapImage.bitsPerPixel != 32 || bitmapImage.isPlanar ||
1259 bitmapImage.bitmapFormat & (NSBitmapFormatAlphaFirst | NSBitmapFormatFloatingPointSamples))
1260 {
1261 return nullptr;
1262 }
1263
1264 uint8_t *ibuf_data = ibuf->byte_buffer.data;
1265 uint8_t *bmp_data = (uint8_t *)bitmapImage.bitmapData;
1266
1267 /* Vertical Flip. */
1268 for (int y = 0; y < imageSize.height; y++) {
1269 const int row_byte_count = 4 * imageSize.width;
1270 const int ibuf_off = (imageSize.height - y - 1) * row_byte_count;
1271 const int bmp_off = y * row_byte_count;
1272 memcpy(ibuf_data + ibuf_off, bmp_data + bmp_off, row_byte_count);
1273 }
1274 }
1275
1276 return ibuf;
1277}
1278
1279/* NOTE: called from #NSWindow subclass. */
1281 GHOST_TDragnDropTypes draggedObjectType,
1282 GHOST_WindowCocoa *window,
1283 int mouseX,
1284 int mouseY,
1285 void *data)
1286{
1287 if (!validWindow(window)) {
1288 return GHOST_kFailure;
1289 }
1290 switch (eventType) {
1294 window->clientToScreenIntern(mouseX, mouseY, mouseX, mouseY);
1296 getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, nullptr));
1297 break;
1298
1300 if (!data) {
1301 return GHOST_kFailure;
1302 }
1303
1304 GHOST_TDragnDropDataPtr eventData;
1305 @autoreleasepool {
1306 switch (draggedObjectType) {
1308 NSArray *droppedArray = (NSArray *)data;
1309
1310 GHOST_TStringArray *strArray = (GHOST_TStringArray *)malloc(
1311 sizeof(GHOST_TStringArray));
1312 if (!strArray) {
1313 return GHOST_kFailure;
1314 }
1315
1316 strArray->count = droppedArray.count;
1317 if (strArray->count == 0) {
1318 free(strArray);
1319 return GHOST_kFailure;
1320 }
1321
1322 strArray->strings = (uint8_t **)malloc(strArray->count * sizeof(uint8_t *));
1323
1324 for (int i = 0; i < strArray->count; i++) {
1325 NSString *droppedStr = [droppedArray objectAtIndex:i];
1326 const size_t pastedTextSize = [droppedStr
1327 lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1328 uint8_t *temp_buff = (uint8_t *)malloc(pastedTextSize + 1);
1329
1330 if (!temp_buff) {
1331 strArray->count = i;
1332 break;
1333 }
1334
1335 memcpy(temp_buff,
1336 [droppedStr cStringUsingEncoding:NSUTF8StringEncoding],
1337 pastedTextSize);
1338 temp_buff[pastedTextSize] = '\0';
1339
1340 strArray->strings[i] = temp_buff;
1341 }
1342
1343 eventData = static_cast<GHOST_TDragnDropDataPtr>(strArray);
1344 break;
1345 }
1347 NSString *droppedStr = (NSString *)data;
1348 const size_t pastedTextSize = [droppedStr
1349 lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1350 uint8_t *temp_buff = (uint8_t *)malloc(pastedTextSize + 1);
1351
1352 if (temp_buff == nullptr) {
1353 return GHOST_kFailure;
1354 }
1355
1356 memcpy(
1357 temp_buff, [droppedStr cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1358 temp_buff[pastedTextSize] = '\0';
1359
1360 eventData = static_cast<GHOST_TDragnDropDataPtr>(temp_buff);
1361 break;
1362 }
1364 NSImage *droppedImg = static_cast<NSImage *>(data);
1365 ImBuf *ibuf = NSImageToImBuf(droppedImg);
1366
1367 eventData = static_cast<GHOST_TDragnDropDataPtr>(ibuf);
1368
1369 [droppedImg release];
1370 break;
1371 }
1372 default:
1373 return GHOST_kFailure;
1374 break;
1375 }
1376 }
1377
1378 window->clientToScreenIntern(mouseX, mouseY, mouseX, mouseY);
1380 getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, eventData));
1381
1382 break;
1383 }
1384 default:
1385 return GHOST_kFailure;
1386 }
1388 return GHOST_kSuccess;
1389}
1390
1392{
1393 GHOST_Window *window = (GHOST_Window *)m_windowManager->getActiveWindow();
1394
1395 /* Discard quit event if we are in cursor grab sequence. */
1396 if (window && window->getCursorGrabModeIsWarp()) {
1397 return;
1398 }
1399
1400 /* Push the event to Blender so it can open a dialog if needed. */
1403}
1404
1406{
1407 NSString *filepath = (NSString *)filepathStr;
1408
1409 /* Check for blender opened windows and make the front-most key.
1410 * In case blender is minimized, opened on another desktop space,
1411 * or in full-screen mode. */
1412 @autoreleasepool {
1413 NSArray *windowsList = [NSApp orderedWindows];
1414 if ([windowsList count]) {
1415 [[windowsList objectAtIndex:0] makeKeyAndOrderFront:nil];
1416 }
1417
1418 GHOST_Window *window = m_windowManager->getWindows().empty() ?
1419 nullptr :
1420 (GHOST_Window *)m_windowManager->getWindows().front();
1421
1422 if (!window) {
1423 return NO;
1424 }
1425
1426 /* Discard event if we are in cursor grab sequence,
1427 * it'll lead to "stuck cursor" situation if the alert panel is raised. */
1428 if (window && window->getCursorGrabModeIsWarp()) {
1429 return NO;
1430 }
1431
1432 const size_t filenameTextSize = [filepath lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1433 char *temp_buff = (char *)malloc(filenameTextSize + 1);
1434
1435 if (temp_buff == nullptr) {
1436 return GHOST_kFailure;
1437 }
1438
1439 memcpy(temp_buff, [filepath cStringUsingEncoding:NSUTF8StringEncoding], filenameTextSize);
1440 temp_buff[filenameTextSize] = '\0';
1441
1444 window,
1445 static_cast<GHOST_TEventDataPtr>(temp_buff)));
1446 }
1447 return YES;
1448}
1449
1451{
1452 NSEvent *event = (NSEvent *)eventPtr;
1453
1454 GHOST_IWindow *window = m_windowManager->getWindowAssociatedWithOSWindow(
1455 (const void *)event.window);
1456 if (!window) {
1457 // printf("\nW failure for event 0x%x",event.type);
1458 return GHOST_kFailure;
1459 }
1460
1461 GHOST_TabletData &ct = ((GHOST_WindowCocoa *)window)->GetCocoaTabletData();
1462
1463 switch (eventType) {
1464 case NSEventTypeTabletPoint:
1465 /* workaround 2 corner-cases:
1466 * 1. if event.isEnteringProximity was not triggered since program-start.
1467 * 2. device is not sending event.pointingDeviceType, due no eraser. */
1468 if (ct.Active == GHOST_kTabletModeNone) {
1470 }
1471
1472 ct.Pressure = event.pressure;
1473 /* Range: -1 (left) to 1 (right). */
1474 ct.Xtilt = event.tilt.x;
1475 /* On macOS, the y tilt behavior is inverted from what we expect: negative
1476 * meaning a tilt toward the user, positive meaning away from the user.
1477 * Convert to what Blender expects: -1.0 (away from user) to +1.0 (toward user). */
1478 ct.Ytilt = -event.tilt.y;
1479 break;
1480
1481 case NSEventTypeTabletProximity:
1482 /* Reset tablet data when device enters proximity or leaves. */
1484 if (event.isEnteringProximity) {
1485 /* Pointer is entering tablet area proximity. */
1486 switch (event.pointingDeviceType) {
1487 case NSPointingDeviceTypePen:
1489 break;
1490 case NSPointingDeviceTypeEraser:
1492 break;
1493 case NSPointingDeviceTypeCursor:
1494 case NSPointingDeviceTypeUnknown:
1495 default:
1496 break;
1497 }
1498 }
1499 break;
1500
1501 default:
1502 GHOST_ASSERT(FALSE, "GHOST_SystemCocoa::handleTabletEvent : unknown event received");
1503 return GHOST_kFailure;
1504 break;
1505 }
1506 return GHOST_kSuccess;
1507}
1508
1510{
1511 NSEvent *event = (NSEvent *)eventPtr;
1512
1513 switch (event.subtype) {
1514 case NSEventSubtypeTabletPoint:
1515 handleTabletEvent(eventPtr, NSEventTypeTabletPoint);
1516 return true;
1517 case NSEventSubtypeTabletProximity:
1518 handleTabletEvent(eventPtr, NSEventTypeTabletProximity);
1519 return true;
1520 default:
1521 /* No tablet event included: do nothing. */
1522 return false;
1523 }
1524}
1525
1527{
1528 NSEvent *event = (NSEvent *)eventPtr;
1529
1530 /* event.window returns other windows if mouse-over, that's OSX input standard
1531 * however, if mouse exits window(s), the windows become inactive, until you click.
1532 * We then fall back to the active window from ghost. */
1534 ->getWindowAssociatedWithOSWindow((const void *)event.window);
1535 if (!window) {
1536 window = (GHOST_WindowCocoa *)m_windowManager->getActiveWindow();
1537 if (!window) {
1538 // printf("\nW failure for event 0x%x", event.type);
1539 return GHOST_kFailure;
1540 }
1541 }
1542
1543 switch (event.type) {
1544 case NSEventTypeLeftMouseDown:
1545 handleTabletEvent(event); /* Update window tablet state to be included in event. */
1546 pushEvent(new GHOST_EventButton(event.timestamp * 1000,
1548 window,
1550 window->GetCocoaTabletData()));
1551 break;
1552 case NSEventTypeRightMouseDown:
1553 handleTabletEvent(event); /* Update window tablet state to be included in event. */
1554 pushEvent(new GHOST_EventButton(event.timestamp * 1000,
1556 window,
1558 window->GetCocoaTabletData()));
1559 break;
1560 case NSEventTypeOtherMouseDown:
1561 handleTabletEvent(event); /* Handle tablet events combined with mouse events. */
1562 pushEvent(new GHOST_EventButton(event.timestamp * 1000,
1564 window,
1565 convertButton(event.buttonNumber),
1566 window->GetCocoaTabletData()));
1567 break;
1568 case NSEventTypeLeftMouseUp:
1569 handleTabletEvent(event); /* Update window tablet state to be included in event. */
1570 pushEvent(new GHOST_EventButton(event.timestamp * 1000,
1572 window,
1574 window->GetCocoaTabletData()));
1575 break;
1576 case NSEventTypeRightMouseUp:
1577 handleTabletEvent(event); /* Update window tablet state to be included in event. */
1578 pushEvent(new GHOST_EventButton(event.timestamp * 1000,
1580 window,
1582 window->GetCocoaTabletData()));
1583 break;
1584 case NSEventTypeOtherMouseUp:
1585 handleTabletEvent(event); /* Update window tablet state to be included in event. */
1586 pushEvent(new GHOST_EventButton(event.timestamp * 1000,
1588 window,
1589 convertButton(event.buttonNumber),
1590 window->GetCocoaTabletData()));
1591 break;
1592 case NSEventTypeLeftMouseDragged:
1593 case NSEventTypeRightMouseDragged:
1594 case NSEventTypeOtherMouseDragged:
1595 handleTabletEvent(event); /* Update window tablet state to be included in event. */
1596
1597 case NSEventTypeMouseMoved: {
1598 GHOST_TGrabCursorMode grab_mode = window->getCursorGrabMode();
1599
1600 /* TODO: CHECK IF THIS IS A TABLET EVENT */
1601 bool is_tablet = false;
1602
1603 if (is_tablet && window->getCursorGrabModeIsWarp()) {
1604 grab_mode = GHOST_kGrabDisable;
1605 }
1606
1607 switch (grab_mode) {
1608 case GHOST_kGrabHide: {
1609 /* Cursor hidden grab operation : no cursor move */
1610 int32_t x_warp, y_warp, x_accum, y_accum, x, y;
1611
1612 window->getCursorGrabInitPos(x_warp, y_warp);
1613 window->screenToClientIntern(x_warp, y_warp, x_warp, y_warp);
1614
1615 /* Strange Apple implementation (inverted coordinates for the deltaY)... */
1616 window->getCursorGrabAccum(x_accum, y_accum);
1617 x_accum += event.deltaX;
1618 y_accum += -event.deltaY;
1619 window->setCursorGrabAccum(x_accum, y_accum);
1620
1621 window->clientToScreenIntern(x_warp + x_accum, y_warp + y_accum, x, y);
1622 pushEvent(new GHOST_EventCursor(event.timestamp * 1000,
1624 window,
1625 x,
1626 y,
1627 window->GetCocoaTabletData()));
1628 break;
1629 }
1630 case GHOST_kGrabWrap: {
1631 /* Wrap cursor at area/window boundaries. */
1632 const NSTimeInterval timestamp = event.timestamp;
1633 if (timestamp < m_last_warp_timestamp) {
1634 /* After warping we can still receive older unwrapped mouse events,
1635 * ignore those. */
1636 break;
1637 }
1638
1639 GHOST_Rect bounds, windowBounds, correctedBounds;
1640
1641 /* fall back to window bounds */
1642 if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
1643 window->getClientBounds(bounds);
1644 }
1645
1646 /* Switch back to Cocoa coordinates orientation
1647 * (y=0 at bottom, the same as blender internal BTW!), and to client coordinates. */
1648 window->getClientBounds(windowBounds);
1649 window->screenToClient(bounds.m_l, bounds.m_b, correctedBounds.m_l, correctedBounds.m_t);
1650 window->screenToClient(bounds.m_r, bounds.m_t, correctedBounds.m_r, correctedBounds.m_b);
1651 correctedBounds.m_b = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_b;
1652 correctedBounds.m_t = (windowBounds.m_b - windowBounds.m_t) - correctedBounds.m_t;
1653
1654 /* Get accumulation from previous mouse warps. */
1655 int32_t x_accum, y_accum;
1656 window->getCursorGrabAccum(x_accum, y_accum);
1657
1658 const NSPoint mousePos = event.locationInWindow;
1659 /* Casting. */
1660 const int32_t x_mouse = mousePos.x;
1661 const int32_t y_mouse = mousePos.y;
1662
1663 /* Warp mouse cursor if needed. */
1664 int32_t warped_x_mouse = x_mouse;
1665 int32_t warped_y_mouse = y_mouse;
1666 correctedBounds.wrapPoint(
1667 warped_x_mouse, warped_y_mouse, 4, window->getCursorGrabAxis());
1668
1669 /* Set new cursor position. */
1670 if (x_mouse != warped_x_mouse || y_mouse != warped_y_mouse) {
1671 int32_t warped_x, warped_y;
1672 window->clientToScreenIntern(warped_x_mouse, warped_y_mouse, warped_x, warped_y);
1673 setMouseCursorPosition(warped_x, warped_y); /* wrap */
1674 window->setCursorGrabAccum(x_accum + (x_mouse - warped_x_mouse),
1675 y_accum + (y_mouse - warped_y_mouse));
1676
1677 /* This is the current time that matches NSEvent timestamp. */
1678 m_last_warp_timestamp = [[NSProcessInfo processInfo] systemUptime];
1679 }
1680
1681 /* Generate event. */
1682 int32_t x, y;
1683 window->clientToScreenIntern(x_mouse + x_accum, y_mouse + y_accum, x, y);
1684 pushEvent(new GHOST_EventCursor(event.timestamp * 1000,
1686 window,
1687 x,
1688 y,
1689 window->GetCocoaTabletData()));
1690 break;
1691 }
1692 default: {
1693 /* Normal cursor operation: send mouse position in window. */
1694 const NSPoint mousePos = event.locationInWindow;
1695 int32_t x, y;
1696
1697 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1698 pushEvent(new GHOST_EventCursor(event.timestamp * 1000,
1700 window,
1701 x,
1702 y,
1703 window->GetCocoaTabletData()));
1704 break;
1705 }
1706 }
1707 break;
1708 }
1709 case NSEventTypeScrollWheel: {
1710 const NSEventPhase momentumPhase = event.momentumPhase;
1711 const NSEventPhase phase = event.phase;
1712
1713 /* when pressing a key while momentum scrolling continues after
1714 * lifting fingers off the trackpad, the action can unexpectedly
1715 * change from e.g. scrolling to zooming. this works around the
1716 * issue by ignoring momentum scroll after a key press */
1717 if (momentumPhase) {
1719 break;
1720 }
1721 }
1722 else {
1723 m_ignoreMomentumScroll = false;
1724 }
1725
1726 /* we assume phases are only set for gestures from trackpad or magic
1727 * mouse events. note that using tablet at the same time may not work
1728 * since this is a static variable */
1729 if (phase == NSEventPhaseBegan && m_multitouchGestures) {
1730 m_multiTouchScroll = true;
1731 }
1732 else if (phase == NSEventPhaseEnded) {
1733 m_multiTouchScroll = false;
1734 }
1735
1736 /* Standard scroll-wheel case, if no swiping happened,
1737 * and no momentum (kinetic scroll) works. */
1738 if (!m_multiTouchScroll && momentumPhase == NSEventPhaseNone) {
1739 if (event.deltaX != 0.0) {
1740 const int32_t delta = event.deltaX > 0.0 ? 1 : -1;
1742 event.timestamp * 1000, window, GHOST_kEventWheelAxisHorizontal, delta));
1743 }
1744 if (event.deltaY != 0.0) {
1745 const int32_t delta = event.deltaY > 0.0 ? 1 : -1;
1747 event.timestamp * 1000, window, GHOST_kEventWheelAxisVertical, delta));
1748 }
1749 }
1750 else {
1751 const NSPoint mousePos = event.locationInWindow;
1752
1753 /* with 10.7 nice scrolling deltas are supported */
1754 double dx = event.scrollingDeltaX;
1755 double dy = event.scrollingDeltaY;
1756
1757 /* However, WACOM tablet (intuos5) needs old deltas,
1758 * it then has momentum and phase at zero. */
1759 if (phase == NSEventPhaseNone && momentumPhase == NSEventPhaseNone) {
1760 dx = event.deltaX;
1761 dy = event.deltaY;
1762 }
1763
1764 int32_t x, y;
1765 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1766
1767 BlenderWindow *view_window = (BlenderWindow *)window->getOSWindow();
1768
1769 @autoreleasepool {
1770 const NSPoint delta = [[view_window contentView]
1771 convertPointToBacking:NSMakePoint(dx, dy)];
1772 pushEvent(new GHOST_EventTrackpad(event.timestamp * 1000,
1773 window,
1775 x,
1776 y,
1777 delta.x,
1778 delta.y,
1779 event.isDirectionInvertedFromDevice));
1780 }
1781 }
1782 break;
1783 }
1784 case NSEventTypeMagnify: {
1785 const NSPoint mousePos = event.locationInWindow;
1786 int32_t x, y;
1787 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1788 pushEvent(new GHOST_EventTrackpad(event.timestamp * 1000,
1789 window,
1791 x,
1792 y,
1793 event.magnification * 125.0 + 0.1,
1794 0,
1795 false));
1796 break;
1797 }
1798 case NSEventTypeSmartMagnify: {
1799 const NSPoint mousePos = event.locationInWindow;
1800 int32_t x, y;
1801 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1803 event.timestamp * 1000, window, GHOST_kTrackpadEventSmartMagnify, x, y, 0, 0, false));
1804 break;
1805 }
1806 case NSEventTypeRotate: {
1807 const NSPoint mousePos = event.locationInWindow;
1808 int32_t x, y;
1809 window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
1810 pushEvent(new GHOST_EventTrackpad(event.timestamp * 1000,
1811 window,
1813 x,
1814 y,
1815 event.rotation * -5.0,
1816 0,
1817 false));
1818 }
1819 default:
1820 return GHOST_kFailure;
1821 break;
1822 }
1823 return GHOST_kSuccess;
1824}
1825
1827{
1828 NSEvent *event = (NSEvent *)eventPtr;
1829 GHOST_IWindow *window = m_windowManager->getWindowAssociatedWithOSWindow(
1830 (const void *)event.window);
1831
1832 if (!window) {
1833 // printf("\nW failure for event 0x%x",event.type);
1834 return GHOST_kFailure;
1835 }
1836
1837 switch (event.type) {
1838 case NSEventTypeKeyDown:
1839 case NSEventTypeKeyUp: {
1840 /* Returns an empty string for dead keys. */
1841 GHOST_TKey keyCode;
1842 char utf8_buf[6] = {'\0'};
1843
1844 @autoreleasepool {
1845 NSString *charsIgnoringModifiers = event.charactersIgnoringModifiers;
1846 if (charsIgnoringModifiers.length > 0) {
1847 keyCode = convertKey(event.keyCode, [charsIgnoringModifiers characterAtIndex:0]);
1848 }
1849 else {
1850 keyCode = convertKey(event.keyCode, 0);
1851 }
1852
1853 NSString *characters = event.characters;
1854 if ([characters length] > 0) {
1855 NSData *convertedCharacters = [characters dataUsingEncoding:NSUTF8StringEncoding];
1856
1857 for (int x = 0; x < convertedCharacters.length; x++) {
1858 utf8_buf[x] = ((char *)convertedCharacters.bytes)[x];
1859 }
1860 }
1861 }
1862
1863 /* Arrow keys should not have UTF8. */
1864 if ((keyCode >= GHOST_kKeyLeftArrow) && (keyCode <= GHOST_kKeyDownArrow)) {
1865 utf8_buf[0] = '\0';
1866 }
1867
1868 /* F-keys should not have UTF8. */
1869 if ((keyCode >= GHOST_kKeyF1) && (keyCode <= GHOST_kKeyF20)) {
1870 utf8_buf[0] = '\0';
1871 }
1872
1873 /* no text with command key pressed */
1874 if (m_modifierMask & NSEventModifierFlagCommand) {
1875 utf8_buf[0] = '\0';
1876 }
1877
1878 if ((keyCode == GHOST_kKeyQ) && (m_modifierMask & NSEventModifierFlagCommand)) {
1879 break; /* Command-Q is directly handled by Cocoa. */
1880 }
1881
1882 if (event.type == NSEventTypeKeyDown) {
1883 pushEvent(new GHOST_EventKey(event.timestamp * 1000,
1885 window,
1886 keyCode,
1887 event.isARepeat,
1888 utf8_buf));
1889#if 0
1890 printf("Key down rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n",
1891 event.keyCode,
1892 charsIgnoringModifiers.length > 0 ? [charsIgnoringModifiers characterAtIndex:0] :
1893 ' ',
1894 keyCode,
1895 utf8_buf);
1896#endif
1897 }
1898 else {
1900 event.timestamp * 1000, GHOST_kEventKeyUp, window, keyCode, false, nullptr));
1901#if 0
1902 printf("Key up rawCode=0x%x charsIgnoringModifiers=%c keyCode=%u utf8=%s\n",
1903 event.keyCode,
1904 charsIgnoringModifiers.length > 0 ? [charsIgnoringModifiers characterAtIndex:0] :
1905 ' ',
1906 keyCode,
1907 utf8_buf);
1908#endif
1909 }
1911 break;
1912 }
1913 case NSEventTypeFlagsChanged: {
1914 const unsigned int modifiers = event.modifierFlags;
1915
1916 if ((modifiers & NSEventModifierFlagShift) != (m_modifierMask & NSEventModifierFlagShift)) {
1917 pushEvent(new GHOST_EventKey(event.timestamp * 1000,
1918 (modifiers & NSEventModifierFlagShift) ? GHOST_kEventKeyDown :
1920 window,
1922 false));
1923 }
1924 if ((modifiers & NSEventModifierFlagControl) !=
1925 (m_modifierMask & NSEventModifierFlagControl))
1926 {
1928 event.timestamp * 1000,
1929 (modifiers & NSEventModifierFlagControl) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
1930 window,
1932 false));
1933 }
1934 if ((modifiers & NSEventModifierFlagOption) != (m_modifierMask & NSEventModifierFlagOption))
1935 {
1937 event.timestamp * 1000,
1938 (modifiers & NSEventModifierFlagOption) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
1939 window,
1941 false));
1942 }
1943 if ((modifiers & NSEventModifierFlagCommand) !=
1944 (m_modifierMask & NSEventModifierFlagCommand))
1945 {
1947 event.timestamp * 1000,
1948 (modifiers & NSEventModifierFlagCommand) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp,
1949 window,
1951 false));
1952 }
1953
1954 m_modifierMask = modifiers;
1956 break;
1957 }
1958
1959 default:
1960 return GHOST_kFailure;
1961 break;
1962 }
1963 return GHOST_kSuccess;
1964}
1965
1966/* --------------------------------------------------------------------
1967 * Clipboard get/set.
1968 */
1969
1970char *GHOST_SystemCocoa::getClipboard(bool /*selection*/) const
1971{
1972 @autoreleasepool {
1973 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
1974 NSString *textPasted = [pasteBoard stringForType:NSPasteboardTypeString];
1975
1976 if (textPasted == nil) {
1977 return nullptr;
1978 }
1979
1980 const size_t pastedTextSize = [textPasted lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1981
1982 char *temp_buff = (char *)malloc(pastedTextSize + 1);
1983
1984 if (temp_buff == nullptr) {
1985 return nullptr;
1986 }
1987
1988 memcpy(temp_buff, [textPasted cStringUsingEncoding:NSUTF8StringEncoding], pastedTextSize);
1989 temp_buff[pastedTextSize] = '\0';
1990
1991 if (temp_buff) {
1992 return temp_buff;
1993 }
1994 }
1995 return nullptr;
1996}
1997
1998void GHOST_SystemCocoa::putClipboard(const char *buffer, bool selection) const
1999{
2000 if (selection) {
2001 return; /* For copying the selection, used on X11. */
2002 }
2003
2004 @autoreleasepool {
2005 NSPasteboard *pasteBoard = NSPasteboard.generalPasteboard;
2006 [pasteBoard declareTypes:@[ NSPasteboardTypeString ] owner:nil];
2007
2008 NSString *textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
2009 [pasteBoard setString:textToCopy forType:NSPasteboardTypeString];
2010 }
2011}
2012
2014{
2015 NSURL *pasteboardImageFile = nil;
2016
2017 @autoreleasepool {
2018 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
2019 NSDictionary *pasteboardFilteringOptions = @{
2020 NSPasteboardURLReadingFileURLsOnlyKey : @YES,
2021 NSPasteboardURLReadingContentsConformToTypesKey : [NSImage imageTypes]
2022 };
2023
2024 NSArray *pasteboardMatches = [pasteboard readObjectsForClasses:@[ [NSURL class] ]
2025 options:pasteboardFilteringOptions];
2026
2027 if (!pasteboardMatches || !pasteboardMatches.count) {
2028 return nil;
2029 }
2030
2031 pasteboardImageFile = [[pasteboardMatches firstObject] copy];
2032 }
2033
2034 return [pasteboardImageFile autorelease];
2035}
2036
2038{
2039 @autoreleasepool {
2040 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
2041 NSArray *supportedTypes = [NSArray
2042 arrayWithObjects:NSPasteboardTypeFileURL, NSPasteboardTypeTIFF, NSPasteboardTypePNG, nil];
2043
2044 NSPasteboardType availableType = [pasteboard availableTypeFromArray:supportedTypes];
2045
2046 if (!availableType) {
2047 return GHOST_kFailure;
2048 }
2049
2050 /* If we got a file, ensure it's an image file. */
2051 if ([pasteboard availableTypeFromArray:@[ NSPasteboardTypeFileURL ]] &&
2052 NSPasteboardGetImageFile() == nil)
2053 {
2054 return GHOST_kFailure;
2055 }
2056 }
2057
2058 return GHOST_kSuccess;
2059}
2060
2061uint *GHOST_SystemCocoa::getClipboardImage(int *r_width, int *r_height) const
2062{
2063 if (!hasClipboardImage()) {
2064 return nullptr;
2065 }
2066
2067 @autoreleasepool {
2068 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
2069
2070 NSImage *clipboardImage = nil;
2071 if (NSURL *pasteboardImageFile = NSPasteboardGetImageFile(); pasteboardImageFile != nil) {
2072 /* Image file. */
2073 clipboardImage = [[[NSImage alloc] initWithContentsOfURL:pasteboardImageFile] autorelease];
2074 }
2075 else {
2076 /* Raw image data. */
2077 clipboardImage = [[[NSImage alloc] initWithPasteboard:pasteboard] autorelease];
2078 }
2079
2080 if (!clipboardImage) {
2081 return nullptr;
2082 }
2083
2084 ImBuf *ibuf = NSImageToImBuf(clipboardImage);
2085 const NSSize clipboardImageSize = getNSImagePixelSize(clipboardImage);
2086
2087 if (ibuf) {
2088 const size_t byteCount = clipboardImageSize.width * clipboardImageSize.height * 4;
2089 uint *rgba = (uint *)malloc(byteCount);
2090
2091 if (!rgba) {
2092 IMB_freeImBuf(ibuf);
2093 return nullptr;
2094 }
2095
2096 memcpy(rgba, ibuf->byte_buffer.data, byteCount);
2097 IMB_freeImBuf(ibuf);
2098
2099 *r_width = clipboardImageSize.width;
2100 *r_height = clipboardImageSize.height;
2101
2102 return rgba;
2103 }
2104 }
2105
2106 return nullptr;
2107}
2108
2110{
2111 @autoreleasepool {
2112 const size_t rowByteCount = width * 4;
2113
2114 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc]
2115 initWithBitmapDataPlanes:nil
2116 pixelsWide:width
2117 pixelsHigh:height
2118 bitsPerSample:8
2119 samplesPerPixel:4
2120 hasAlpha:YES
2121 isPlanar:NO
2122 colorSpaceName:NSDeviceRGBColorSpace
2123 bytesPerRow:rowByteCount
2124 bitsPerPixel:32];
2125
2126 /* Copy the source image data to imageRep, flipping it vertically. */
2127 uint8_t *srcBuffer = reinterpret_cast<uint8_t *>(rgba);
2128 uint8_t *dstBuffer = static_cast<uint8_t *>([imageRep bitmapData]);
2129
2130 for (int y = 0; y < height; y++) {
2131 const int dstOff = (height - y - 1) * rowByteCount;
2132 const int srcOff = y * rowByteCount;
2133 memcpy(dstBuffer + dstOff, srcBuffer + srcOff, rowByteCount);
2134 }
2135
2136 NSImage *image = [[[NSImage alloc] initWithSize:NSMakeSize(width, height)] autorelease];
2137 [image addRepresentation:imageRep];
2138
2139 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
2140 [pasteboard clearContents];
2141
2142 BOOL pasteSuccess = [pasteboard writeObjects:@[ image ]];
2143
2144 if (!pasteSuccess) {
2145 return GHOST_kFailure;
2146 }
2147 }
2148 return GHOST_kSuccess;
2149}
2150
2152 const char *message,
2153 const char *help_label,
2154 const char *continue_label,
2155 const char *link,
2156 GHOST_DialogOptions dialog_options) const
2157{
2158 @autoreleasepool {
2159 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
2160 alert.accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 500, 0)] autorelease];
2161
2162 NSString *titleString = [NSString stringWithCString:title];
2163 NSString *messageString = [NSString stringWithCString:message];
2164 NSString *continueString = [NSString stringWithCString:continue_label];
2165 NSString *helpString = [NSString stringWithCString:help_label];
2166
2167 if (dialog_options & GHOST_DialogError) {
2168 alert.alertStyle = NSAlertStyleCritical;
2169 }
2170 else if (dialog_options & GHOST_DialogWarning) {
2171 alert.alertStyle = NSAlertStyleWarning;
2172 }
2173 else {
2174 alert.alertStyle = NSAlertStyleInformational;
2175 }
2176
2177 alert.messageText = titleString;
2178 alert.informativeText = messageString;
2179
2180 [alert addButtonWithTitle:continueString];
2181 if (link && strlen(link)) {
2182 [alert addButtonWithTitle:helpString];
2183 }
2184
2185 const NSModalResponse response = [alert runModal];
2186 if (response == NSAlertSecondButtonReturn) {
2187 NSString *linkString = [NSString stringWithCString:link];
2188 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:linkString]];
2189 }
2190 }
2191 return GHOST_kSuccess;
2192}
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned int uint
#define FALSE
#define GHOST_ASSERT(x, info)
#define GHOST_PRINT(x)
static GHOST_TKey convertKey(int rawCode, unichar recvChar)
static ImBuf * NSImageToImBuf(NSImage *image)
#define FIRSTFILEBUFLG
int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
static char g_firstFileBuf[FIRSTFILEBUFLG]
static NSSize getNSImagePixelSize(NSImage *image)
static bool g_hasFirstFile
static GHOST_TButton convertButton(int button)
static NSURL * NSPasteboardGetImageFile()
#define pushEvent
@ GHOST_kEventWheelAxisVertical
@ GHOST_kEventWheelAxisHorizontal
@ GHOST_kTrackpadEventMagnify
@ GHOST_kTrackpadEventSmartMagnify
@ GHOST_kTrackpadEventRotate
@ GHOST_kTrackpadEventScroll
GHOST_TWindowState
GHOST_TEventType
@ GHOST_kEventWindowClose
@ GHOST_kEventWindowMove
@ GHOST_kEventWindowSize
@ GHOST_kEventDraggingDropDone
@ GHOST_kEventDraggingExited
@ GHOST_kEventNativeResolutionChange
@ GHOST_kEventCursorMove
@ GHOST_kEventDraggingUpdated
@ GHOST_kEventOpenMainFile
@ GHOST_kEventDraggingEntered
@ GHOST_kEventButtonUp
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowUpdate
@ GHOST_kEventWindowDeactivate
@ GHOST_kEventButtonDown
@ GHOST_kEventKeyDown
@ GHOST_kEventKeyUp
@ GHOST_kEventQuitRequest
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
@ GHOST_kTabletModeEraser
@ GHOST_kTabletModeStylus
@ GHOST_kTabletModeNone
GHOST_TCapabilityFlag
Definition GHOST_Types.h:89
@ GHOST_kCapabilityKeyboardHyperKey
@ GHOST_kCapabilityPrimaryClipboard
void * GHOST_TDragnDropDataPtr
#define GHOST_CAPABILITY_FLAG_ALL
GHOST_TKey
@ GHOST_kKeyLeftOS
@ GHOST_kKeySemicolon
@ GHOST_kKey5
@ GHOST_kKeyZ
@ GHOST_kKeyQuote
@ GHOST_kKey4
@ GHOST_kKeyT
@ GHOST_kKeyNumpad3
@ GHOST_kKeyAccentGrave
@ GHOST_kKeyNumpad1
@ GHOST_kKeyW
@ GHOST_kKeyLeftAlt
@ GHOST_kKey3
@ GHOST_kKeyG
@ GHOST_kKeyF9
@ GHOST_kKeyC
@ GHOST_kKeyI
@ GHOST_kKeyEnter
@ GHOST_kKeyF20
@ GHOST_kKeyP
@ GHOST_kKeyJ
@ GHOST_kKeyNumpadSlash
@ GHOST_kKeyRightArrow
@ GHOST_kKeyF13
@ GHOST_kKeyF6
@ GHOST_kKeyNumpad4
@ GHOST_kKeyF11
@ GHOST_kKeyR
@ GHOST_kKeyN
@ GHOST_kKeyMinus
@ GHOST_kKeyO
@ GHOST_kKey6
@ GHOST_kKeyBackSpace
@ GHOST_kKey0
@ GHOST_kKeyF5
@ GHOST_kKeyF19
@ GHOST_kKeyDownPage
@ GHOST_kKeyDownArrow
@ GHOST_kKeyQ
@ GHOST_kKeyNumpadPeriod
@ GHOST_kKeyF12
@ GHOST_kKeyF1
@ GHOST_kKeyF
@ GHOST_kKeyU
@ GHOST_kKeyNumpadAsterisk
@ GHOST_kKeyB
@ GHOST_kKeyLeftControl
@ GHOST_kKeyLeftBracket
@ GHOST_kKey1
@ GHOST_kKeyM
@ GHOST_kKeyTab
@ GHOST_kKey8
@ GHOST_kKeyComma
@ GHOST_kKeyRightBracket
@ GHOST_kKeyBackslash
@ GHOST_kKeyNumpad2
@ GHOST_kKeyX
@ GHOST_kKeyL
@ GHOST_kKeyY
@ GHOST_kKeyPeriod
@ GHOST_kKeyNumpadPlus
@ GHOST_kKeyUpPage
@ GHOST_kKey9
@ GHOST_kKeyNumpad5
@ GHOST_kKeyLeftArrow
@ GHOST_kKeyF17
@ GHOST_kKeyD
@ GHOST_kKeyEqual
@ GHOST_kKey7
@ GHOST_kKeyS
@ GHOST_kKeyF8
@ GHOST_kKeyF18
@ GHOST_kKeyHome
@ GHOST_kKeyNumpad6
@ GHOST_kKeyF14
@ GHOST_kKeyNumpad8
@ GHOST_kKeyNumpad9
@ GHOST_kKeyF15
@ GHOST_kKeyEnd
@ GHOST_kKeyUpArrow
@ GHOST_kKeyH
@ GHOST_kKeyDelete
@ GHOST_kKeyF16
@ GHOST_kKeyNumpad0
@ GHOST_kKeyA
@ GHOST_kKey2
@ GHOST_kKeyK
@ GHOST_kKeyNumpad7
@ GHOST_kKeyEsc
@ GHOST_kKeyPlus
@ GHOST_kKeyUnknown
@ GHOST_kKeySlash
@ GHOST_kKeyV
@ GHOST_kKeyF7
@ GHOST_kKeyNumpadEnter
@ GHOST_kKeyNumpadMinus
@ GHOST_kKeyF10
@ GHOST_kKeyLeftShift
@ GHOST_kKeyF3
@ GHOST_kKeyF2
@ GHOST_kKeyF4
@ GHOST_kKeyE
@ GHOST_kKeySpace
const void * GHOST_TEventDataPtr
@ GHOST_kModifierKeyLeftControl
@ GHOST_kModifierKeyLeftAlt
@ GHOST_kModifierKeyLeftShift
@ GHOST_kModifierKeyLeftOS
GHOST_TSuccess
Definition GHOST_Types.h:80
@ GHOST_kFailure
Definition GHOST_Types.h:80
@ GHOST_kSuccess
Definition GHOST_Types.h:80
@ GHOST_kFireTimeNever
@ GHOST_gpuStereoVisual
Definition GHOST_Types.h:69
@ GHOST_gpuDebugContext
Definition GHOST_Types.h:70
GHOST_TGrabCursorMode
@ GHOST_kGrabWrap
@ GHOST_kGrabDisable
@ GHOST_kGrabHide
GHOST_TDragnDropTypes
@ GHOST_kDragnDropTypeFilenames
@ GHOST_kDragnDropTypeBitmap
@ GHOST_kDragnDropTypeString
GHOST_TButton
@ GHOST_kButtonMaskRight
@ GHOST_kButtonMaskButton4
@ GHOST_kButtonMaskLeft
@ GHOST_kButtonMaskButton7
@ GHOST_kButtonMaskButton6
@ GHOST_kButtonMaskButton5
@ GHOST_kButtonMaskMiddle
GHOST_DialogOptions
Definition GHOST_Types.h:73
@ GHOST_DialogError
Definition GHOST_Types.h:75
@ GHOST_DialogWarning
Definition GHOST_Types.h:74
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
@ IB_byte_data
#define C
Definition RandGen.cpp:29
BMesh const char void * data
PyObject * self
unsigned long long int uint64_t
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
virtual bool getValid() const =0
int32_t m_l
int32_t m_r
int32_t m_b
virtual void wrapPoint(int32_t &x, int32_t &y, int32_t ofs, GHOST_TAxisFlag axis)
int32_t m_t
bool processEvents(bool waitForEvent) override
uint * getClipboardImage(int *r_width, int *r_height) const override
GHOST_TSuccess disposeContext(GHOST_IContext *context) override
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override
GHOST_TSuccess handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa *window)
GHOST_TSuccess handleDraggingEvent(GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, GHOST_WindowCocoa *window, int mouseX, int mouseY, void *data)
bool m_needDelayedApplicationBecomeActiveEventProcessing
GHOST_TSuccess handleApplicationBecomeActiveEvent()
GHOST_TSuccess hasClipboardImage() const override
GHOST_TSuccess handleMouseEvent(void *eventPtr)
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override
GHOST_IWindow * createWindow(const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, GHOST_GPUSettings gpuSettings, const bool exclusive=false, const bool is_dialog=false, const GHOST_IWindow *parentWindow=nullptr) override
GHOST_TSuccess handleKeyEvent(void *eventPtr)
GHOST_TSuccess init() override
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const override
uint64_t getMilliSeconds() const override
GHOST_TCapabilityFlag getCapabilities() const override
GHOST_TSuccess showMessageBox(const char *title, const char *message, const char *help_label, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options) const override
uint8_t getNumDisplays() const override
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override
GHOST_TSuccess handleTabletEvent(void *eventPtr, short eventType)
bool handleOpenDocumentRequest(void *filepathStr)
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override
char * getClipboard(bool selection) const override
GHOST_TSuccess setMouseCursorPosition(int32_t x, int32_t y)
void putClipboard(const char *buffer, bool selection) const override
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override
GHOST_IWindow * getWindowUnderCursor(int32_t x, int32_t y) override
GHOST_IContext * createOffscreenContext(GHOST_GPUSettings gpuSettings) override
GHOST_TSuccess putClipboardImage(uint *rgba, int width, int height) const override
bool m_multitouchGestures
GHOST_TSuccess init() override
GHOST_TimerManager * getTimerManager() const
bool validWindow(GHOST_IWindow *window) override
GHOST_WindowManager * m_windowManager
bool fireTimers(uint64_t time)
bool isDialog() const override
NSScreen * getScreen() const
void clientToScreenIntern(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
void * getOSWindow() const override
GHOST_TabletData & GetCocoaTabletData()
void getClientBounds(GHOST_Rect &bounds) const override
void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const override
void screenToClientIntern(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const
void loadCursor(bool visible, GHOST_TStandardCursor cursor) const
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds) const override
void setCursorGrabAccum(int32_t x, int32_t y)
void getCursorGrabInitPos(int32_t &x, int32_t &y) const
GHOST_TAxisFlag getCursorGrabAxis() const
GHOST_TStandardCursor getCursorShape() const override
bool getCursorVisibility() const override
GHOST_TGrabCursorMode getCursorGrabMode() const
GHOST_TSuccess updateDrawingContext()
bool getCursorGrabModeIsWarp() const
void getCursorGrabAccum(int32_t &x, int32_t &y) const
uint top
#define in
IMG_TEMPLATE SizeVec imageSize(const T &) RET
#define printf(...)
float length(VecOp< float, D >) RET
GHOST_SystemCocoa * systemCocoa
int count
static ulong * next
static ulong state[N]
static int left
void set(GHOST_TButton mask, bool down)
GHOST_TDrawingContextType context_type
GHOST_GPUDevice preferred_device
void set(GHOST_TModifierKey mask, bool down)
GHOST_TTabletMode Active
ImBufByteBuffer byte_buffer
i
Definition text_draw.cc:230
void WM_exit(bContext *C, const int exit_code)