[macOS] Cleanup and split Objective-C objects to the separate files

This commit is contained in:
bruvzg 2022-01-26 11:01:27 +02:00
parent 33d6d4bdf7
commit b84ef16aa7
No known key found for this signature in database
GPG Key ID: 7960FCF39844EC38
26 changed files with 3460 additions and 2679 deletions

View File

@ -6,14 +6,21 @@ from platform_methods import run_in_subprocess
import platform_osx_builders
files = [
"crash_handler_osx.mm",
"os_osx.mm",
"godot_application.mm",
"godot_application_delegate.mm",
"crash_handler_osx.mm",
"osx_terminal_logger.mm",
"display_server_osx.mm",
"godot_content_view.mm",
"godot_window_delegate.mm",
"godot_window.mm",
"key_mapping_osx.mm",
"godot_main_osx.mm",
"dir_access_osx.mm",
"joypad_osx.cpp",
"vulkan_context_osx.mm",
"gl_manager_osx.mm",
"gl_manager_osx_legacy.mm",
]
prog = env.add_program("#bin/godot", files)

View File

@ -78,9 +78,9 @@ def configure(env):
env["osxcross"] = True
if env["arch"] == "arm64":
print("Building for macOS 10.15+, platform arm64.")
env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
print("Building for macOS 11.00+, platform arm64.")
env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.00"])
env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.00"])
else:
print("Building for macOS 10.12+, platform x86_64.")
env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
@ -190,6 +190,8 @@ def configure(env):
env.Append(CCFLAGS=["-Wno-deprecated-declarations"]) # Disable deprecation warnings
env.Append(LINKFLAGS=["-framework", "OpenGL"])
env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"])
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-framework", "IOSurface"])

View File

