|
@@ -104,46 +104,6 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
|
|
|
return [NSCursor arrowCursor];
|
|
|
}
|
|
|
|
|
|
-/*************************************************************************/
|
|
|
-/* GodotApplication */
|
|
|
-/*************************************************************************/
|
|
|
-
|
|
|
-@interface GodotApplication : NSApplication
|
|
|
-@end
|
|
|
-
|
|
|
-@implementation GodotApplication
|
|
|
-
|
|
|
-- (void)sendEvent:(NSEvent *)event {
|
|
|
- // special case handling of command-period, which is traditionally a special
|
|
|
- // shortcut in macOS and doesn't arrive at our regular keyDown handler.
|
|
|
- if ([event type] == NSEventTypeKeyDown) {
|
|
|
- if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
|
|
|
- Ref<InputEventKey> k;
|
|
|
- k.instantiate();
|
|
|
-
|
|
|
- _get_key_modifier_state([event modifierFlags], k);
|
|
|
- k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
|
|
|
- k->set_pressed(true);
|
|
|
- k->set_keycode(KEY_PERIOD);
|
|
|
- k->set_physical_keycode(KEY_PERIOD);
|
|
|
- k->set_echo([event isARepeat]);
|
|
|
-
|
|
|
- Input::get_singleton()->accumulate_input_event(k);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
|
|
|
- // This works around an AppKit bug, where key up events while holding
|
|
|
- // down the command key don't get sent to the key window.
|
|
|
- if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) {
|
|
|
- [[self keyWindow] sendEvent:event];
|
|
|
- } else {
|
|
|
- [super sendEvent:event];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@end
|
|
|
-
|
|
|
/*************************************************************************/
|
|
|
/* GlobalMenuItem */
|
|
|
/*************************************************************************/
|
|
@@ -160,121 +120,6 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
|
|
|
@implementation GlobalMenuItem
|
|
|
@end
|
|
|
|
|
|
-/*************************************************************************/
|
|
|
-/* GodotApplicationDelegate */
|
|
|
-/*************************************************************************/
|
|
|
-
|
|
|
-@interface GodotApplicationDelegate : NSObject
|
|
|
-- (void)forceUnbundledWindowActivationHackStep1;
|
|
|
-- (void)forceUnbundledWindowActivationHackStep2;
|
|
|
-- (void)forceUnbundledWindowActivationHackStep3;
|
|
|
-@end
|
|
|
-
|
|
|
-@implementation GodotApplicationDelegate
|
|
|
-
|
|
|
-- (void)forceUnbundledWindowActivationHackStep1 {
|
|
|
- // Step1: Switch focus to macOS Dock.
|
|
|
- // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
|
|
|
- for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
|
|
|
- [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
|
|
|
- break;
|
|
|
- }
|
|
|
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)forceUnbundledWindowActivationHackStep2 {
|
|
|
- // Step 2: Register app as foreground process.
|
|
|
- ProcessSerialNumber psn = { 0, kCurrentProcess };
|
|
|
- (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
|
|
|
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)forceUnbundledWindowActivationHackStep3 {
|
|
|
- // Step 3: Switch focus back to app window.
|
|
|
- [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)applicationDidFinishLaunching:(NSNotification *)notice {
|
|
|
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
|
|
|
- if (nsappname == nil) {
|
|
|
- // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
|
|
|
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)applicationDidResignActive:(NSNotification *)notification {
|
|
|
- if (OS_OSX::get_singleton()->get_main_loop()) {
|
|
|
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)applicationDidBecomeActive:(NSNotification *)notification {
|
|
|
- if (OS_OSX::get_singleton()->get_main_loop()) {
|
|
|
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)globalMenuCallback:(id)sender {
|
|
|
- if (![sender representedObject]) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- GlobalMenuItem *value = [sender representedObject];
|
|
|
-
|
|
|
- if (value) {
|
|
|
- if (value->checkable) {
|
|
|
- if ([sender state] == NSControlStateValueOff) {
|
|
|
- [sender setState:NSControlStateValueOn];
|
|
|
- } else {
|
|
|
- [sender setState:NSControlStateValueOff];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (value->callback != Callable()) {
|
|
|
- Variant tag = value->meta;
|
|
|
- Variant *tagp = &tag;
|
|
|
- Variant ret;
|
|
|
- Callable::CallError ce;
|
|
|
- value->callback.call((const Variant **)&tagp, 1, ret, ce);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
|
|
|
- return DS_OSX->dock_menu;
|
|
|
-}
|
|
|
-
|
|
|
-- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
|
|
|
- // Note: may be called called before main loop init!
|
|
|
- char *utfs = strdup([filename UTF8String]);
|
|
|
- ((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename.parse_utf8(utfs);
|
|
|
- free(utfs);
|
|
|
-
|
|
|
-#ifdef TOOLS_ENABLED
|
|
|
- // Open new instance
|
|
|
- if (OS_OSX::get_singleton()->get_main_loop()) {
|
|
|
- List<String> args;
|
|
|
- args.push_back(((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename);
|
|
|
- String exec = OS::get_singleton()->get_executable_path();
|
|
|
- OS::get_singleton()->create_process(exec, args);
|
|
|
- }
|
|
|
-#endif
|
|
|
- return YES;
|
|
|
-}
|
|
|
-
|
|
|
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
|
|
- DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
|
|
|
- return NSTerminateCancel;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)showAbout:(id)sender {
|
|
|
- if (OS_OSX::get_singleton()->get_main_loop()) {
|
|
|
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-@end
|
|
|
-
|
|
|
/*************************************************************************/
|
|
|
/* GodotWindowDelegate */
|
|
|
/*************************************************************************/
|
|
@@ -1983,26 +1828,6 @@ void DisplayServerOSX::global_menu_clear(const String &p_menu_root) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void DisplayServerOSX::alert(const String &p_alert, const String &p_title) {
|
|
|
- _THREAD_SAFE_METHOD_
|
|
|
-
|
|
|
- NSAlert *window = [[NSAlert alloc] init];
|
|
|
- NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
|
|
|
- NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
|
|
|
-
|
|
|
- [window addButtonWithTitle:@"OK"];
|
|
|
- [window setMessageText:ns_title];
|
|
|
- [window setInformativeText:ns_alert];
|
|
|
- [window setAlertStyle:NSAlertStyleWarning];
|
|
|
-
|
|
|
- id key_window = [[NSApplication sharedApplication] keyWindow];
|
|
|
- [window runModal];
|
|
|
- [window release];
|
|
|
- if (key_window) {
|
|
|
- [key_window makeKeyAndOrderFront:nil];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
@@ -3375,6 +3200,56 @@ void DisplayServerOSX::_release_pressed_events() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+NSMenu *DisplayServerOSX::_get_dock_menu() const {
|
|
|
+ return dock_menu;
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::_menu_callback(id p_sender) {
|
|
|
+ if (![p_sender representedObject]) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ GlobalMenuItem *value = [p_sender representedObject];
|
|
|
+
|
|
|
+ if (value) {
|
|
|
+ if (value->checkable) {
|
|
|
+ if ([p_sender state] == NSControlStateValueOff) {
|
|
|
+ [p_sender setState:NSControlStateValueOn];
|
|
|
+ } else {
|
|
|
+ [p_sender setState:NSControlStateValueOff];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value->callback != Callable()) {
|
|
|
+ Variant tag = value->meta;
|
|
|
+ Variant *tagp = &tag;
|
|
|
+ Variant ret;
|
|
|
+ Callable::CallError ce;
|
|
|
+ value->callback.call((const Variant **)&tagp, 1, ret, ce);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::_send_event(NSEvent *p_event) {
|
|
|
+ // special case handling of command-period, which is traditionally a special
|
|
|
+ // shortcut in macOS and doesn't arrive at our regular keyDown handler.
|
|
|
+ if ([p_event type] == NSEventTypeKeyDown) {
|
|
|
+ if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
|
|
|
+ Ref<InputEventKey> k;
|
|
|
+ k.instantiate();
|
|
|
+
|
|
|
+ _get_key_modifier_state([p_event modifierFlags], k);
|
|
|
+ k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
|
|
|
+ k->set_pressed(true);
|
|
|
+ k->set_keycode(KEY_PERIOD);
|
|
|
+ k->set_physical_keycode(KEY_PERIOD);
|
|
|
+ k->set_echo([p_event isARepeat]);
|
|
|
+
|
|
|
+ Input::get_singleton()->accumulate_input_event(k);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void DisplayServerOSX::_process_key_events() {
|
|
|
Ref<InputEventKey> k;
|
|
|
for (int i = 0; i < key_event_pos; i++) {
|
|
@@ -3615,7 +3490,7 @@ ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) co
|
|
|
DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
|
|
|
DisplayServer *ds = memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error));
|
|
|
if (r_error != OK) {
|
|
|
- ds->alert("Your video card driver does not support any of the supported Metal versions.", "Unable to initialize Video driver");
|
|
|
+ OS::get_singleton()->alert("Your video card driver does not support any of the supported Metal versions.", "Unable to initialize Video driver");
|
|
|
}
|
|
|
return ds;
|
|
|
}
|
|
@@ -3785,12 +3660,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
|
|
|
|
|
|
CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
|
|
|
|
|
|
- // Implicitly create shared NSApplication instance
|
|
|
- [GodotApplication sharedApplication];
|
|
|
-
|
|
|
- // In case we are unbundled, make us a proper UI application
|
|
|
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
|
|
-
|
|
|
keyboard_layout_dirty = true;
|
|
|
displays_arrangement_dirty = true;
|
|
|
displays_scale_dirty = true;
|
|
@@ -3804,9 +3673,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
|
|
|
// Register to be notified on displays arrangement changes
|
|
|
CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, nullptr);
|
|
|
|
|
|
- // Menu bar setup must go between sharedApplication above and
|
|
|
- // finishLaunching below, in order to properly emulate the behavior
|
|
|
- // of NSApplicationMain
|
|
|
NSMenuItem *menu_item;
|
|
|
NSString *title;
|
|
|
|
|
@@ -3846,32 +3712,10 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
|
|
|
title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
|
|
|
[apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
|
|
|
|
|
- // Setup menu bar
|
|
|
- NSMenu *main_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
|
|
|
+ // Add items to the menu bar
|
|
|
+ NSMenu *main_menu = [NSApp mainMenu];
|
|
|
menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
|
|
|
[main_menu setSubmenu:apple_menu forItem:menu_item];
|
|
|
- [NSApp setMainMenu:main_menu];
|
|
|
-
|
|
|
- [NSApp finishLaunching];
|
|
|
-
|
|
|
- delegate = [[GodotApplicationDelegate alloc] init];
|
|
|
- ERR_FAIL_COND(!delegate);
|
|
|
- [NSApp setDelegate:delegate];
|
|
|
-
|
|
|
- //process application:openFile: event
|
|
|
- while (true) {
|
|
|
- NSEvent *event = [NSApp
|
|
|
- nextEventMatchingMask:NSEventMaskAny
|
|
|
- untilDate:[NSDate distantPast]
|
|
|
- inMode:NSDefaultRunLoopMode
|
|
|
- dequeue:YES];
|
|
|
-
|
|
|
- if (event == nil) {
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- [NSApp sendEvent:event];
|
|
|
- }
|
|
|
|
|
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
//TODO - do Vulkan and GLES2 support checks, driver selection and fallback
|
|
@@ -3924,8 +3768,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
|
|
|
RendererCompositorRD::make_current();
|
|
|
}
|
|
|
#endif
|
|
|
-
|
|
|
- [NSApp activateIgnoringOtherApps:YES];
|
|
|
}
|
|
|
|
|
|
DisplayServerOSX::~DisplayServerOSX() {
|