summaryrefslogtreecommitdiffstats
path: root/platform/macos/display_server_macos.mm
diff options
context:
space:
mode:
Diffstat (limited to 'platform/macos/display_server_macos.mm')
-rw-r--r--platform/macos/display_server_macos.mm307
1 files changed, 261 insertions, 46 deletions
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index cad8435cbb..ce4c7b2e05 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -35,6 +35,7 @@
#include "godot_menu_delegate.h"
#include "godot_menu_item.h"
#include "godot_open_save_delegate.h"
+#include "godot_status_item.h"
#include "godot_window.h"
#include "godot_window_delegate.h"
#include "key_mapping_macos.h"
@@ -195,19 +196,22 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
}
#if defined(RD_ENABLED)
- if (context_rd) {
+ if (rendering_context) {
union {
#ifdef VULKAN_ENABLED
- VulkanContextMacOS::WindowPlatformData vulkan;
+ RenderingContextDriverVulkanMacOS::WindowPlatformData vulkan;
#endif
} wpd;
#ifdef VULKAN_ENABLED
if (rendering_driver == "vulkan") {
- wpd.vulkan.view_ptr = &wd.window_view;
+ wpd.vulkan.layer_ptr = (CAMetalLayer *const *)&layer;
}
#endif
- Error err = context_rd->window_create(window_id_counter, p_vsync_mode, p_rect.size.width, p_rect.size.height, &wpd);
- ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s context", context_rd->get_api_name()));
+ Error err = rendering_context->window_create(window_id_counter, &wpd);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, vformat("Can't create a %s context", rendering_driver));
+
+ rendering_context->window_set_size(window_id_counter, p_rect.size.width, p_rect.size.height);
+ rendering_context->window_set_vsync_mode(window_id_counter, p_vsync_mode);
}
#endif
#if defined(GLES3_ENABLED)
@@ -257,11 +261,6 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod
gl_manager_angle->window_resize(id, wd.size.width, wd.size.height);
}
#endif
-#if defined(RD_ENABLED)
- if (context_rd) {
- context_rd->window_resize(id, wd.size.width, wd.size.height);
- }
-#endif
return id;
}
@@ -700,10 +699,12 @@ DisplayServerMacOS::WindowData &DisplayServerMacOS::get_window(WindowID p_window
}
void DisplayServerMacOS::send_event(NSEvent *p_event) {
- // Special case handling of command-period, which is traditionally a special
- // shortcut in macOS and doesn't arrive at our regular keyDown handler.
+ // Special case handling of shortcuts that don't arrive at the regular keyDown handler
if ([p_event type] == NSEventTypeKeyDown) {
- if ((([p_event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
+ NSEventModifierFlags flags = [p_event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
+
+ // Command-period
+ if ((flags == NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
Ref<InputEventKey> k;
k.instantiate();
@@ -716,6 +717,24 @@ void DisplayServerMacOS::send_event(NSEvent *p_event) {
k->set_echo([p_event isARepeat]);
Input::get_singleton()->parse_input_event(k);
+ return;
+ }
+
+ // Ctrl+Tab and Ctrl+Shift+Tab
+ if (((flags == NSEventModifierFlagControl) || (flags == (NSEventModifierFlagControl | NSEventModifierFlagShift))) && [p_event keyCode] == 0x30) {
+ Ref<InputEventKey> k;
+ k.instantiate();
+
+ get_key_modifier_state([p_event modifierFlags], k);
+ k->set_window_id(DisplayServerMacOS::INVALID_WINDOW_ID);
+ k->set_pressed(true);
+ k->set_keycode(Key::TAB);
+ k->set_physical_keycode(Key::TAB);
+ k->set_key_label(Key::TAB);
+ k->set_echo([p_event isARepeat]);
+
+ Input::get_singleton()->parse_input_event(k);
+ return;
}
}
}
@@ -791,8 +810,12 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) {
}
#endif
#ifdef RD_ENABLED
- if (context_rd) {
- context_rd->window_destroy(p_window);
+ if (rendering_device) {
+ rendering_device->screen_free(p_window);
+ }
+
+ if (rendering_context) {
+ rendering_context->window_destroy(p_window);
}
#endif
windows.erase(p_window);
@@ -800,6 +823,11 @@ void DisplayServerMacOS::window_destroy(WindowID p_window) {
}
void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_height) {
+#if defined(RD_ENABLED)
+ if (rendering_context) {
+ rendering_context->window_set_size(p_window, p_width, p_height);
+ }
+#endif
#if defined(GLES3_ENABLED)
if (gl_manager_legacy) {
gl_manager_legacy->window_resize(p_window, p_width, p_height);
@@ -808,11 +836,6 @@ void DisplayServerMacOS::window_resize(WindowID p_window, int p_width, int p_hei
gl_manager_angle->window_resize(p_window, p_width, p_height);
}
#endif
-#if defined(VULKAN_ENABLED)
- if (context_rd) {
- context_rd->window_resize(p_window, p_width, p_height);
- }
-#endif
}
bool DisplayServerMacOS::has_feature(Feature p_feature) const {
@@ -836,6 +859,8 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
case FEATURE_TEXT_TO_SPEECH:
case FEATURE_EXTEND_TO_TITLE:
case FEATURE_SCREEN_CAPTURE:
+ case FEATURE_STATUS_INDICATOR:
+ case FEATURE_NATIVE_HELP:
return true;
default: {
}
@@ -847,6 +872,19 @@ String DisplayServerMacOS::get_name() const {
return "macOS";
}
+void DisplayServerMacOS::help_set_search_callbacks(const Callable &p_search_callback, const Callable &p_action_callback) {
+ help_search_callback = p_search_callback;
+ help_action_callback = p_action_callback;
+}
+
+Callable DisplayServerMacOS::_help_get_search_callback() const {
+ return help_search_callback;
+}
+
+Callable DisplayServerMacOS::_help_get_action_callback() const {
+ return help_action_callback;
+}
+
bool DisplayServerMacOS::_is_menu_opened(NSMenu *p_menu) const {
if (submenu_inv.has(p_menu)) {
const MenuData &md = submenu[submenu_inv[p_menu]];
@@ -2033,7 +2071,17 @@ bool DisplayServerMacOS::is_dark_mode() const {
Color DisplayServerMacOS::get_accent_color() const {
if (@available(macOS 10.14, *)) {
- NSColor *color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
+ __block NSColor *color = nullptr;
+ if (@available(macOS 11.0, *)) {
+ [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{
+ color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
+ }];
+ } else {
+ NSAppearance *saved_appearance = [NSAppearance currentAppearance];
+ [NSAppearance setCurrentAppearance:[NSApp effectiveAppearance]];
+ color = [[NSColor controlAccentColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
+ [NSAppearance setCurrentAppearance:saved_appearance];
+ }
if (color) {
CGFloat components[4];
[color getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
@@ -2046,6 +2094,41 @@ Color DisplayServerMacOS::get_accent_color() const {
}
}
+Color DisplayServerMacOS::get_base_color() const {
+ if (@available(macOS 10.14, *)) {
+ __block NSColor *color = nullptr;
+ if (@available(macOS 11.0, *)) {
+ [NSApp.effectiveAppearance performAsCurrentDrawingAppearance:^{
+ color = [[NSColor controlColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
+ }];
+ } else {
+ NSAppearance *saved_appearance = [NSAppearance currentAppearance];
+ [NSAppearance setCurrentAppearance:[NSApp effectiveAppearance]];
+ color = [[NSColor controlColor] colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
+ [NSAppearance setCurrentAppearance:saved_appearance];
+ }
+ if (color) {
+ CGFloat components[4];
+ [color getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
+ return Color(components[0], components[1], components[2], components[3]);
+ } else {
+ return Color(0, 0, 0, 0);
+ }
+ } else {
+ return Color(0, 0, 0, 0);
+ }
+}
+
+void DisplayServerMacOS::set_system_theme_change_callback(const Callable &p_callable) {
+ system_theme_changed = p_callable;
+}
+
+void DisplayServerMacOS::emit_system_theme_changed() {
+ if (system_theme_changed.is_valid()) {
+ system_theme_changed.call();
+ }
+}
+
Error DisplayServerMacOS::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
_THREAD_SAFE_METHOD_
@@ -2734,6 +2817,7 @@ Color DisplayServerMacOS::screen_get_pixel(const Point2i &p_position) const {
position += _get_screens_origin();
position /= screen_get_max_scale();
+ Color color;
for (NSScreen *screen in [NSScreen screens]) {
NSRect frame = [screen frame];
if (NSMouseInRect(NSMakePoint(position.x, position.y), frame, NO)) {
@@ -2741,18 +2825,22 @@ Color DisplayServerMacOS::screen_get_pixel(const Point2i &p_position) const {
CGDirectDisplayID display_id = [[screenDescription objectForKey:@"NSScreenNumber"] unsignedIntValue];
CGImageRef image = CGDisplayCreateImageForRect(display_id, CGRectMake(position.x - frame.origin.x, frame.size.height - (position.y - frame.origin.y), 1, 1));
if (image) {
- NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithCGImage:image];
- CGImageRelease(image);
- NSColor *color = [bitmap colorAtX:0 y:0];
- if (color) {
- CGFloat components[4];
- [color getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
- return Color(components[0], components[1], components[2], components[3]);
+ CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
+ if (color_space) {
+ uint8_t img_data[4];
+ CGContextRef context = CGBitmapContextCreate(img_data, 1, 1, 8, 4, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
+ if (context) {
+ CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), image);
+ color = Color(img_data[0] / 255.0f, img_data[1] / 255.0f, img_data[2] / 255.0f, img_data[3] / 255.0f);
+ CGContextRelease(context);
+ }
+ CGColorSpaceRelease(color_space);
}
+ CGImageRelease(image);
}
}
}
- return Color();
+ return color;
}
Ref<Image> DisplayServerMacOS::screen_get_image(int p_screen) const {
@@ -2850,7 +2938,11 @@ DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode,
window_set_flag(WindowFlags(i), true, id);
}
}
-
+#ifdef RD_ENABLED
+ if (rendering_device) {
+ rendering_device->screen_create(id);
+ }
+#endif
return id;
}
@@ -3811,8 +3903,8 @@ void DisplayServerMacOS::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_
}
#endif
#if defined(VULKAN_ENABLED)
- if (context_rd) {
- context_rd->set_vsync_mode(p_window, p_vsync_mode);
+ if (rendering_context) {
+ rendering_context->window_set_vsync_mode(p_window, p_vsync_mode);
}
#endif
}
@@ -3828,8 +3920,8 @@ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_wi
}
#endif
#if defined(VULKAN_ENABLED)
- if (context_rd) {
- return context_rd->get_vsync_mode(p_window);
+ if (rendering_context) {
+ return rendering_context->window_get_vsync_mode(p_window);
}
#endif
return DisplayServer::VSYNC_ENABLED;
@@ -4290,6 +4382,124 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) {
}
}
+DisplayServer::IndicatorID DisplayServerMacOS::create_status_indicator(const Ref<Image> &p_icon, const String &p_tooltip, const Callable &p_callback) {
+ NSImage *nsimg = nullptr;
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ Ref<Image> img = p_icon->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
+
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:nullptr
+ pixelsWide:img->get_width()
+ pixelsHigh:img->get_height()
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:img->get_width() * 4
+ bitsPerPixel:32];
+ if (imgrep) {
+ uint8_t *pixels = [imgrep bitmapData];
+
+ int len = img->get_width() * img->get_height();
+ const uint8_t *r = img->get_data().ptr();
+
+ /* Premultiply the alpha channel */
+ for (int i = 0; i < len; i++) {
+ uint8_t alpha = r[i * 4 + 3];
+ pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
+ pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
+ pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
+ if (nsimg) {
+ [nsimg addRepresentation:imgrep];
+ }
+ }
+ }
+
+ IndicatorData idat;
+
+ idat.item = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
+ idat.view = [[GodotStatusItemView alloc] init];
+
+ [idat.view setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]];
+ [idat.view setImage:nsimg];
+ [idat.view setCallback:p_callback];
+ [idat.item setView:idat.view];
+
+ IndicatorID iid = indicator_id_counter++;
+ indicators[iid] = idat;
+
+ return iid;
+}
+
+void DisplayServerMacOS::status_indicator_set_icon(IndicatorID p_id, const Ref<Image> &p_icon) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ NSImage *nsimg = nullptr;
+ if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0) {
+ Ref<Image> img = p_icon->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
+
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:nullptr
+ pixelsWide:img->get_width()
+ pixelsHigh:img->get_height()
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:img->get_width() * 4
+ bitsPerPixel:32];
+ if (imgrep) {
+ uint8_t *pixels = [imgrep bitmapData];
+
+ int len = img->get_width() * img->get_height();
+ const uint8_t *r = img->get_data().ptr();
+
+ /* Premultiply the alpha channel */
+ for (int i = 0; i < len; i++) {
+ uint8_t alpha = r[i * 4 + 3];
+ pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
+ pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
+ pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
+ if (nsimg) {
+ [nsimg addRepresentation:imgrep];
+ }
+ }
+ }
+
+ [indicators[p_id].view setImage:nsimg];
+}
+
+void DisplayServerMacOS::status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ [indicators[p_id].view setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]];
+}
+
+void DisplayServerMacOS::status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ [indicators[p_id].view setCallback:p_callback];
+}
+
+void DisplayServerMacOS::delete_status_indicator(IndicatorID p_id) {
+ ERR_FAIL_COND(!indicators.has(p_id));
+
+ [[NSStatusBar systemStatusBar] removeStatusItem:indicators[p_id].item];
+ indicators.erase(p_id);
+}
+
DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) {
DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, r_error));
if (r_error != OK) {
@@ -4632,16 +4842,16 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
#if defined(RD_ENABLED)
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
- context_rd = memnew(VulkanContextMacOS);
+ rendering_context = memnew(RenderingContextDriverVulkanMacOS);
}
#endif
- if (context_rd) {
- if (context_rd->initialize() != OK) {
- memdelete(context_rd);
- context_rd = nullptr;
+ if (rendering_context) {
+ if (rendering_context->initialize() != OK) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
r_error = ERR_CANT_CREATE;
- ERR_FAIL_MSG("Could not initialize Vulkan");
+ ERR_FAIL_MSG("Could not initialize " + rendering_driver);
}
}
#endif
@@ -4676,9 +4886,10 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
}
#endif
#if defined(RD_ENABLED)
- if (context_rd) {
+ if (rendering_context) {
rendering_device = memnew(RenderingDevice);
- rendering_device->initialize(context_rd);
+ rendering_device->initialize(rendering_context, MAIN_WINDOW_ID);
+ rendering_device->screen_create(MAIN_WINDOW_ID);
RendererCompositorRD::make_current();
}
@@ -4693,6 +4904,11 @@ DisplayServerMacOS::~DisplayServerMacOS() {
screen_keep_on_assertion = kIOPMNullAssertionID;
}
+ // Destroy all status indicators.
+ for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E;) {
+ [[NSStatusBar systemStatusBar] removeStatusItem:E->value.item];
+ }
+
// Destroy all windows.
for (HashMap<WindowID, WindowData>::Iterator E = windows.begin(); E;) {
HashMap<WindowID, WindowData>::Iterator F = E;
@@ -4714,14 +4930,13 @@ DisplayServerMacOS::~DisplayServerMacOS() {
#endif
#if defined(RD_ENABLED)
if (rendering_device) {
- rendering_device->finalize();
memdelete(rendering_device);
rendering_device = nullptr;
}
- if (context_rd) {
- memdelete(context_rd);
- context_rd = nullptr;
+ if (rendering_context) {
+ memdelete(rendering_context);
+ rendering_context = nullptr;
}
#endif