@ -37,13 +37,13 @@
#include "servers/display_server.h"
#if defined(GLES3_ENABLED)
#include "gl_manager_osx.h"
#endif
#include "gl_manager_osx_legacy.h"
#endif // GLES3_ENABLED
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/osx/vulkan_context_osx.h"
#endif
#endif // VULKAN_ENABLED
#include <AppKit/AppKit.h>
#include <AppKit/NSCursor.h>
@ -59,27 +59,8 @@ class DisplayServerOSX : public DisplayServer {
_THREAD_SAFE_CLASS_
public:
void _send_event(NSEvent *p_event);
NSMenu *_get_dock_menu() const;
void _menu_callback(id p_sender);
#if defined(GLES3_ENABLED)
GLManager_OSX *gl_manager = nullptr;
#endif
#if defined(VULKAN_ENABLED)
VulkanContextOSX *context_vulkan = nullptr;
RenderingDeviceVulkan *rendering_device_vulkan = nullptr;
#endif
const NSMenu *_get_menu_root(const String &p_menu_root) const;
NSMenu *_get_menu_root(const String &p_menu_root);
NSMenu *apple_menu = nullptr;
NSMenu *dock_menu = nullptr;
Map<String, NSMenu *> submenu;
struct KeyEvent {
WindowID window_id;
WindowID window_id = INVALID_WINDOW_ID;
unsigned int osx_state = false;
bool pressed = false;
bool echo = false;
@ -89,20 +70,6 @@ public:
uint32_t unicode = 0;
};
struct WarpEvent {
NSTimeInterval timestamp;
NSPoint delta;
};
List<WarpEvent> warp_events;
NSTimeInterval last_warp = 0;
bool ignore_warp = false;
float display_max_scale = 1.f;
Vector<KeyEvent> key_event_buffer;
int key_event_pos;
struct WindowData {
id window_delegate;
id window_object;
@ -116,8 +83,6 @@ public:
Size2i max_size;
Size2i size;
bool mouse_down_control = false;
bool im_active = false;
Size2i im_position;
@ -140,48 +105,102 @@ public:
bool no_focus = false;
};
private:
#if defined(GLES3_ENABLED)
GLManager_OSX *gl_manager = nullptr;
#endif
#if defined(VULKAN_ENABLED)
VulkanContextOSX *context_vulkan = nullptr;
RenderingDeviceVulkan *rendering_device_vulkan = nullptr;
#endif
String rendering_driver;
NSMenu *apple_menu = nullptr;
NSMenu *dock_menu = nullptr;
Map<String, NSMenu *> submenu;
struct WarpEvent {
NSTimeInterval timestamp;
NSPoint delta;
};
List<WarpEvent> warp_events;
NSTimeInterval last_warp = 0;
bool ignore_warp = false;
Vector<KeyEvent> key_event_buffer;
int key_event_pos = 0;
Point2i im_selection;
String im_text;
Map<WindowID, WindowData> windows;
CGEventSourceRef event_source;
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
MouseButton last_button_state = MouseButton::NONE;
bool drop_events = false;
bool in_dispatch_input_event = false;
struct LayoutInfo {
String name;
String code;
};
Vector<LayoutInfo> kbd_layouts;
int current_layout = 0;
bool keyboard_layout_dirty = true;
WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
float display_max_scale = 1.f;
Point2i origin;
bool displays_arrangement_dirty = true;
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
void _update_window(WindowData p_wd);
void _send_window_event(const WindowData &wd, WindowEvent p_event);
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
WindowID _find_window_id(id p_window);
void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window);
Point2i _get_screens_origin() const;
Point2i _get_native_screen_position(int p_screen) const;
void _push_input(const Ref<InputEvent> &p_event);
void _process_key_events();
void _release_pressed_events();
String rendering_driver;
CGEventSourceRef eventSource;
CursorShape cursor_shape;
CursorShape cursor_shape = CURSOR_ARROW;
NSCursor *cursors[CURSOR_MAX];
Map<CursorShape, Vector<Variant>> cursors_cache;
MouseMode mouse_mode;
Point2i last_mouse_pos;
MouseButton last_button_state = MouseButton::NONE;
Map<WindowID, WindowData> windows;
bool window_focused;
bool drop_events;
bool in_dispatch_input_event = false;
const NSMenu *_get_menu_root(const String &p_menu_root) const;
NSMenu *_get_menu_root(const String &p_menu_root);
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
void _update_window_style(WindowData p_wd);
void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window);
void _update_displays_arrangement();
Point2i _get_screens_origin() const;
Point2i _get_native_screen_position(int p_screen) const;
static void _displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info);
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
void _dispatch_input_event(const Ref<InputEvent> &p_event);
void _push_input(const Ref<InputEvent> &p_event);
void _process_key_events();
void _update_keyboard_layouts();
static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info);
static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil);
public:
NSMenu *get_dock_menu() const;
void menu_callback(id p_sender);
bool has_window(WindowID p_window) const;
WindowData &get_window(WindowID p_window);
void send_event(NSEvent *p_event);
void send_window_event(const WindowData &p_wd, WindowEvent p_event);
void release_pressed_events();
void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) const;
void update_mouse_pos(WindowData &p_wd, NSPoint p_location_in_window);
void push_to_key_event_buffer(const KeyEvent &p_event);
void update_im_text(const Point2i &p_selection, const String &p_text);
void set_last_focused_window(WindowID p_window);
void window_update(WindowID p_window);
void window_destroy(WindowID p_window);
void window_resize(WindowID p_window, int p_width, int p_height);
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
@ -215,8 +234,10 @@ public:
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp);
virtual void mouse_warp_to_position(const Point2i &p_to) override;
virtual Point2i mouse_get_position() const override;
void mouse_set_button_state(MouseButton p_state);
virtual MouseButton mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
@ -295,6 +316,7 @@ public:
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
void cursor_update_shape();
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*************************************************************************/
/* gl_manager_osx.h */
/* gl_manager_osx_legacy.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GL_MANAGER_OSX_H
#define GL_MANAGER_OSX_H
#ifndef GL_MANAGER_OSX_LEGACY_H
#define GL_MANAGER_OSX_LEGACY_H
#if defined(OSX_ENABLED) && defined(GLES3_ENABLED)
@ -50,29 +50,21 @@ public:
private:
struct GLWindow {
GLWindow() { in_use = false; }
bool in_use;
int width = 0;
int height = 0;
DisplayServer::WindowID window_id;
int width;
int height;
id window_view;
NSOpenGLContext *context;
id window_view = nullptr;
NSOpenGLContext *context = nullptr;
};
LocalVector<GLWindow> _windows;
Map<DisplayServer::WindowID, GLWindow> windows;
NSOpenGLContext *_shared_context = nullptr;
GLWindow *_current_window;
NSOpenGLContext *shared_context = nullptr;
DisplayServer::WindowID current_window = DisplayServer::INVALID_WINDOW_ID;
Error _create_context(GLWindow &win);
void _internal_set_current_window(GLWindow *p_win);
Error create_context(GLWindow &win);
GLWindow &get_window(unsigned int id) { return _windows[id]; }
const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
bool use_vsync;
bool use_vsync = false;
ContextType context_type;
public:
@ -80,7 +72,6 @@ public:
void window_destroy(DisplayServer::WindowID p_window_id);
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
// get directly from the cached GLWindow
int window_get_width(DisplayServer::WindowID p_window_id = 0);
int window_get_height(DisplayServer::WindowID p_window_id = 0);
@ -91,6 +82,7 @@ public:
void window_make_current(DisplayServer::WindowID p_window_id);
void window_update(DisplayServer::WindowID p_window_id);
void window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled);
Error initialize();
@ -101,6 +93,5 @@ public:
~GLManager_OSX();
};
#endif // defined(OSX_ENABLED) && defined(GLES3_ENABLED)
#endif // GL_MANAGER_OSX_H
#endif // OSX_ENABLED && GLES3_ENABLED
#endif // GL_MANAGER_OSX_LEGACY_H

View File

@ -1,5 +1,5 @@
/*************************************************************************/
/* gl_manager_osx.mm */
/* gl_manager_osx_legacy.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "gl_manager_osx.h"
#include "gl_manager_osx_legacy.h"
#ifdef OSX_ENABLED
#ifdef GLES3_ENABLED
@ -36,7 +36,7 @@
#include <stdio.h>
#include <stdlib.h>
Error GLManager_OSX::_create_context(GLWindow &win) {
Error GLManager_OSX::create_context(GLWindow &win) {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAClosestPolicy,
@ -50,10 +50,10 @@ Error GLManager_OSX::_create_context(GLWindow &win) {
NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
ERR_FAIL_COND_V(pixel_format == nil, ERR_CANT_CREATE);
win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:_shared_context];
win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context];
ERR_FAIL_COND_V(win.context == nil, ERR_CANT_CREATE);
if (_shared_context == nullptr) {
_shared_context = win.context;
if (shared_context == nullptr) {
shared_context = win.context;
}
[win.context setView:win.window_view];
@ -63,40 +63,27 @@ Error GLManager_OSX::_create_context(GLWindow &win) {
}
Error GLManager_OSX::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) {
if (p_window_id >= (int)_windows.size()) {
_windows.resize(p_window_id + 1);
}
GLWindow &win = _windows[p_window_id];
win.in_use = true;
win.window_id = p_window_id;
GLWindow win;
win.width = p_width;
win.height = p_height;
win.window_view = p_view;
if (_create_context(win) != OK) {
_windows.remove_at(_windows.size() - 1);
if (create_context(win) != OK) {
return FAILED;
}
window_make_current(_windows.size() - 1);
windows[p_window_id] = win;
window_make_current(p_window_id);
return OK;
}
void GLManager_OSX::_internal_set_current_window(GLWindow *p_win) {
_current_window = p_win;
}
void GLManager_OSX::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {
if (p_window_id == -1) {
if (!windows.has(p_window_id)) {
return;
}
GLWindow &win = _windows[p_window_id];
if (!win.in_use) {
return;
}
GLWindow &win = windows[p_window_id];
win.width = p_width;
win.height = p_height;
@ -116,24 +103,37 @@ void GLManager_OSX::window_resize(DisplayServer::WindowID p_window_id, int p_wid
}
int GLManager_OSX::window_get_width(DisplayServer::WindowID p_window_id) {
return get_window(p_window_id).width;
if (!windows.has(p_window_id)) {
return 0;
}
GLWindow &win = windows[p_window_id];
return win.width;
}
int GLManager_OSX::window_get_height(DisplayServer::WindowID p_window_id) {
return get_window(p_window_id).height;
if (!windows.has(p_window_id)) {
return 0;
}
GLWindow &win = windows[p_window_id];
return win.height;
}
void GLManager_OSX::window_destroy(DisplayServer::WindowID p_window_id) {
GLWindow &win = get_window(p_window_id);
win.in_use = false;
if (_current_window == &win) {
_current_window = nullptr;
if (!windows.has(p_window_id)) {
return;
}
if (current_window == p_window_id) {
current_window = DisplayServer::INVALID_WINDOW_ID;
}
windows.erase(p_window_id);
}
void GLManager_OSX::release_current() {
if (!_current_window) {
if (current_window == DisplayServer::INVALID_WINDOW_ID) {
return;
}
@ -141,63 +141,59 @@ void GLManager_OSX::release_current() {
}
void GLManager_OSX::window_make_current(DisplayServer::WindowID p_window_id) {
if (p_window_id == -1) {
return;
}
GLWindow &win = _windows[p_window_id];
if (!win.in_use) {
return;
}
if (&win == _current_window) {
if (current_window == p_window_id) {
return;
}
if (!windows.has(p_window_id)) {
return;
}
GLWindow &win = windows[p_window_id];
[win.context makeCurrentContext];
_internal_set_current_window(&win);
current_window = p_window_id;
}
void GLManager_OSX::make_current() {
if (!_current_window) {
if (current_window == DisplayServer::INVALID_WINDOW_ID) {
return;
}
if (!_current_window->in_use) {
WARN_PRINT("current window not in use!");
if (!windows.has(current_window)) {
return;
}
[_current_window->context makeCurrentContext];
GLWindow &win = windows[current_window];
[win.context makeCurrentContext];
}
void GLManager_OSX::swap_buffers() {
// NO NEED TO CALL SWAP BUFFERS for each window...
// see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
if (!_current_window) {
return;
for (Map<DisplayServer::WindowID, GLWindow>::Element *E = windows.front(); E; E = E->next()) {
[E->get().context flushBuffer];
}
if (!_current_window->in_use) {
WARN_PRINT("current window not in use!");
return;
}
[_current_window->context flushBuffer];
}
void GLManager_OSX::window_update(DisplayServer::WindowID p_window_id) {
if (p_window_id == -1) {
if (!windows.has(p_window_id)) {
return;
}
GLWindow &win = _windows[p_window_id];
if (!win.in_use) {
GLWindow &win = windows[p_window_id];
[win.context update];
}
void GLManager_OSX::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) {
if (!windows.has(p_window_id)) {
return;
}
if (&win == _current_window) {
return;
GLWindow &win = windows[p_window_id];
if (p_enabled) {
GLint opacity = 0;
[win.context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
} else {
GLint opacity = 1;
[win.context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
}
[win.context update];
}
@ -207,6 +203,7 @@ Error GLManager_OSX::initialize() {
void GLManager_OSX::set_use_vsync(bool p_use) {
use_vsync = p_use;
CGLContextObj ctx = CGLGetCurrentContext();
if (ctx) {
GLint swapInterval = p_use ? 1 : 0;
@ -221,8 +218,6 @@ bool GLManager_OSX::is_using_vsync() const {
GLManager_OSX::GLManager_OSX(ContextType p_context_type) {
context_type = p_context_type;
use_vsync = false;
_current_window = nullptr;
}
GLManager_OSX::~GLManager_OSX() {

View File

@ -0,0 +1,42 @@
/*************************************************************************/
/* godot_application.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOT_APPLICATION_H
#define GODOT_APPLICATION_H
#include "core/os/os.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@interface GodotApplication : NSApplication
@end
#endif // GODOT_APPLICATION_H

View File

@ -0,0 +1,53 @@
/*************************************************************************/
/* godot_application.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godot_application.h"
#include "display_server_osx.h"
@implementation GodotApplication
- (void)sendEvent:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (ds) {
ds->send_event(event);
}
// 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

View File

@ -0,0 +1,45 @@
/*************************************************************************/
/* godot_application_delegate.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOT_APPLICATION_DELEGATE_H
#define GODOT_APPLICATION_DELEGATE_H
#include "core/os/os.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@interface GodotApplicationDelegate : NSObject
- (void)forceUnbundledWindowActivationHackStep1;
- (void)forceUnbundledWindowActivationHackStep2;
- (void)forceUnbundledWindowActivationHackStep3;
@end
#endif // GODOT_APPLICATION_DELEGATE_H

View File

@ -0,0 +1,132 @@
/*************************************************************************/
/* godot_application_delegate.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godot_application_delegate.h"
#include "display_server_osx.h"
#include "os_osx.h"
@implementation GodotApplicationDelegate
- (void)forceUnbundledWindowActivationHackStep1 {
// Step 1: Switch focus to macOS SystemUIServer process.
// Required to perform step 2, TransformProcessType will fail if app is already the in focus.
for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.systemuiserver"]) {
[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 || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) {
// If the executable is started from terminal or is not 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::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
}
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
}
}
- (void)globalMenuCallback:(id)sender {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (ds) {
return ds->menu_callback(sender);
}
}
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (ds) {
return ds->get_dock_menu();
} else {
return nullptr;
}
}
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
// Note: may be called called before main loop init!
OS_OSX *os = (OS_OSX *)OS::get_singleton();
if (os) {
os->set_open_with_filename(String::utf8([filename UTF8String]));
}
#ifdef TOOLS_ENABLED
// Open new instance.
if (os && os->get_main_loop()) {
List<String> args;
args.push_back(os->get_open_with_filename());
String exec = os->get_executable_path();
os->create_process(exec, args);
}
#endif
return YES;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (ds) {
ds->send_window_event(ds->get_window(DisplayServerOSX::MAIN_WINDOW_ID), DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
}
return NSTerminateCancel;
}
- (void)showAbout:(id)sender {
OS_OSX *os = (OS_OSX *)OS::get_singleton();
if (os && os->get_main_loop()) {
os->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
}
}
@end

View File

@ -0,0 +1,65 @@
/*************************************************************************/
/* godot_content_view.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOT_CONTENT_VIEW_H
#define GODOT_CONTENT_VIEW_H
#include "servers/display_server.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#if defined(GLES3_ENABLED)
#import <AppKit/NSOpenGLView.h>
#define RootView NSOpenGLView
#else
#define RootView NSView
#endif
#import <QuartzCore/CAMetalLayer.h>
@interface GodotContentView : RootView <NSTextInputClient> {
DisplayServer::WindowID window_id;
NSTrackingArea *tracking_area;
NSMutableAttributedString *marked_text;
bool ime_input_event_in_progress;
bool mouse_down_control;
bool ignore_momentum_scroll;
}
- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor;
- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy;
- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed;
- (void)setWindowID:(DisplayServer::WindowID)wid;
- (void)cancelComposition;
@end
#endif // GODOT_CONTENT_VIEW_H

View File

@ -0,0 +1,760 @@
/*************************************************************************/
/* godot_content_view.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godot_content_view.h"
#include "display_server_osx.h"
#include "key_mapping_osx.h"
@implementation GodotContentView
- (id)init {
self = [super init];
window_id = DisplayServer::INVALID_WINDOW_ID;
tracking_area = nil;
ime_input_event_in_progress = false;
mouse_down_control = false;
ignore_momentum_scroll = false;
[self updateTrackingAreas];
if (@available(macOS 10.13, *)) {
[self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM.
} else {
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
#endif
}
marked_text = [[NSMutableAttributedString alloc] init];
return self;
}
- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
window_id = wid;
}
// MARK: Backing Layer
- (CALayer *)makeBackingLayer {
return [[CAMetalLayer class] layer];
}
- (void)updateLayer {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
ds->window_update(window_id);
[super updateLayer];
}
- (BOOL)wantsUpdateLayer {
return YES;
}
- (BOOL)isOpaque {
return YES;
}
// MARK: IME
- (BOOL)hasMarkedText {
return (marked_text.length > 0);
}
- (NSRange)markedRange {
return NSMakeRange(0, marked_text.length);
}
- (NSRange)selectedRange {
static const NSRange kEmptyRange = { NSNotFound, 0 };
return kEmptyRange;
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
if ([aString isKindOfClass:[NSAttributedString class]]) {
marked_text = [[NSMutableAttributedString alloc] initWithAttributedString:aString];
} else {
marked_text = [[NSMutableAttributedString alloc] initWithString:aString];
}
if (marked_text.length == 0) {
[self unmarkText];
return;
}
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
if (wd.im_active) {
ime_input_event_in_progress = true;
ds->update_im_text(Point2i(selectedRange.location, selectedRange.length), String::utf8([[marked_text mutableString] UTF8String]));
}
}
- (void)unmarkText {
ime_input_event_in_progress = false;
[[marked_text mutableString] setString:@""];
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
if (wd.im_active) {
ds->update_im_text(Point2i(), String());
}
}
- (NSArray *)validAttributesForMarkedText {
return [NSArray array];
}
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
return nil;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
return 0;
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return NSMakeRect(0, 0, 0, 0);
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
const NSRect content_rect = [wd.window_view frame];
const float scale = ds->screen_get_max_scale();
NSRect point_in_window_rect = NSMakeRect(wd.im_position.x / scale, content_rect.size.height - (wd.im_position.y / scale) - 1, 0, 0);
NSPoint point_on_screen = [wd.window_object convertRectToScreen:point_in_window_rect].origin;
return NSMakeRect(point_on_screen.x, point_on_screen.y, 0, 0);
}
- (void)cancelComposition {
[self unmarkText];
[[NSTextInputContext currentInputContext] discardMarkedText];
}
- (void)insertText:(id)aString {
[self insertText:aString replacementRange:NSMakeRange(0, 0)];
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
NSEvent *event = [NSApp currentEvent];
NSString *characters;
if ([aString isKindOfClass:[NSAttributedString class]]) {
characters = [aString string];
} else {
characters = (NSString *)aString;
}
NSCharacterSet *ctrl_chars = [NSCharacterSet controlCharacterSet];
NSCharacterSet *wsnl_chars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
if ([characters rangeOfCharacterFromSet:ctrl_chars].length && [characters rangeOfCharacterFromSet:wsnl_chars].length == 0) {
[[NSTextInputContext currentInputContext] discardMarkedText];
[self cancelComposition];
return;
}
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
[self cancelComposition];
return;
}
Char16String text;
text.resize([characters length] + 1);
[characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
String u32text;
u32text.parse_utf16(text.ptr(), text.length());
for (int i = 0; i < u32text.length(); i++) {
const char32_t codepoint = u32text[i];
if ((codepoint & 0xFF00) == 0xF700) {
continue;
}
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = false;
ke.raw = false; // IME input event.
ke.keycode = Key::NONE;
ke.physical_keycode = Key::NONE;
ke.unicode = codepoint;
ds->push_to_key_event_buffer(ke);
}
[self cancelComposition];
}
// MARK: Drag and drop
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
return NSDragOperationCopy;
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
return NSDragOperationCopy;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return NO;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
if (!wd.drop_files_callback.is_null()) {
Vector<String> files;
NSPasteboard *pboard = [sender draggingPasteboard];
if (@available(macOS 10.13, *)) {
NSArray *items = pboard.pasteboardItems;
for (NSPasteboardItem *item in items) {
NSString *url = [item stringForType:NSPasteboardTypeFileURL];
NSString *file = [NSURL URLWithString:url].path;
files.push_back(String::utf8([file UTF8String]));
}
#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM.
} else {
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
for (NSString *file in filenames) {
files.push_back(String::utf8([file UTF8String]));
}
#endif
}
Variant v = files;
Variant *vp = &v;
Variant ret;
Callable::CallError ce;
wd.drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
}
return NO;
}
// MARK: Focus
- (BOOL)canBecomeKeyView {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return YES;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
return !wd.no_focus;
}
- (BOOL)acceptsFirstResponder {
return YES;
}
// MARK: Mouse
- (void)cursorUpdate:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds) {
return;
}
ds->cursor_update_shape();
}
- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
MouseButton last_button_state = ds->mouse_get_button_state();
if (pressed) {
last_button_state |= mask;
} else {
last_button_state &= (MouseButton)~mask;
}
ds->mouse_set_button_state(last_button_state);
Ref<InputEventMouseButton> mb;
mb.instantiate();
mb->set_window_id(window_id);
ds->update_mouse_pos(wd, [event locationInWindow]);
ds->get_key_modifier_state([event modifierFlags], mb);
mb->set_button_index(index);
mb->set_pressed(pressed);
mb->set_position(wd.mouse_pos);
mb->set_global_position(wd.mouse_pos);
mb->set_button_mask(last_button_state);
if (index == MouseButton::LEFT && pressed) {
mb->set_double_click([event clickCount] == 2);
}
Input::get_singleton()->parse_input_event(mb);
}
- (void)mouseDown:(NSEvent *)event {
if (([event modifierFlags] & NSEventModifierFlagControl)) {
mouse_down_control = true;
[self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
} else {
mouse_down_control = false;
[self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:true];
}
}
- (void)mouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
- (void)mouseUp:(NSEvent *)event {
if (mouse_down_control) {
[self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
} else {
[self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:false];
}
}
- (void)mouseMoved:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
NSPoint delta = NSMakePoint([event deltaX], [event deltaY]);
NSPoint mpos = [event locationInWindow];
if (ds->update_mouse_wrap(wd, delta, mpos, [event timestamp])) {
return;
}
Ref<InputEventMouseMotion> mm;
mm.instantiate();
mm->set_window_id(window_id);
mm->set_button_mask(ds->mouse_get_button_state());
ds->update_mouse_pos(wd, mpos);
mm->set_position(wd.mouse_pos);
mm->set_pressure([event pressure]);
if ([event subtype] == NSEventSubtypeTabletPoint) {
const NSPoint p = [event tilt];
mm->set_tilt(Vector2(p.x, p.y));
}
mm->set_global_position(wd.mouse_pos);
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * ds->screen_get_max_scale();
mm->set_relative(relativeMotion);
ds->get_key_modifier_state([event modifierFlags], mm);
Input::get_singleton()->parse_input_event(mm);
}
- (void)rightMouseDown:(NSEvent *)event {
[self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
}
- (void)rightMouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
- (void)rightMouseUp:(NSEvent *)event {
[self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
}
- (void)otherMouseDown:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
[self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:true];
} else if ((int)[event buttonNumber] == 3) {
[self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:true];
} else if ((int)[event buttonNumber] == 4) {
[self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:true];
} else {
return;
}
}
- (void)otherMouseDragged:(NSEvent *)event {
[self mouseMoved:event];
}
- (void)otherMouseUp:(NSEvent *)event {
if ((int)[event buttonNumber] == 2) {
[self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:false];
} else if ((int)[event buttonNumber] == 3) {
[self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:false];
} else if ((int)[event buttonNumber] == 4) {
[self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:false];
} else {
return;
}
}
- (void)mouseExited:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT);
}
}
- (void)mouseEntered:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER);
}
ds->cursor_update_shape();
}
- (void)magnifyWithEvent:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
Ref<InputEventMagnifyGesture> ev;
ev.instantiate();
ev->set_window_id(window_id);
ds->get_key_modifier_state([event modifierFlags], ev);
ds->update_mouse_pos(wd, [event locationInWindow]);
ev->set_position(wd.mouse_pos);
ev->set_factor([event magnification] + 1.0);
Input::get_singleton()->parse_input_event(ev);
}
- (void)updateTrackingAreas {
if (tracking_area != nil) {
[self removeTrackingArea:tracking_area];
}
NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate | NSTrackingInVisibleRect;
tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
[self addTrackingArea:tracking_area];
[super updateTrackingAreas];
}
// MARK: Keyboard
- (void)keyDown:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
ignore_momentum_scroll = true;
// Ignore all input if IME input is in progress.
if (!ime_input_event_in_progress) {
NSString *characters = [event characters];
NSUInteger length = [characters length];
if (!wd.im_active && length > 0 && keycode_has_unicode(KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]))) {
// Fallback unicode character handler used if IME is not active.
Char16String text;
text.resize([characters length] + 1);
[characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
String u32text;
u32text.parse_utf16(text.ptr(), text.length());
for (int i = 0; i < u32text.length(); i++) {
const char32_t codepoint = u32text[i];
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = [event isARepeat];
ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
ke.raw = true;
ke.unicode = codepoint;
ds->push_to_key_event_buffer(ke);
}
} else {
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = [event isARepeat];
ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
ke.raw = false;
ke.unicode = 0;
ds->push_to_key_event_buffer(ke);
}
}
// Pass events to IME handler
if (wd.im_active) {
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
}
}
- (void)flagsChanged:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
ignore_momentum_scroll = true;
// Ignore all input if IME input is in progress
if (!ime_input_event_in_progress) {
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
ke.echo = false;
ke.raw = true;
int key = [event keyCode];
int mod = [event modifierFlags];
if (key == 0x36 || key == 0x37) {
if (mod & NSEventModifierFlagCommand) {
mod &= ~NSEventModifierFlagCommand;
ke.pressed = true;
} else {
ke.pressed = false;
}
} else if (key == 0x38 || key == 0x3c) {
if (mod & NSEventModifierFlagShift) {
mod &= ~NSEventModifierFlagShift;
ke.pressed = true;
} else {
ke.pressed = false;
}
} else if (key == 0x3a || key == 0x3d) {
if (mod & NSEventModifierFlagOption) {
mod &= ~NSEventModifierFlagOption;
ke.pressed = true;
} else {
ke.pressed = false;
}
} else if (key == 0x3b || key == 0x3e) {
if (mod & NSEventModifierFlagControl) {
mod &= ~NSEventModifierFlagControl;
ke.pressed = true;
} else {
ke.pressed = false;
}
} else {
return;
}
ke.osx_state = mod;
ke.keycode = KeyMappingOSX::remap_key(key, mod);
ke.physical_keycode = KeyMappingOSX::translate_key(key);
ke.unicode = 0;
ds->push_to_key_event_buffer(ke);
}
}
- (void)keyUp:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
// Ignore all input if IME input is in progress.
if (!ime_input_event_in_progress) {
NSString *characters = [event characters];
NSUInteger length = [characters length];
// Fallback unicode character handler used if IME is not active.
if (!wd.im_active && length > 0 && keycode_has_unicode(KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]))) {
Char16String text;
text.resize([characters length] + 1);
[characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
String u32text;
u32text.parse_utf16(text.ptr(), text.length());
for (int i = 0; i < u32text.length(); i++) {
const char32_t codepoint = u32text[i];
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
ke.osx_state = [event modifierFlags];
ke.pressed = false;
ke.echo = [event isARepeat];
ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
ke.raw = true;
ke.unicode = codepoint;
ds->push_to_key_event_buffer(ke);
}
} else {
DisplayServerOSX::KeyEvent ke;
ke.window_id = window_id;
ke.osx_state = [event modifierFlags];
ke.pressed = false;
ke.echo = [event isARepeat];
ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
ke.raw = true;
ke.unicode = 0;
ds->push_to_key_event_buffer(ke);
}
}
}
// MARK: Scroll and pan
- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
MouseButton mask = mouse_button_to_mask(button);
Ref<InputEventMouseButton> sc;
sc.instantiate();
sc->set_window_id(window_id);
ds->get_key_modifier_state([event modifierFlags], sc);
sc->set_button_index(button);
sc->set_factor(factor);
sc->set_pressed(true);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
MouseButton last_button_state = ds->mouse_get_button_state() | (MouseButton)mask;
sc->set_button_mask(last_button_state);
ds->mouse_set_button_state(last_button_state);
Input::get_singleton()->parse_input_event(sc);
sc.instantiate();
sc->set_window_id(window_id);
sc->set_button_index(button);
sc->set_factor(factor);
sc->set_pressed(false);
sc->set_position(wd.mouse_pos);
sc->set_global_position(wd.mouse_pos);
last_button_state &= (MouseButton)~mask;
sc->set_button_mask(last_button_state);
ds->mouse_set_button_state(last_button_state);
Input::get_singleton()->parse_input_event(sc);
}
- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
Ref<InputEventPanGesture> pg;
pg.instantiate();
pg->set_window_id(window_id);
ds->get_key_modifier_state([event modifierFlags], pg);
pg->set_position(wd.mouse_pos);
pg->set_delta(Vector2(-dx, -dy));
Input::get_singleton()->parse_input_event(pg);
}
- (void)scrollWheel:(NSEvent *)event {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
ds->update_mouse_pos(wd, [event locationInWindow]);
double delta_x = [event scrollingDeltaX];
double delta_y = [event scrollingDeltaY];
if ([event hasPreciseScrollingDeltas]) {
delta_x *= 0.03;
delta_y *= 0.03;
}
if ([event momentumPhase] != NSEventPhaseNone) {
if (ignore_momentum_scroll) {
return;
}
} else {
ignore_momentum_scroll = false;
}
if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
[self processPanEvent:event dx:delta_x dy:delta_y];
} else {
if (fabs(delta_x)) {
[self processScrollEvent:event button:(0 > delta_x ? MouseButton::WHEEL_RIGHT : MouseButton::WHEEL_LEFT) factor:fabs(delta_x * 0.3)];
}
if (fabs(delta_y)) {
[self processScrollEvent:event button:(0 < delta_y ? MouseButton::WHEEL_UP : MouseButton::WHEEL_DOWN) factor:fabs(delta_y * 0.3)];
}
}
}
@end

View File

@ -37,7 +37,7 @@
int main(int argc, char **argv) {
#if defined(VULKAN_ENABLED)
// MoltenVK - enable full component swizzling support
// MoltenVK - enable full component swizzling support.
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
@ -45,13 +45,14 @@ int main(int argc, char **argv) {
const char *dbg_arg = "-NSDocumentRevisionsDebugMode";
printf("arguments\n");
for (int i = 0; i < argc; i++) {
if (strcmp(dbg_arg, argv[i]) == 0)
if (strcmp(dbg_arg, argv[i]) == 0) {
first_arg = i + 2;
}
printf("%i: %s\n", i, argv[i]);
};
#ifdef DEBUG_ENABLED
// lets report the path we made current after all that
// Lets report the path we made current after all that.
char cwd[4096];
getcwd(cwd, 4096);
printf("Current path: %s\n", cwd);
@ -60,23 +61,25 @@ int main(int argc, char **argv) {
OS_OSX os;
Error err;
// We must override main when testing is enabled
// We must override main when testing is enabled.
TEST_MAIN_OVERRIDE
if (os.open_with_filename != "") {
char *argv_c = (char *)malloc(os.open_with_filename.utf8().size());
memcpy(argv_c, os.open_with_filename.utf8().get_data(), os.open_with_filename.utf8().size());
if (os.get_open_with_filename() != "") {
char *argv_c = (char *)malloc(os.get_open_with_filename().utf8().size());
memcpy(argv_c, os.get_open_with_filename().utf8().get_data(), os.get_open_with_filename().utf8().size());
err = Main::setup(argv[0], 1, &argv_c);
free(argv_c);
} else {
err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]);
}
if (err != OK)
if (err != OK) {
return 255;
}
if (Main::start())
os.run(); // it is actually the OS that decides how to run
if (Main::start()) {
os.run(); // It is actually the OS that decides how to run.
}
Main::cleanup();

View File

@ -0,0 +1,52 @@
/*************************************************************************/
/* godot_menu_item.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOT_MENU_ITEM_H
#define GODOT_MENU_ITEM_H
#include "servers/display_server.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@interface GodotMenuItem : NSObject {
@public
Callable callback;
Variant meta;
int id;
bool checkable;
}
@end
@implementation GodotMenuItem
@end
#endif // GODOT_MENU_ITEM_H

View File

@ -0,0 +1,47 @@
/*************************************************************************/
/* godot_window.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOT_WINDOW_H
#define GODOT_WINDOW_H
#include "servers/display_server.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@interface GodotWindow : NSWindow {
DisplayServer::WindowID window_id;
}
- (void)setWindowID:(DisplayServer::WindowID)wid;
@end
#endif //GODOT_WINDOW_H

View File

@ -0,0 +1,69 @@
/*************************************************************************/
/* godot_window.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godot_window.h"
#include "display_server_osx.h"
@implementation GodotWindow
- (id)init {
self = [super init];
window_id = DisplayServer::INVALID_WINDOW_ID;
return self;
}
- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
window_id = wid;
}
- (BOOL)canBecomeKeyWindow {
// Required for NSBorderlessWindowMask windows.
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return YES;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
return !wd.no_focus;
}
- (BOOL)canBecomeMainWindow {
// Required for NSBorderlessWindowMask windows.
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return YES;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
return !wd.no_focus;
}
@end

View File

@ -0,0 +1,47 @@
/*************************************************************************/
/* godot_window_delegate.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef GODOT_WINDOW_DELEGATE_H
#define GODOT_WINDOW_DELEGATE_H
#include "servers/display_server.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
@interface GodotWindowDelegate : NSObject <NSWindowDelegate> {
DisplayServer::WindowID window_id;
}
- (void)setWindowID:(DisplayServer::WindowID)wid;
@end
#endif //GODOT_WINDOW_DELEGATE_H

View File

@ -0,0 +1,254 @@
/*************************************************************************/
/* godot_window_delegate.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "godot_window_delegate.h"
#include "display_server_osx.h"
@implementation GodotWindowDelegate
- (void)setWindowID:(DisplayServer::WindowID)wid {
window_id = wid;
}
- (BOOL)windowShouldClose:(id)sender {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return YES;
}
ds->send_window_event(ds->get_window(window_id), DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
return NO;
}
- (void)windowWillClose:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
while (wd.transient_children.size()) {
ds->window_set_transient(wd.transient_children.front()->get(), DisplayServerOSX::INVALID_WINDOW_ID);
}
if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) {
ds->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID);
}
ds->window_destroy(window_id);
}
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
wd.fullscreen = true;
// Reset window size limits.
[wd.window_object setContentMinSize:NSMakeSize(0, 0)];
[wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
// Force window resize event.
[self windowDidResize:notification];
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
wd.fullscreen = false;
// Set window size limits.
const float scale = ds->screen_get_max_scale();
if (wd.min_size != Size2i()) {
Size2i size = wd.min_size / scale;
[wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
}
if (wd.max_size != Size2i()) {
Size2i size = wd.max_size / scale;
[wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
}
// Restore resizability state.
if (wd.resize_disabled) {
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
}
// Restore on-top state.
if (wd.on_top) {
[wd.window_object setLevel:NSFloatingWindowLevel];
}
// Force window resize event.
[self windowDidResize:notification];
}
- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
CGFloat new_scale_factor = [wd.window_object backingScaleFactor];
CGFloat old_scale_factor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
if (new_scale_factor != old_scale_factor) {
// Set new display scale and window size.
const float scale = ds->screen_get_max_scale();
const NSRect content_rect = [wd.window_view frame];
wd.size.width = content_rect.size.width * scale;
wd.size.height = content_rect.size.height * scale;
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE);
CALayer *layer = [wd.window_view layer];
if (layer) {
layer.contentsScale = scale;
}
//Force window resize event
[self windowDidResize:notification];
}
}
- (void)windowDidResize:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
const NSRect content_rect = [wd.window_view frame];
const float scale = ds->screen_get_max_scale();
wd.size.width = content_rect.size.width * scale;
wd.size.height = content_rect.size.height * scale;
CALayer *layer = [wd.window_view layer];
if (layer) {
layer.contentsScale = scale;
}
ds->window_resize(window_id, wd.size.width, wd.size.height);
if (!wd.rect_changed_callback.is_null()) {
Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id));
Variant *sizep = &size;
Variant ret;
Callable::CallError ce;
wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
}
}
- (void)windowDidMove:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
ds->release_pressed_events();
if (!wd.rect_changed_callback.is_null()) {
Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id));
Variant *sizep = &size;
Variant ret;
Callable::CallError ce;
wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
}
}
- (void)windowDidBecomeKey:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
if (ds->mouse_get_mode() == DisplayServer::MOUSE_MODE_CAPTURED) {
const NSRect content_rect = [wd.window_view frame];
NSRect point_in_window_rect = NSMakeRect(content_rect.size.width / 2, content_rect.size.height / 2, 0, 0);
NSPoint point_on_screen = [[wd.window_view window] convertRectToScreen:point_in_window_rect].origin;
CGPoint mouse_warp_pos = { point_on_screen.x, CGDisplayBounds(CGMainDisplayID()).size.height - point_on_screen.y };
CGWarpMouseCursorPosition(mouse_warp_pos);
} else {
ds->update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
}
ds->set_last_focused_window(window_id);
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
}
- (void)windowDidResignKey:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
ds->release_pressed_events();
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
}
- (void)windowDidMiniaturize:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
ds->release_pressed_events();
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
}
- (void)windowDidDeminiaturize:(NSNotification *)notification {
DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
if (!ds || !ds->has_window(window_id)) {
return;
}
DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
ds->set_last_focused_window(window_id);
ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
}
@end

View File

@ -80,7 +80,7 @@ int joypad::get_hid_element_state(rec_element *p_element) const {
if (IOHIDDeviceGetValue(device_ref, p_element->ref, &valueRef) == kIOReturnSuccess) {
value = (SInt32)IOHIDValueGetIntegerValue(valueRef);
/* record min and max for auto calibration */
// Record min and max for auto calibration.
if (value < p_element->min) {
p_element->min = value;
}
@ -179,7 +179,7 @@ void joypad::add_hid_element(IOHIDElementRef p_element) {
break;
}
if (list) { /* add to list */
if (list) { // Add to list.
rec_element element;
element.ref = p_element;
@ -280,7 +280,7 @@ static String _hex_str(uint8_t p_byte) {
bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
p_joy->device_ref = p_device_ref;
/* get device name */
// Get device name.
String name;
char c_name[256];
CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductKey));
@ -319,7 +319,7 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version));
input->joy_connection_changed(id, true, name, uid);
} else {
//bluetooth device
// Bluetooth device.
String guid = "05000000";
for (int i = 0; i < 12; i++) {
if (i < name.size())
@ -445,7 +445,7 @@ static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offse
void JoypadOSX::poll_joypads() const {
while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
/* no-op. Pending callbacks will fire. */
// No-op. Pending callbacks will fire.
}
}
@ -568,7 +568,7 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
IOHIDManagerScheduleWithRunLoop(hid_manager, runloop, GODOT_JOY_LOOP_RUN_MODE);
while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
/* no-op. Callback fires once per existing device. */
// No-op. Callback fires once per existing device.
}
}

View File

@ -66,7 +66,7 @@ struct joypad {
int id = 0;
bool offset_hat = false;
io_service_t ffservice = 0; /* Interface for force feedback, 0 = no ff */
io_service_t ffservice = 0; // Interface for force feedback, 0 = no ff.
FFCONSTANTFORCE ff_constant_force;
FFDeviceObjectReference ff_device = nullptr;
FFEffectObjectReference ff_object = nullptr;

View File

@ -0,0 +1,52 @@
/*************************************************************************/
/* key_mapping_osx.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef KEY_MAPPING_OSX_H
#define KEY_MAPPING_OSX_H
#include "core/os/keyboard.h"
class KeyMappingOSX {
KeyMappingOSX() {}
static bool is_numpad_key(unsigned int key);
public:
// Mappings input.
static Key translate_key(unsigned int key);
static unsigned int unmap_key(Key key);
static Key remap_key(unsigned int key, unsigned int state);
// Mapping for menu shortcuts.
static String keycode_get_native_string(Key p_keycode);
static unsigned int keycode_get_native_mask(Key p_keycode);
};
#endif // KEY_MAPPING_OSX_H

View File

@ -0,0 +1,477 @@
/*************************************************************************/
/* key_mapping_osx.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "key_mapping_osx.h"
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>
bool KeyMappingOSX::is_numpad_key(unsigned int key) {
static const unsigned int table[] = {
0x41, /* kVK_ANSI_KeypadDecimal */
0x43, /* kVK_ANSI_KeypadMultiply */
0x45, /* kVK_ANSI_KeypadPlus */
0x47, /* kVK_ANSI_KeypadClear */
0x4b, /* kVK_ANSI_KeypadDivide */
0x4c, /* kVK_ANSI_KeypadEnter */
0x4e, /* kVK_ANSI_KeypadMinus */
0x51, /* kVK_ANSI_KeypadEquals */
0x52, /* kVK_ANSI_Keypad0 */
0x53, /* kVK_ANSI_Keypad1 */
0x54, /* kVK_ANSI_Keypad2 */
0x55, /* kVK_ANSI_Keypad3 */
0x56, /* kVK_ANSI_Keypad4 */
0x57, /* kVK_ANSI_Keypad5 */
0x58, /* kVK_ANSI_Keypad6 */
0x59, /* kVK_ANSI_Keypad7 */
0x5b, /* kVK_ANSI_Keypad8 */
0x5c, /* kVK_ANSI_Keypad9 */
0x5f, /* kVK_JIS_KeypadComma */
0x00
};
for (int i = 0; table[i] != 0; i++) {
if (key == table[i]) {
return true;
}
}
return false;
}
// Keyboard symbol translation table.
static const Key _osx_to_godot_table[128] = {
/* 00 */ Key::A,
/* 01 */ Key::S,
/* 02 */ Key::D,
/* 03 */ Key::F,
/* 04 */ Key::H,
/* 05 */ Key::G,
/* 06 */ Key::Z,
/* 07 */ Key::X,
/* 08 */ Key::C,
/* 09 */ Key::V,
/* 0a */ Key::SECTION, /* ISO Section */
/* 0b */ Key::B,
/* 0c */ Key::Q,
/* 0d */ Key::W,
/* 0e */ Key::E,
/* 0f */ Key::R,
/* 10 */ Key::Y,
/* 11 */ Key::T,
/* 12 */ Key::KEY_1,
/* 13 */ Key::KEY_2,
/* 14 */ Key::KEY_3,
/* 15 */ Key::KEY_4,
/* 16 */ Key::KEY_6,
/* 17 */ Key::KEY_5,
/* 18 */ Key::EQUAL,
/* 19 */ Key::KEY_9,
/* 1a */ Key::KEY_7,
/* 1b */ Key::MINUS,
/* 1c */ Key::KEY_8,
/* 1d */ Key::KEY_0,
/* 1e */ Key::BRACERIGHT,
/* 1f */ Key::O,
/* 20 */ Key::U,
/* 21 */ Key::BRACELEFT,
/* 22 */ Key::I,
/* 23 */ Key::P,
/* 24 */ Key::ENTER,
/* 25 */ Key::L,
/* 26 */ Key::J,
/* 27 */ Key::APOSTROPHE,
/* 28 */ Key::K,
/* 29 */ Key::SEMICOLON,
/* 2a */ Key::BACKSLASH,
/* 2b */ Key::COMMA,
/* 2c */ Key::SLASH,
/* 2d */ Key::N,
/* 2e */ Key::M,
/* 2f */ Key::PERIOD,
/* 30 */ Key::TAB,
/* 31 */ Key::SPACE,
/* 32 */ Key::QUOTELEFT,
/* 33 */ Key::BACKSPACE,
/* 34 */ Key::UNKNOWN,
/* 35 */ Key::ESCAPE,
/* 36 */ Key::META,
/* 37 */ Key::META,
/* 38 */ Key::SHIFT,
/* 39 */ Key::CAPSLOCK,
/* 3a */ Key::ALT,
/* 3b */ Key::CTRL,
/* 3c */ Key::SHIFT,
/* 3d */ Key::ALT,
/* 3e */ Key::CTRL,
/* 3f */ Key::UNKNOWN, /* Function */
/* 40 */ Key::UNKNOWN, /* F17 */
/* 41 */ Key::KP_PERIOD,
/* 42 */ Key::UNKNOWN,
/* 43 */ Key::KP_MULTIPLY,
/* 44 */ Key::UNKNOWN,
/* 45 */ Key::KP_ADD,
/* 46 */ Key::UNKNOWN,
/* 47 */ Key::NUMLOCK, /* Really KeypadClear... */
/* 48 */ Key::VOLUMEUP, /* VolumeUp */
/* 49 */ Key::VOLUMEDOWN, /* VolumeDown */
/* 4a */ Key::VOLUMEMUTE, /* Mute */
/* 4b */ Key::KP_DIVIDE,
/* 4c */ Key::KP_ENTER,
/* 4d */ Key::UNKNOWN,
/* 4e */ Key::KP_SUBTRACT,
/* 4f */ Key::UNKNOWN, /* F18 */
/* 50 */ Key::UNKNOWN, /* F19 */
/* 51 */ Key::EQUAL, /* KeypadEqual */
/* 52 */ Key::KP_0,
/* 53 */ Key::KP_1,
/* 54 */ Key::KP_2,
/* 55 */ Key::KP_3,
/* 56 */ Key::KP_4,
/* 57 */ Key::KP_5,
/* 58 */ Key::KP_6,
/* 59 */ Key::KP_7,
/* 5a */ Key::UNKNOWN, /* F20 */
/* 5b */ Key::KP_8,
/* 5c */ Key::KP_9,
/* 5d */ Key::YEN, /* JIS Yen */
/* 5e */ Key::UNDERSCORE, /* JIS Underscore */
/* 5f */ Key::COMMA, /* JIS KeypadComma */
/* 60 */ Key::F5,
/* 61 */ Key::F6,
/* 62 */ Key::F7,
/* 63 */ Key::F3,
/* 64 */ Key::F8,
/* 65 */ Key::F9,
/* 66 */ Key::UNKNOWN, /* JIS Eisu */
/* 67 */ Key::F11,
/* 68 */ Key::UNKNOWN, /* JIS Kana */
/* 69 */ Key::F13,
/* 6a */ Key::F16,
/* 6b */ Key::F14,
/* 6c */ Key::UNKNOWN,
/* 6d */ Key::F10,
/* 6e */ Key::MENU,
/* 6f */ Key::F12,
/* 70 */ Key::UNKNOWN,
/* 71 */ Key::F15,
/* 72 */ Key::INSERT, /* Really Help... */
/* 73 */ Key::HOME,
/* 74 */ Key::PAGEUP,
/* 75 */ Key::KEY_DELETE,
/* 76 */ Key::F4,
/* 77 */ Key::END,
/* 78 */ Key::F2,
/* 79 */ Key::PAGEDOWN,
/* 7a */ Key::F1,
/* 7b */ Key::LEFT,
/* 7c */ Key::RIGHT,
/* 7d */ Key::DOWN,
/* 7e */ Key::UP,
/* 7f */ Key::UNKNOWN,
};
// Translates a OS X keycode to a Godot keycode.
Key KeyMappingOSX::translate_key(unsigned int key) {
if (key >= 128) {
return Key::UNKNOWN;
}
return _osx_to_godot_table[key];
}
// Translates a Godot keycode back to a OSX keycode.
unsigned int KeyMappingOSX::unmap_key(Key key) {
for (int i = 0; i <= 126; i++) {
if (_osx_to_godot_table[i] == key) {
return i;
}
}
return 127;
}
struct _KeyCodeMap {
UniChar kchar;
Key kcode;
};
static const _KeyCodeMap _keycodes[55] = {
{ '`', Key::QUOTELEFT },
{ '~', Key::ASCIITILDE },
{ '0', Key::KEY_0 },
{ '1', Key::KEY_1 },
{ '2', Key::KEY_2 },
{ '3', Key::KEY_3 },
{ '4', Key::KEY_4 },
{ '5', Key::KEY_5 },
{ '6', Key::KEY_6 },
{ '7', Key::KEY_7 },
{ '8', Key::KEY_8 },
{ '9', Key::KEY_9 },
{ '-', Key::MINUS },
{ '_', Key::UNDERSCORE },
{ '=', Key::EQUAL },
{ '+', Key::PLUS },
{ 'q', Key::Q },
{ 'w', Key::W },
{ 'e', Key::E },
{ 'r', Key::R },
{ 't', Key::T },
{ 'y', Key::Y },
{ 'u', Key::U },
{ 'i', Key::I },
{ 'o', Key::O },
{ 'p', Key::P },
{ '[', Key::BRACELEFT },
{ ']', Key::BRACERIGHT },
{ '{', Key::BRACELEFT },
{ '}', Key::BRACERIGHT },
{ 'a', Key::A },
{ 's', Key::S },
{ 'd', Key::D },
{ 'f', Key::F },
{ 'g', Key::G },
{ 'h', Key::H },
{ 'j', Key::J },
{ 'k', Key::K },
{ 'l', Key::L },
{ ';', Key::SEMICOLON },
{ ':', Key::COLON },
{ '\'', Key::APOSTROPHE },
{ '\"', Key::QUOTEDBL },
{ '\\', Key::BACKSLASH },
{ '#', Key::NUMBERSIGN },
{ 'z', Key::Z },
{ 'x', Key::X },
{ 'c', Key::C },
{ 'v', Key::V },
{ 'b', Key::B },
{ 'n', Key::N },
{ 'm', Key::M },
{ ',', Key::COMMA },
{ '.', Key::PERIOD },
{ '/', Key::SLASH }
};
// Remap key according to current keyboard layout.
Key KeyMappingOSX::remap_key(unsigned int key, unsigned int state) {
if (is_numpad_key(key)) {
return translate_key(key);
}
TISInputSourceRef current_keyboard = TISCopyCurrentKeyboardInputSource();
if (!current_keyboard) {
return translate_key(key);
}
CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData);
if (!layout_data) {
return translate_key(key);
}
const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data);
UInt32 keys_down = 0;
UniChar chars[4];
UniCharCount real_length;
OSStatus err = UCKeyTranslate(keyboard_layout,
key,
kUCKeyActionDisplay,
(state >> 8) & 0xFF,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&keys_down,
sizeof(chars) / sizeof(chars[0]),
&real_length,
chars);
if (err != noErr) {
return translate_key(key);
}
for (unsigned int i = 0; i < 55; i++) {
if (_keycodes[i].kchar == chars[0]) {
return _keycodes[i].kcode;
}
}
return translate_key(key);
}
struct _KeyCodeText {
Key code;
char32_t text;
};
static const _KeyCodeText _native_keycodes[] = {
/* clang-format off */
{Key::ESCAPE ,0x001B},
{Key::TAB ,0x0009},
{Key::BACKTAB ,0x007F},
{Key::BACKSPACE ,0x0008},
{Key::ENTER ,0x000D},
{Key::INSERT ,NSInsertFunctionKey},
{Key::KEY_DELETE ,0x007F},
{Key::PAUSE ,NSPauseFunctionKey},
{Key::PRINT ,NSPrintScreenFunctionKey},
{Key::SYSREQ ,NSSysReqFunctionKey},
{Key::CLEAR ,NSClearLineFunctionKey},
{Key::HOME ,0x2196},
{Key::END ,0x2198},
{Key::LEFT ,0x001C},
{Key::UP ,0x001E},
{Key::RIGHT ,0x001D},
{Key::DOWN ,0x001F},
{Key::PAGEUP ,0x21DE},
{Key::PAGEDOWN ,0x21DF},
{Key::NUMLOCK ,NSClearLineFunctionKey},
{Key::SCROLLLOCK ,NSScrollLockFunctionKey},
{Key::F1 ,NSF1FunctionKey},
{Key::F2 ,NSF2FunctionKey},
{Key::F3 ,NSF3FunctionKey},
{Key::F4 ,NSF4FunctionKey},
{Key::F5 ,NSF5FunctionKey},
{Key::F6 ,NSF6FunctionKey},
{Key::F7 ,NSF7FunctionKey},
{Key::F8 ,NSF8FunctionKey},
{Key::F9 ,NSF9FunctionKey},
{Key::F10 ,NSF10FunctionKey},
{Key::F11 ,NSF11FunctionKey},
{Key::F12 ,NSF12FunctionKey},
{Key::F13 ,NSF13FunctionKey},
{Key::F14 ,NSF14FunctionKey},
{Key::F15 ,NSF15FunctionKey},
{Key::F16 ,NSF16FunctionKey}, //* ... NSF35FunctionKey */
{Key::MENU ,NSMenuFunctionKey},
{Key::HELP ,NSHelpFunctionKey},
{Key::STOP ,NSStopFunctionKey},
{Key::LAUNCH0 ,NSUserFunctionKey},
{Key::SPACE ,0x0020},
{Key::EXCLAM ,'!'},
{Key::QUOTEDBL ,'\"'},
{Key::NUMBERSIGN ,'#'},
{Key::DOLLAR ,'$'},
{Key::PERCENT ,'\%'},
{Key::AMPERSAND ,'&'},
{Key::APOSTROPHE ,'\''},
{Key::PARENLEFT ,'('},
{Key::PARENRIGHT ,')'},
{Key::ASTERISK ,'*'},
{Key::PLUS ,'+'},
{Key::COMMA ,','},
{Key::MINUS ,'-'},
{Key::PERIOD ,'.'},
{Key::SLASH ,'/'},
{Key::KEY_0 ,'0'},
{Key::KEY_1 ,'1'},
{Key::KEY_2 ,'2'},
{Key::KEY_3 ,'3'},
{Key::KEY_4 ,'4'},
{Key::KEY_5 ,'5'},
{Key::KEY_6 ,'6'},
{Key::KEY_7 ,'7'},
{Key::KEY_8 ,'8'},
{Key::KEY_9 ,'9'},
{Key::COLON ,':'},
{Key::SEMICOLON ,';'},
{Key::LESS ,'<'},
{Key::EQUAL ,'='},
{Key::GREATER ,'>'},
{Key::QUESTION ,'?'},
{Key::AT ,'@'},
{Key::A ,'a'},
{Key::B ,'b'},
{Key::C ,'c'},
{Key::D ,'d'},
{Key::E ,'e'},
{Key::F ,'f'},
{Key::G ,'g'},
{Key::H ,'h'},
{Key::I ,'i'},
{Key::J ,'j'},
{Key::K ,'k'},
{Key::L ,'l'},
{Key::M ,'m'},
{Key::N ,'n'},
{Key::O ,'o'},
{Key::P ,'p'},
{Key::Q ,'q'},
{Key::R ,'r'},
{Key::S ,'s'},
{Key::T ,'t'},
{Key::U ,'u'},
{Key::V ,'v'},
{Key::W ,'w'},
{Key::X ,'x'},
{Key::Y ,'y'},
{Key::Z ,'z'},
{Key::BRACKETLEFT ,'['},
{Key::BACKSLASH ,'\\'},
{Key::BRACKETRIGHT ,']'},
{Key::ASCIICIRCUM ,'^'},
{Key::UNDERSCORE ,'_'},
{Key::QUOTELEFT ,'`'},
{Key::BRACELEFT ,'{'},
{Key::BAR ,'|'},
{Key::BRACERIGHT ,'}'},
{Key::ASCIITILDE ,'~'},
{Key::NONE ,0x0000}
/* clang-format on */
};
String KeyMappingOSX::keycode_get_native_string(Key p_keycode) {
const _KeyCodeText *kct = &_native_keycodes[0];
while (kct->text) {
if (kct->code == p_keycode) {
return String::chr(kct->text);
}
kct++;
}
return String();
}
unsigned int KeyMappingOSX::keycode_get_native_mask(Key p_keycode) {
unsigned int mask = 0;
if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) {
mask |= NSEventModifierFlagControl;
}
if ((p_keycode & KeyModifierMask::ALT) != Key::NONE) {
mask |= NSEventModifierFlagOption;
}
if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) {
mask |= NSEventModifierFlagShift;
}
if ((p_keycode & KeyModifierMask::META) != Key::NONE) {
mask |= NSEventModifierFlagCommand;
}
if ((p_keycode & KeyModifierMask::KPAD) != Key::NONE) {
mask |= NSEventModifierFlagNumericPad;
}
return mask;
}

View File

@ -40,9 +40,7 @@
#include "servers/audio_server.h"
class OS_OSX : public OS_Unix {
virtual void delete_main_loop() override;
bool force_quit;
bool force_quit = false;
JoypadOSX *joypad_osx = nullptr;
@ -55,13 +53,15 @@ class OS_OSX : public OS_Unix {
CrashHandler crash_handler;
MainLoop *main_loop;
CFRunLoopObserverRef pre_wait_observer;
static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context);
MainLoop *main_loop = nullptr;
public:
String open_with_filename;
static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context);
protected:
virtual void initialize_core() override;
virtual void initialize() override;
@ -70,8 +70,12 @@ protected:
virtual void initialize_joypads() override;
virtual void set_main_loop(MainLoop *p_main_loop) override;
virtual void delete_main_loop() override;
public:
String get_open_with_filename() const;
void set_open_with_filename(const String &p_path);
virtual String get_name() const override;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
@ -89,26 +93,27 @@ public:
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
Error shell_open(String p_uri) override;
virtual Error shell_open(String p_uri) override;
String get_locale() const override;
virtual String get_locale() const override;
virtual String get_executable_path() const override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
virtual String get_unique_id() const override; //++
virtual String get_unique_id() const override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
void run();
virtual void disable_crash_handler() override;
virtual bool is_disable_crash_handler() const override;
virtual Error move_to_trash(const String &p_path) override;
void run();
OS_OSX();
~OS_OSX();
};
#endif

View File

@ -31,223 +31,94 @@
#include "os_osx.h"
#include "core/version_generated.gen.h"
#include "main/main.h"
#include "dir_access_osx.h"
#include "display_server_osx.h"
#include "main/main.h"
#include "godot_application.h"
#include "godot_application_delegate.h"
#include "osx_terminal_logger.h"
#include <dlfcn.h>
#include <libproc.h>
#include <mach-o/dyld.h>
#include <os/log.h>
#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
/*************************************************************************/
/* GodotApplication */
/*************************************************************************/
@interface GodotApplication : NSApplication
@end
@implementation GodotApplication
- (void)sendEvent:(NSEvent *)event {
if (DS_OSX) {
DS_OSX->_send_event(event);
}
// 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];
_FORCE_INLINE_ String OS_OSX::get_framework_executable(const String &p_path) {
// Append framework executable name, or return as is if p_path is not a framework.
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
return p_path.plus_file(p_path.get_file().get_basename());
} else {
[super sendEvent:event];
return p_path;
}
}
@end
void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) {
// Prevent main loop from sleeping and redraw window during resize / modal popups.
/*************************************************************************/
/* GodotApplicationDelegate */
/*************************************************************************/
@interface GodotApplicationDelegate : NSObject
- (void)forceUnbundledWindowActivationHackStep1;
- (void)forceUnbundledWindowActivationHackStep2;
- (void)forceUnbundledWindowActivationHackStep3;
@end
@implementation GodotApplicationDelegate
- (void)forceUnbundledWindowActivationHackStep1 {
// Step 1: Switch focus to macOS SystemUIServer process.
// Required to perform step 2, TransformProcessType will fail if app is already the in focus.
for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.systemuiserver"]) {
[app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
break;
if (get_singleton()->get_main_loop()) {
Main::force_redraw();
if (!Main::is_iterating()) { // Avoid cyclic loop.
Main::iteration();
}
[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 || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) {
// If the executable is started from terminal or is not 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];
}
CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
}
- (void)applicationDidResignActive:(NSNotification *)notification {
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
}
void OS_OSX::initialize() {
crash_handler.initialize();
initialize_core();
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
}
void OS_OSX::initialize_core() {
OS_Unix::initialize_core();
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
}
- (void)globalMenuCallback:(id)sender {
if (DS_OSX) {
return DS_OSX->_menu_callback(sender);
}
}
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
if (DS_OSX) {
return DS_OSX->_get_dock_menu();
} else {
return nullptr;
}
}
- (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_OSX::get_singleton()->get_executable_path();
OS_OSX::get_singleton()->create_process(exec, args);
}
void OS_OSX::finalize() {
#ifdef COREMIDI_ENABLED
midi_driver.close();
#endif
return YES;
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
if (DS_OSX) {
DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
}
return NSTerminateCancel;
}
delete_main_loop();
- (void)showAbout:(id)sender {
if (OS_OSX::get_singleton()->get_main_loop()) {
OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
if (joypad_osx) {
memdelete(joypad_osx);
}
}
@end
void OS_OSX::initialize_joypads() {
joypad_osx = memnew(JoypadOSX(Input::get_singleton()));
}
/*************************************************************************/
/* OSXTerminalLogger */
/*************************************************************************/
void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
}
class OSXTerminalLogger : public StdLogger {
public:
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) {
if (!should_log(true)) {
void OS_OSX::delete_main_loop() {
if (!main_loop) {
return;
}
const char *err_details;
if (p_rationale && p_rationale[0])
err_details = p_rationale;
else
err_details = p_code;
memdelete(main_loop);
main_loop = nullptr;
}
switch (p_type) {
case ERR_WARNING:
os_log_info(OS_LOG_DEFAULT,
"WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_SCRIPT:
os_log_error(OS_LOG_DEFAULT,
"SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_SHADER:
os_log_error(OS_LOG_DEFAULT,
"SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_ERROR:
default:
os_log_error(OS_LOG_DEFAULT,
"ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
}
}
};
String OS_OSX::get_open_with_filename() const {
return open_with_filename;
}
/*************************************************************************/
/* OS_OSX */
/*************************************************************************/
void OS_OSX::set_open_with_filename(const String &p_path) {
open_with_filename = p_path;
}
String OS_OSX::get_unique_id() const {
static String serial_number;
if (serial_number.is_empty()) {
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
CFStringRef serialNumberAsCFString = nullptr;
if (platformExpert) {
serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
IOObjectRelease(platformExpert);
}
NSString *serialNumberAsNSString = nil;
if (serialNumberAsCFString) {
serialNumberAsNSString = [NSString stringWithString:(__bridge NSString *)serialNumberAsCFString];
CFRelease(serialNumberAsCFString);
}
serial_number = [serialNumberAsNSString UTF8String];
}
return serial_number;
String OS_OSX::get_name() const {
return "macOS";
}
void OS_OSX::alert(const String &p_alert, const String &p_title) {
@ -267,73 +138,17 @@ void OS_OSX::alert(const String &p_alert, const String &p_title) {
}
}
void OS_OSX::initialize_core() {
OS_Unix::initialize_core();
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
}
void OS_OSX::initialize_joypads() {
joypad_osx = memnew(JoypadOSX(Input::get_singleton()));
}
void OS_OSX::initialize() {
crash_handler.initialize();
initialize_core();
//ensure_user_data_dir();
}
void OS_OSX::finalize() {
#ifdef COREMIDI_ENABLED
midi_driver.close();
#endif
delete_main_loop();
if (joypad_osx) {
memdelete(joypad_osx);
}
}
void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
}
void OS_OSX::delete_main_loop() {
if (!main_loop)
return;
memdelete(main_loop);
main_loop = nullptr;
}
String OS_OSX::get_name() const {
return "macOS";
}
_FORCE_INLINE_ String _get_framework_executable(const String p_path) {
// Append framework executable name, or return as is if p_path is not a framework.
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
return p_path.plus_file(p_path.get_file().get_basename());
} else {
return p_path;
}
}
Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
String path = _get_framework_executable(p_path);
String path = get_framework_executable(p_path);
if (!FileAccess::exists(path)) {
// This code exists so gdnative can load .dylib files from within the executable path.
path = _get_framework_executable(get_executable_path().get_base_dir().plus_file(p_path.get_file()));
// Load .dylib or framework from within the executable path.
path = get_framework_executable(get_executable_path().get_base_dir().plus_file(p_path.get_file()));
}
if (!FileAccess::exists(path)) {
// This code exists so gdnative can load .dylib files from a standard macOS location.
path = _get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()));
// Load .dylib or framework from a standard macOS location.
path = get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()));
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
@ -392,8 +207,8 @@ String OS_OSX::get_bundle_resource_dir() const {
NSBundle *main = [NSBundle mainBundle];
if (main) {
NSString *resourcePath = [main resourcePath];
ret.parse_utf8([resourcePath UTF8String]);
NSString *resource_path = [main resourcePath];
ret.parse_utf8([resource_path UTF8String]);
}
return ret;
}
@ -403,9 +218,9 @@ String OS_OSX::get_bundle_icon_path() const {
NSBundle *main = [NSBundle mainBundle];
if (main) {
NSString *iconPath = [[main infoDictionary] objectForKey:@"CFBundleIconFile"];
if (iconPath) {
ret.parse_utf8([iconPath UTF8String]);
NSString *icon_path = [[main infoDictionary] objectForKey:@"CFBundleIconFile"];
if (icon_path) {
ret.parse_utf8([icon_path UTF8String]);
}
}
return ret;
@ -448,9 +263,7 @@ String OS_OSX::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
if (found) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES);
if (paths && [paths count] >= 1) {
char *utfs = strdup([[paths firstObject] UTF8String]);
ret.parse_utf8(utfs);
free(utfs);
ret.parse_utf8([[paths firstObject] UTF8String]);
}
}
@ -474,12 +287,9 @@ String OS_OSX::get_locale() const {
}
String OS_OSX::get_executable_path() const {
int ret;
pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
pid = getpid();
ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
int pid = getpid();
pid_t ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
if (ret <= 0) {
return OS::get_executable_path();
} else {
@ -490,18 +300,6 @@ String OS_OSX::get_executable_path() const {
}
}
Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
// If executable is bundled, always execute editor instances as an app bundle to ensure app window is registered and activated correctly.
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
if (nsappname != nil) {
String path;
path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]);
return create_process(path, p_arguments, r_child_id, false);
} else {
return create_process(get_executable_path(), p_arguments, r_child_id, false);
}
}
Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
if (@available(macOS 10.15, *)) {
// Use NSWorkspace if path is an .app bundle.
@ -547,51 +345,53 @@ Error OS_OSX::create_process(const String &p_path, const List<String> &p_argumen
}
}
void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) {
// Prevent main loop from sleeping and redraw window during resize / modal popups.
if (get_singleton()->get_main_loop()) {
Main::force_redraw();
if (!Main::is_iterating()) { // Avoid cyclic loop.
Main::iteration();
Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
// If executable is bundled, always execute editor instances as an app bundle to ensure app window is registered and activated correctly.
NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
if (nsappname != nil) {
String path;
path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]);
return create_process(path, p_arguments, r_child_id, false);
} else {
return create_process(get_executable_path(), p_arguments, r_child_id, false);
}
}
CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
}
void OS_OSX::run() {
force_quit = false;
String OS_OSX::get_unique_id() const {
static String serial_number;
if (!main_loop) {
return;
if (serial_number.is_empty()) {
io_service_t platform_expert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
CFStringRef serial_number_cf_string = nullptr;
if (platform_expert) {
serial_number_cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(platform_expert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
IOObjectRelease(platform_expert);
}
main_loop->initialize();
CFRunLoopObserverRef pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
bool quit = false;
while (!force_quit && !quit) {
@try {
if (DisplayServer::get_singleton()) {
DisplayServer::get_singleton()->process_events(); // get rid of pending events
NSString *serial_number_ns_string = nil;
if (serial_number_cf_string) {
serial_number_ns_string = [NSString stringWithString:(__bridge NSString *)serial_number_cf_string];
CFRelease(serial_number_cf_string);
}
joypad_osx->process_joypads();
if (Main::iteration()) {
quit = true;
if (serial_number_ns_string) {
serial_number.parse_utf8([serial_number_ns_string UTF8String]);
}
} @catch (NSException *exception) {
ERR_PRINT("NSException: " + String::utf8([exception reason].UTF8String));
}
};
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
CFRelease(pre_wait_observer);
return serial_number;
}
main_loop->finalize();
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
return p_feature == "pc";
}
void OS_OSX::disable_crash_handler() {
crash_handler.disable();
}
bool OS_OSX::is_disable_crash_handler() const {
return crash_handler.is_disabled();
}
Error OS_OSX::move_to_trash(const String &p_path) {
@ -607,6 +407,34 @@ Error OS_OSX::move_to_trash(const String &p_path) {
return OK;
}
void OS_OSX::run() {
force_quit = false;
if (!main_loop) {
return;
}
main_loop->initialize();
bool quit = false;
while (!force_quit && !quit) {
@try {
if (DisplayServer::get_singleton()) {
DisplayServer::get_singleton()->process_events(); // Get rid of pending events.
}
joypad_osx->process_joypads();
if (Main::iteration()) {
quit = true;
}
} @catch (NSException *exception) {
ERR_PRINT("NSException: " + String::utf8([exception reason].UTF8String));
}
};
main_loop->finalize();
}
OS_OSX::OS_OSX() {
main_loop = nullptr;
force_quit = false;
@ -621,15 +449,15 @@ OS_OSX::OS_OSX() {
DisplayServerOSX::register_osx_driver();
// Implicitly create shared NSApplication instance
// Implicitly create shared NSApplication instance.
[GodotApplication sharedApplication];
// In case we are unbundled, make us a proper UI application
// In case we are unbundled, make us a proper UI application.
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
// Menu bar setup must go between sharedApplication above and
// finishLaunching below, in order to properly emulate the behavior
// of NSApplicationMain
// of NSApplicationMain.
NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""];
[NSApp setMainMenu:main_menu];
@ -639,7 +467,10 @@ OS_OSX::OS_OSX() {
ERR_FAIL_COND(!delegate);
[NSApp setDelegate:delegate];
//process application:openFile: event
pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
// Process application:openFile: event.
while (true) {
NSEvent *event = [NSApp
nextEventMatchingMask:NSEventMaskAny
@ -657,14 +488,7 @@ OS_OSX::OS_OSX() {
[NSApp activateIgnoringOtherApps:YES];
}
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
return p_feature == "pc";
}
void OS_OSX::disable_crash_handler() {
crash_handler.disable();
}
bool OS_OSX::is_disable_crash_handler() const {
return crash_handler.is_disabled();
OS_OSX::~OS_OSX() {
CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
CFRelease(pre_wait_observer);
}

View File

@ -0,0 +1,44 @@
/*************************************************************************/
/* osx_terminal_logger.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifndef OSX_TERMINAL_LOGGER_H
#define OSX_TERMINAL_LOGGER_H
#ifdef OSX_ENABLED
#include "core/io/logger.h"
class OSXTerminalLogger : public StdLogger {
public:
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) override;
};
#endif // OSX_ENABLED
#endif // OSX_TERMINAL_LOGGER_H

View File

@ -0,0 +1,81 @@
/*************************************************************************/
/* osx_terminal_logger.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "osx_terminal_logger.h"
#ifdef OSX_ENABLED
#include <os/log.h>
void OSXTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
if (!should_log(true)) {
return;
}
const char *err_details;
if (p_rationale && p_rationale[0])
err_details = p_rationale;
else
err_details = p_code;
switch (p_type) {
case ERR_WARNING:
os_log_info(OS_LOG_DEFAULT,
"WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_SCRIPT:
os_log_error(OS_LOG_DEFAULT,
"SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_SHADER:
os_log_error(OS_LOG_DEFAULT,
"SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
case ERR_ERROR:
default:
os_log_error(OS_LOG_DEFAULT,
"ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
err_details, p_function, p_file, p_line);
logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
break;
}
}
#endif // OSX_ENABLED