feat: Add EDR support to Metal and all Apple platforms

This commit is contained in:
Stuart Carnie 2026-02-07 10:52:07 +11:00
parent 923c751af4
commit d5504ffc69
24 changed files with 912 additions and 450 deletions

View file

@ -2751,7 +2751,7 @@
Display server supports interaction with screen reader or Braille display. [b]Linux (X11/Wayland), macOS, Windows[/b]
</constant>
<constant name="FEATURE_HDR_OUTPUT" value="35" enum="Feature">
Display server supports HDR output. [b]Windows[/b]
Display server supports HDR output. [b]macOS, iOS, visionOS, Windows[/b]
</constant>
<constant name="ROLE_UNKNOWN" value="0" enum="AccessibilityRole">
Unknown or custom role.

View file

@ -57,6 +57,7 @@
class InputEvent;
class NativeMenu;
/// "Embedded" as in "Embedded Device".
class DisplayServerAppleEmbedded : public DisplayServer {
GDSOFTCLASS(DisplayServerAppleEmbedded, DisplayServer);
@ -87,7 +88,16 @@ class DisplayServerAppleEmbedded : public DisplayServer {
void initialize_tts() const;
bool edr_requested = false;
void _update_hdr_output();
float _calculate_current_reference_luminance() const;
protected:
virtual bool _screen_hdr_is_supported() const { return false; }
virtual float _screen_potential_edr_headroom() const { return 1.0f; }
virtual float _screen_current_edr_headroom() const { return 1.0f; }
float hardware_reference_luminance_nits = 100.0f;
DisplayServerAppleEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
~DisplayServerAppleEmbedded();
@ -230,6 +240,24 @@ public:
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
// MARK: - HDR / EDR
void current_edr_headroom_changed();
virtual bool window_is_hdr_output_supported(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_request_hdr_output(const bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_requested(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_output_max_linear_value(WindowID p_window = MAIN_WINDOW_ID) const override;
void resize_window(CGSize size);
virtual void swap_buffers() override {}

View file

@ -376,6 +376,7 @@ bool DisplayServerAppleEmbedded::has_feature(Feature p_feature) const {
// case FEATURE_NATIVE_ICON:
// case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_CLIPBOARD:
case FEATURE_HDR_OUTPUT:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_ORIENTATION:
case FEATURE_TOUCHSCREEN:
@ -820,6 +821,106 @@ DisplayServer::VSyncMode DisplayServerAppleEmbedded::window_get_vsync_mode(Windo
return DisplayServer::VSYNC_ENABLED;
}
// MARK: - HDR / EDR
void DisplayServerAppleEmbedded::_update_hdr_output() {
#ifdef RD_ENABLED
if (!rendering_context) {
return;
}
bool desired = edr_requested && _screen_hdr_is_supported();
if (rendering_context->window_get_hdr_output_enabled(MAIN_WINDOW_ID) != desired) {
rendering_context->window_set_hdr_output_enabled(MAIN_WINDOW_ID, desired);
}
float reference_luminance = _calculate_current_reference_luminance();
rendering_context->window_set_hdr_output_reference_luminance(MAIN_WINDOW_ID, reference_luminance);
float max_luminance = _screen_potential_edr_headroom() * hardware_reference_luminance_nits;
rendering_context->window_set_hdr_output_max_luminance(MAIN_WINDOW_ID, max_luminance);
#endif
}
void DisplayServerAppleEmbedded::current_edr_headroom_changed() {
_update_hdr_output();
}
bool DisplayServerAppleEmbedded::window_is_hdr_output_supported(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
return false;
}
#endif
return _screen_hdr_is_supported();
}
void DisplayServerAppleEmbedded::window_request_hdr_output(const bool p_enabled, WindowID p_window) {
#if defined(RD_ENABLED)
ERR_FAIL_COND_MSG(p_enabled && rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT), "HDR output is not supported by the rendering device.");
#endif
edr_requested = p_enabled;
_update_hdr_output();
}
bool DisplayServerAppleEmbedded::window_is_hdr_output_requested(WindowID p_window) const {
return edr_requested;
}
bool DisplayServerAppleEmbedded::window_is_hdr_output_enabled(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_enabled(p_window);
}
#endif
return false;
}
void DisplayServerAppleEmbedded::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) {
ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Apple devices, as they provide a user-facing brightness setting that directly controls reference white luminance.");
}
float DisplayServerAppleEmbedded::window_get_hdr_output_reference_luminance(WindowID p_window) const {
return -1.0f; // Always auto-adjusted by the OS on Apple platforms.
}
float DisplayServerAppleEmbedded::_calculate_current_reference_luminance() const {
float potential = _screen_potential_edr_headroom();
float current = _screen_current_edr_headroom();
return potential * hardware_reference_luminance_nits / current;
}
float DisplayServerAppleEmbedded::window_get_hdr_output_current_reference_luminance(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_reference_luminance(p_window);
}
#endif
return 200.0f;
}
void DisplayServerAppleEmbedded::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
ERR_PRINT_ONCE("Manually setting max luminance is not supported on Apple embedded devices as they provide accurate max luminance values for their built-in screens.");
}
float DisplayServerAppleEmbedded::window_get_hdr_output_max_luminance(WindowID p_window) const {
return -1.0f;
}
float DisplayServerAppleEmbedded::window_get_hdr_output_current_max_luminance(WindowID p_window) const {
return _screen_potential_edr_headroom() * hardware_reference_luminance_nits;
}
float DisplayServerAppleEmbedded::window_get_output_max_linear_value(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_output_max_linear_value(p_window);
}
#endif
return 1.0f;
}
void DisplayServerAppleEmbedded::set_native_icon(const String &p_filename) {
// Not supported on Apple embedded platforms.
}

View file

@ -45,6 +45,7 @@ static const float earth_gravity = 9.80665;
@interface GDTView () {
UITouch *godot_touches[max_touches];
CGFloat last_edr_headroom;
}
@property(assign, nonatomic) BOOL isActive;
@ -121,6 +122,7 @@ static const float earth_gravity = 9.80665;
- (void)godot_commonInit {
self.preferredFrameRate = 60;
self.useCADisplayLink = bool(GLOBAL_DEF("display.AppleEmbedded/use_cadisplaylink", true)) ? YES : NO;
last_edr_headroom = 0.0;
#if !defined(VISIONOS_ENABLED)
self.contentScaleFactor = [UIScreen mainScreen].scale;
@ -249,6 +251,20 @@ static const float earth_gravity = 9.80665;
}
[self handleMotion];
#if !defined(VISIONOS_ENABLED)
if (@available(iOS 16.0, *)) {
CGFloat edr_headroom = UIScreen.mainScreen.currentEDRHeadroom;
if (last_edr_headroom != edr_headroom) {
last_edr_headroom = edr_headroom;
if (DisplayServerAppleEmbedded::get_singleton()) {
DisplayServerAppleEmbedded::get_singleton()->current_edr_headroom_changed();
}
}
}
#endif
[self.renderer renderOnView:self];
[self.renderingLayer stopRenderDisplayLayer];

View file

@ -119,7 +119,7 @@ public:
layer = nullptr;
}
Error resize(uint32_t p_desired_framebuffer_count) override final {
Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) override final {
if (width == 0 || height == 0) {
// Very likely the window is minimized, don't create a swap chain.
return ERR_SKIP;
@ -155,6 +155,28 @@ public:
frame_buffers[i].set_texture_count(1);
}
if (hdr_output) {
layer->setWantsExtendedDynamicRangeContent(true);
CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceExtendedLinearSRGB);
layer->setColorspace(color_space);
CGColorSpaceRelease(color_space);
layer->setPixelFormat(MTL::PixelFormatRGBA16Float);
r_color_space = RDD::COLOR_SPACE_REC709_LINEAR;
r_format = RDD::DATA_FORMAT_R16G16B16A16_SFLOAT;
pixel_format = MTL::PixelFormatRGBA16Float;
} else {
layer->setWantsExtendedDynamicRangeContent(false);
CGColorSpaceRef color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
layer->setColorspace(color_space);
CGColorSpaceRelease(color_space);
layer->setPixelFormat(MTL::PixelFormatBGRA8Unorm);
r_color_space = RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB;
r_format = RDD::DATA_FORMAT_B8G8R8A8_UNORM;
pixel_format = MTL::PixelFormatBGRA8Unorm;
}
return OK;
}
@ -256,7 +278,7 @@ public:
memdelete_arr(frame_buffers);
}
Error resize(uint32_t p_desired_framebuffer_count) override final {
Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) override final {
if (width == 0 || height == 0) {
// Very likely the window is minimized, don't create a swap chain.
return ERR_SKIP;
@ -379,13 +401,12 @@ DisplayServer::VSyncMode RenderingContextDriverMetal::surface_get_vsync_mode(Sur
void RenderingContextDriverMetal::surface_set_hdr_output_enabled(SurfaceID p_surface, bool p_enabled) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_output = p_enabled;
surface->needs_resize = true;
surface->set_hdr_output_enabled(p_enabled);
}
bool RenderingContextDriverMetal::surface_get_hdr_output_enabled(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_output;
return surface->is_hdr_output_enabled();
}
void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(SurfaceID p_surface, float p_reference_luminance) {
@ -394,7 +415,7 @@ void RenderingContextDriverMetal::surface_set_hdr_output_reference_luminance(Sur
}
float RenderingContextDriverMetal::surface_get_hdr_output_reference_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
const Surface *surface = (Surface *)(p_surface);
return surface->hdr_reference_luminance;
}
@ -404,18 +425,16 @@ void RenderingContextDriverMetal::surface_set_hdr_output_max_luminance(SurfaceID
}
float RenderingContextDriverMetal::surface_get_hdr_output_max_luminance(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
const Surface *surface = (Surface *)(p_surface);
return surface->hdr_max_luminance;
}
void RenderingContextDriverMetal::surface_set_hdr_output_linear_luminance_scale(SurfaceID p_surface, float p_linear_luminance_scale) {
Surface *surface = (Surface *)(p_surface);
surface->hdr_linear_luminance_scale = p_linear_luminance_scale;
}
float RenderingContextDriverMetal::surface_get_hdr_output_linear_luminance_scale(SurfaceID p_surface) const {
Surface *surface = (Surface *)(p_surface);
return surface->hdr_linear_luminance_scale;
return surface->hdr_reference_luminance;
}
float RenderingContextDriverMetal::surface_get_hdr_output_max_value(SurfaceID p_surface) const {

View file

@ -91,22 +91,32 @@ public:
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
bool needs_resize = false;
double present_minimum_duration = 0.0;
MTL::PixelFormat pixel_format = MTL::PixelFormatBGRA8Unorm;
bool hdr_output = false;
// BT.2408 recommendation of 203 nits for HDR Reference White, rounded to 200
// to be a more pleasant player-facing value.
float hdr_reference_luminance = 200.0f;
float hdr_max_luminance = 1000.0f;
float hdr_linear_luminance_scale = 100.0f;
bool needs_resize = false;
bool hdr_output = false;
Surface(MTL::Device *p_device) :
device(p_device) {}
virtual ~Surface() = default;
MTL::PixelFormat get_pixel_format() const { return MTL::PixelFormatBGRA8Unorm; }
virtual Error resize(uint32_t p_desired_framebuffer_count) = 0;
MTL::PixelFormat get_pixel_format() const { return pixel_format; }
void set_hdr_output_enabled(bool p_enabled) {
if (hdr_output != p_enabled) {
hdr_output = p_enabled;
needs_resize = true;
}
}
bool is_hdr_output_enabled() const {
return hdr_output;
}
virtual Error resize(uint32_t p_desired_framebuffer_count, RDD::DataFormat &r_format, RDD::ColorSpace &r_color_space) = 0;
virtual RDD::FramebufferID acquire_next_frame_buffer() = 0;
virtual void present(MTL3::MDCommandBuffer *p_cmd_buffer) = 0;
virtual MTL::Drawable *next_drawable() = 0;

View file

@ -881,6 +881,11 @@ void RenderingDeviceDriverMetal::command_buffer_execute_secondary(CommandBufferI
void RenderingDeviceDriverMetal::_swap_chain_release(SwapChain *p_swap_chain) {
_swap_chain_release_buffers(p_swap_chain);
if (p_swap_chain->render_pass.id != 0) {
render_pass_free(p_swap_chain->render_pass);
p_swap_chain->render_pass = RenderPassID();
}
}
void RenderingDeviceDriverMetal::_swap_chain_release_buffers(SwapChain *p_swap_chain) {
@ -894,9 +899,15 @@ RDD::SwapChainID RenderingDeviceDriverMetal::swap_chain_create(RenderingContextD
GODOT_CLANG_WARNING_POP
}
SwapChain *swap_chain = memnew(SwapChain);
swap_chain->surface = p_surface;
return SwapChainID(swap_chain);
}
RDD::RenderPassID RenderingDeviceDriverMetal::_swap_chain_create_render_pass(RDD::DataFormat p_format) {
// Create the render pass that will be used to draw to the swap chain's framebuffers.
RDD::Attachment attachment;
attachment.format = pixel_formats->getDataFormat(surface->get_pixel_format());
attachment.format = p_format;
attachment.samples = RDD::TEXTURE_SAMPLES_1;
attachment.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR;
attachment.store_op = RDD::ATTACHMENT_STORE_OP_STORE;
@ -907,15 +918,7 @@ RDD::SwapChainID RenderingDeviceDriverMetal::swap_chain_create(RenderingContextD
color_ref.aspect.set_flag(RDD::TEXTURE_ASPECT_COLOR_BIT);
subpass.color_references.push_back(color_ref);
RenderPassID render_pass = render_pass_create(attachment, subpass, {}, 1, RDD::AttachmentReference());
ERR_FAIL_COND_V(!render_pass, SwapChainID());
// Create the empty swap chain until it is resized.
SwapChain *swap_chain = memnew(SwapChain);
swap_chain->surface = p_surface;
swap_chain->data_format = attachment.format;
swap_chain->render_pass = render_pass;
return SwapChainID(swap_chain);
return render_pass_create(attachment, subpass, {}, 1, RDD::AttachmentReference());
}
Error RenderingDeviceDriverMetal::swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) {
@ -924,7 +927,24 @@ Error RenderingDeviceDriverMetal::swap_chain_resize(CommandQueueID p_cmd_queue,
SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
RenderingContextDriverMetal::Surface *surface = (RenderingContextDriverMetal::Surface *)(swap_chain->surface);
surface->resize(p_desired_framebuffer_count);
DataFormat new_data_format = DATA_FORMAT_MAX;
ColorSpace new_color_space = COLOR_SPACE_MAX;
Error err = surface->resize(p_desired_framebuffer_count, new_data_format, new_color_space);
if (err != OK) {
return err;
}
if (new_data_format != swap_chain->data_format) {
_swap_chain_release(swap_chain);
swap_chain->render_pass = _swap_chain_create_render_pass(new_data_format);
if (!swap_chain->render_pass) {
return ERR_INVALID_DATA;
}
}
swap_chain->data_format = new_data_format;
swap_chain->color_space = new_color_space;
// Once everything's been created correctly, indicate the surface no longer needs to be resized.
context_driver->surface_set_needs_resize(swap_chain->surface, false);
@ -957,7 +977,8 @@ RDD::DataFormat RenderingDeviceDriverMetal::swap_chain_get_format(SwapChainID p_
}
RDD::ColorSpace RenderingDeviceDriverMetal::swap_chain_get_color_space(SwapChainID p_swap_chain) {
return RDD::COLOR_SPACE_REC709_NONLINEAR_SRGB;
const SwapChain *swap_chain = (const SwapChain *)(p_swap_chain.id);
return swap_chain->color_space;
}
void RenderingDeviceDriverMetal::swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) {
@ -975,7 +996,6 @@ void RenderingDeviceDriverMetal::swap_chain_free(SwapChainID p_swap_chain) {
GODOT_CLANG_WARNING_POP
}
_swap_chain_release(swap_chain);
render_pass_free(swap_chain->render_pass);
memdelete(swap_chain);
}
@ -2645,6 +2665,8 @@ bool RenderingDeviceDriverMetal::has_feature(Features p_feature) {
return device_properties->features.metal_fx_spatial;
case SUPPORTS_METALFX_TEMPORAL:
return device_properties->features.metal_fx_temporal;
case SUPPORTS_HDR_OUTPUT:
return true;
case SUPPORTS_IMAGE_ATOMIC_32_BIT:
return device_properties->features.supports_native_image_atomics;
case SUPPORTS_VULKAN_MEMORY_MODEL:

View file

@ -301,11 +301,11 @@ protected:
struct SwapChain {
RenderingContextDriver::SurfaceID surface = RenderingContextDriver::SurfaceID();
RenderPassID render_pass;
RDD::DataFormat data_format = DATA_FORMAT_MAX;
SwapChain() :
render_pass(nullptr) {}
DataFormat data_format = DATA_FORMAT_MAX;
ColorSpace color_space = COLOR_SPACE_MAX;
};
RenderPassID _swap_chain_create_render_pass(DataFormat p_format);
void _swap_chain_release(SwapChain *p_swap_chain);
void _swap_chain_release_buffers(SwapChain *p_swap_chain);

View file

@ -7327,6 +7327,9 @@ bool RenderingDeviceDriverVulkan::has_feature(Features p_feature) {
// involving integrated GPU hardware do not function correctly
// with HDR output.
return false;
#elif defined(MACOS_ENABLED) || defined(APPLE_EMBEDDED_ENABLED)
// HDR support has not yet been thoroughly tested and validated for Apple platforms.
return false;
#else
return context_driver->is_colorspace_supported();
#endif // defined(WINDOWS_ENABLED)

View file

@ -32,7 +32,7 @@
#include "drivers/apple_embedded/display_server_apple_embedded.h"
class DisplayServerIOS : public DisplayServerAppleEmbedded {
class DisplayServerIOS final : public DisplayServerAppleEmbedded {
GDSOFTCLASS(DisplayServerIOS, DisplayServerAppleEmbedded);
_THREAD_SAFE_CLASS_
@ -51,4 +51,9 @@ public:
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
protected:
virtual bool _screen_hdr_is_supported() const override;
virtual float _screen_potential_edr_headroom() const override;
virtual float _screen_current_edr_headroom() const override;
};

View file

@ -41,6 +41,8 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() {
DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error) :
DisplayServerAppleEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, p_parent_window, r_error) {
// See: https://github.com/godotengine/godot/pull/106814#discussion_r2854262156 for why this was set to 203 nits.
hardware_reference_luminance_nits = 203.0f;
}
DisplayServerIOS::~DisplayServerIOS() {
@ -118,3 +120,24 @@ float DisplayServerIOS::screen_get_scale(int p_screen) const {
return [UIScreen mainScreen].scale;
}
bool DisplayServerIOS::_screen_hdr_is_supported() const {
if (@available(iOS 16.0, *)) {
return [UIScreen mainScreen].potentialEDRHeadroom > 1.0;
}
return false;
}
float DisplayServerIOS::_screen_potential_edr_headroom() const {
if (@available(iOS 16.0, *)) {
return [UIScreen mainScreen].potentialEDRHeadroom;
}
return 1.0f;
}
float DisplayServerIOS::_screen_current_edr_headroom() const {
if (@available(iOS 16.0, *)) {
return [UIScreen mainScreen].currentEDRHeadroom;
}
return 1.0f;
}

View file

@ -33,7 +33,7 @@ files = [
if env.editor_build:
files += [
"display_server_embedded.mm",
"display_server_macos_embedded.mm",
"embedded_debugger.mm",
"embedded_gl_manager.mm",
"editor/embedded_game_view_plugin.mm",

View file

@ -151,6 +151,8 @@ public:
bool extend_to_title = false;
bool hide_from_capture = false;
HDROutput hdr_output;
Rect2i parent_safe_rect;
};
@ -160,13 +162,11 @@ public:
GodotProgressView *dock_progress = nullptr;
private:
id screen_observer = nil;
#if defined(GLES3_ENABLED)
GLManagerLegacy_MacOS *gl_manager_legacy = nullptr;
GLManagerANGLE_MacOS *gl_manager_angle = nullptr;
#endif
#if defined(RD_ENABLED)
RenderingContextDriver *rendering_context = nullptr;
RenderingDevice *rendering_device = nullptr;
#endif
String rendering_driver;
@ -184,15 +184,11 @@ private:
id menu_delegate = nullptr;
NativeMenuMacOS *native_menu = nullptr;
Point2i im_selection;
String im_text;
CGEventSourceRef event_source;
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE;
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
bool mouse_mode_override_enabled = false;
void _mouse_update_mode();
void _mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) override;
HDROutput &_get_hdr_output(WindowID p_window) override;
const HDROutput &_get_hdr_output(WindowID p_window) const override;
bool drop_events = false;
bool in_dispatch_input_event = false;
@ -205,7 +201,6 @@ private:
mutable bool displays_arrangement_dirty = true;
bool is_resizing = false;
CursorShape cursor_shape = CURSOR_ARROW;
NSCursor *cursors[CURSOR_MAX];
HashMap<CursorShape, Vector<Variant>> cursors_cache;
@ -219,8 +214,6 @@ private:
IndicatorID indicator_id_counter = 0;
HashMap<IndicatorID, IndicatorData> indicators;
IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID;
Callable help_search_callback;
Callable help_action_callback;
@ -275,7 +268,6 @@ public:
void update_mouse_pos(WindowData &p_wd, NSPoint p_location_in_window);
void push_to_key_event_buffer(const KeyEvent &p_event);
void pop_last_key_event();
void update_im_text(const Point2i &p_selection, const String &p_text);
void set_last_focused_window(WindowID p_window);
bool mouse_process_popups(bool p_close = false);
void popup_open(WindowID p_window);
@ -312,22 +304,12 @@ public:
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) override;
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, WindowID p_window_id) override;
virtual void beep() const override;
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
virtual void mouse_set_mode_override(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode_override() const override;
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
virtual bool mouse_is_mode_override_enabled() const override;
bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp);
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
virtual int get_keyboard_focus_screen() const override;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
@ -335,13 +317,9 @@ public:
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_max_scale() const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Color screen_get_pixel(const Point2i &p_position) const override;
virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Ref<Image> screen_get_image_rect(const Rect2i &p_rect) const override;
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
virtual Vector<int> get_window_list() const override;
virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID) override;
@ -416,6 +394,9 @@ public:
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
public:
void update_screen_parameters();
virtual bool window_maximize_on_title_dbl_click() const override;
virtual bool window_minimize_on_title_dbl_click() const override;
@ -425,17 +406,8 @@ public:
virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) override;
virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual int accessibility_should_increase_contrast() const override;
virtual int accessibility_should_reduce_animation() const override;
virtual int accessibility_should_reduce_transparency() const override;
virtual int accessibility_screen_reader_active() const override;
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 Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
virtual bool get_swap_cancel_ok() override;
@ -467,6 +439,8 @@ public:
virtual bool is_window_transparency_available() const override;
void window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const override;
static DisplayServer *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, Context p_context, int64_t p_parent_window, Error &r_error);
static Vector<String> get_rendering_drivers_func();

View file

@ -63,7 +63,7 @@
#include "servers/rendering/dummy/rasterizer_dummy.h"
#ifdef TOOLS_ENABLED
#import "display_server_embedded.h"
#import "display_server_macos_embedded.h"
#import "editor/embedded_process_macos.h"
#endif
@ -742,13 +742,6 @@ void DisplayServerMacOS::push_to_key_event_buffer(const DisplayServerMacOS::KeyE
key_event_buffer.write[key_event_pos++] = p_event;
}
void DisplayServerMacOS::update_im_text(const Point2i &p_selection, const String &p_text) {
im_selection = p_selection;
im_text = p_text;
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
}
void DisplayServerMacOS::set_last_focused_window(WindowID p_window) {
last_focused_window = p_window;
}
@ -842,6 +835,7 @@ bool DisplayServerMacOS::has_feature(Feature p_feature) const {
case FEATURE_SCREEN_EXCLUDE_FROM_CAPTURE:
case FEATURE_EMOJI_AND_SYMBOL_PICKER:
case FEATURE_WINDOW_EMBEDDING:
case FEATURE_HDR_OUTPUT:
return true;
#ifdef ACCESSKIT_ENABLED
case FEATURE_ACCESSIBILITY_SCREEN_READER: {
@ -1174,10 +1168,6 @@ Error DisplayServerMacOS::_file_dialog_with_options_show(const String &p_title,
return OK;
}
void DisplayServerMacOS::beep() const {
NSBeep();
}
Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
_THREAD_SAFE_METHOD_
@ -1214,32 +1204,24 @@ Error DisplayServerMacOS::dialog_input_text(String p_title, String p_description
return OK;
}
void DisplayServerMacOS::_mouse_update_mode() {
void DisplayServerMacOS::_mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) {
_THREAD_SAFE_METHOD_
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
? mouse_mode_override
: mouse_mode_base;
if (wanted_mouse_mode == mouse_mode) {
return;
}
WindowID window_id = _get_focused_window_or_popup();
if (!windows.has(window_id)) {
window_id = MAIN_WINDOW_ID;
}
WindowData &wd = windows[window_id];
bool show_cursor = (wanted_mouse_mode == MOUSE_MODE_VISIBLE || wanted_mouse_mode == MOUSE_MODE_CONFINED);
bool previously_shown = (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED);
bool show_cursor = (p_new_mode == MOUSE_MODE_VISIBLE || p_new_mode == MOUSE_MODE_CONFINED);
bool previously_shown = (p_prev_mode == MOUSE_MODE_VISIBLE || p_prev_mode == MOUSE_MODE_CONFINED);
if (show_cursor && !previously_shown) {
window_id = get_window_at_screen_position(mouse_get_position());
mouse_enter_window(window_id);
}
if (wanted_mouse_mode == MOUSE_MODE_CAPTURED) {
if (p_new_mode == MOUSE_MODE_CAPTURED) {
// Apple Docs state that the display parameter is not used.
// "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
// https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
@ -1253,17 +1235,17 @@ void DisplayServerMacOS::_mouse_update_mode() {
NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
CGWarpMouseCursorPosition(lMouseWarpPos);
} else if (wanted_mouse_mode == MOUSE_MODE_HIDDEN) {
} else if (p_new_mode == MOUSE_MODE_HIDDEN) {
if (previously_shown) {
CGDisplayHideCursor(kCGDirectMainDisplay);
}
[wd.window_object setMovable:YES];
CGAssociateMouseAndMouseCursorPosition(true);
} else if (wanted_mouse_mode == MOUSE_MODE_CONFINED) {
} else if (p_new_mode == MOUSE_MODE_CONFINED) {
CGDisplayShowCursor(kCGDirectMainDisplay);
[wd.window_object setMovable:NO];
CGAssociateMouseAndMouseCursorPosition(false);
} else if (wanted_mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
} else if (p_new_mode == MOUSE_MODE_CONFINED_HIDDEN) {
if (previously_shown) {
CGDisplayHideCursor(kCGDirectMainDisplay);
}
@ -1278,51 +1260,12 @@ void DisplayServerMacOS::_mouse_update_mode() {
last_warp = [[NSProcessInfo processInfo] systemUptime];
ignore_warp = true;
warp_events.clear();
mouse_mode = wanted_mouse_mode;
if (show_cursor) {
cursor_update_shape();
}
}
void DisplayServerMacOS::mouse_set_mode(MouseMode p_mode) {
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
if (p_mode == mouse_mode_base) {
return;
}
mouse_mode_base = p_mode;
_mouse_update_mode();
}
DisplayServer::MouseMode DisplayServerMacOS::mouse_get_mode() const {
return mouse_mode;
}
void DisplayServerMacOS::mouse_set_mode_override(MouseMode p_mode) {
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
if (p_mode == mouse_mode_override) {
return;
}
mouse_mode_override = p_mode;
_mouse_update_mode();
}
DisplayServer::MouseMode DisplayServerMacOS::mouse_get_mode_override() const {
return mouse_mode_override;
}
void DisplayServerMacOS::mouse_set_mode_override_enabled(bool p_override_enabled) {
if (p_override_enabled == mouse_mode_override_enabled) {
return;
}
mouse_mode_override_enabled = p_override_enabled;
_mouse_update_mode();
}
bool DisplayServerMacOS::mouse_is_mode_override_enabled() const {
return mouse_mode_override_enabled;
}
bool DisplayServerMacOS::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp) {
_THREAD_SAFE_METHOD_
@ -1460,10 +1403,6 @@ int DisplayServerMacOS::get_screen_count() const {
return [screenArray count];
}
int DisplayServerMacOS::get_primary_screen() const {
return 0;
}
int DisplayServerMacOS::get_keyboard_focus_screen() const {
const NSUInteger index = [[NSScreen screens] indexOfObject:[NSScreen mainScreen]];
return (index == NSNotFound) ? get_primary_screen() : index;
@ -1509,11 +1448,12 @@ int DisplayServerMacOS::screen_get_dpi(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
NSScreen *screen = [screenArray objectAtIndex:p_screen];
NSDictionary *description = [screen deviceDescription];
const NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
const CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
float scale = [[screenArray objectAtIndex:p_screen] backingScaleFactor];
const CGSize displayPhysicalSize = CGDisplayScreenSize(_get_display_id_for_screen(screen));
float scale = [screen backingScaleFactor];
float den2 = (displayPhysicalSize.width / 25.4f) * (displayPhysicalSize.width / 25.4f) + (displayPhysicalSize.height / 25.4f) * (displayPhysicalSize.height / 25.4f);
if (den2 > 0.0f) {
@ -1711,42 +1651,6 @@ Ref<Image> DisplayServerMacOS::screen_get_image_rect(const Rect2i &p_rect) const
return img;
}
float DisplayServerMacOS::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
int screen_count = get_screen_count();
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode);
return (float)displayRefreshRate;
}
ERR_PRINT("An error occurred while trying to get the screen refresh rate.");
return SCREEN_REFRESH_RATE_FALLBACK;
}
bool DisplayServerMacOS::screen_is_kept_on() const {
return (screen_keep_on_assertion);
}
void DisplayServerMacOS::screen_set_keep_on(bool p_enable) {
if (screen_keep_on_assertion) {
IOPMAssertionRelease(screen_keep_on_assertion);
screen_keep_on_assertion = kIOPMNullAssertionID;
}
if (p_enable) {
String app_name_string = GLOBAL_GET("application/config/name");
NSString *name = [NSString stringWithUTF8String:(app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data())];
NSString *reason = @"Godot Engine running with display/window/energy_saving/keep_screen_on = true";
IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, (__bridge CFStringRef)name, (__bridge CFStringRef)reason, (__bridge CFStringRef)reason, nullptr, 0, nullptr, &screen_keep_on_assertion);
}
}
Vector<DisplayServer::WindowID> DisplayServerMacOS::get_window_list() const {
_THREAD_SAFE_METHOD_
@ -1761,11 +1665,16 @@ DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode,
_THREAD_SAFE_METHOD_
WindowID id = _create_window(p_mode, p_vsync_mode, p_rect);
for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
if (p_flags & (1 << i)) {
window_set_flag(WindowFlags(i), true, id);
}
uint32_t set_flags = p_flags & ~(WINDOW_FLAG_MAX - 1); // Clear the flags that are not supported by the window.
while (set_flags != 0) {
// Find the index of the next set bit.
uint32_t index = (uint32_t)__builtin_ctzll(set_flags);
// Clear the set bit.
set_flags &= (set_flags - 1);
window_set_flag(WindowFlags(index), true, id);
}
#ifdef RD_ENABLED
if (rendering_device) {
rendering_device->screen_create(id);
@ -1975,6 +1884,8 @@ void DisplayServerMacOS::reparent_check(WindowID p_window) {
WindowData &wd = windows[p_window];
NSScreen *screen = [wd.window_object screen];
_update_hdr_output(p_window, wd.hdr_output);
_window_update_display_id(&wd);
if (wd.transient_parent != INVALID_WINDOW_ID) {
@ -2925,28 +2836,49 @@ DisplayServer::VSyncMode DisplayServerMacOS::window_get_vsync_mode(WindowID p_wi
return DisplayServer::VSYNC_ENABLED;
}
int DisplayServerMacOS::accessibility_should_increase_contrast() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getHighContrast];
DisplayServerMacOSBase::HDROutput &DisplayServerMacOS::_get_hdr_output(WindowID p_window) {
return windows.getptr(p_window)->hdr_output;
}
int DisplayServerMacOS::accessibility_should_reduce_animation() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceMotion];
const DisplayServerMacOSBase::HDROutput &DisplayServerMacOS::_get_hdr_output(WindowID p_window) const {
return windows.getptr(p_window)->hdr_output;
}
int DisplayServerMacOS::accessibility_should_reduce_transparency() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceTransparency];
void DisplayServerMacOS::window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const {
_THREAD_SAFE_METHOD_
const WindowData *wd = windows.getptr(p_window);
ERR_FAIL_NULL(wd);
NSScreen *screen = wd->window_object.screen;
#define SET_VAL(v, val) \
if (v) { \
*v = val; \
}
if (@available(macOS 10.15, *)) {
SET_VAL(r_max_potential_edr_value, screen.maximumPotentialExtendedDynamicRangeColorComponentValue);
SET_VAL(r_max_edr_value, screen.maximumExtendedDynamicRangeColorComponentValue);
} else {
SET_VAL(r_max_potential_edr_value, 1.0);
SET_VAL(r_max_edr_value, 1.0);
}
#undef SET_VAL
}
int DisplayServerMacOS::accessibility_screen_reader_active() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getVoiceOver];
}
void DisplayServerMacOS::update_screen_parameters() {
for (const KeyValue<WindowID, WindowData> &E : windows) {
if (E.value.hdr_output.requested) {
_update_hdr_output(E.key, E.value.hdr_output);
}
}
Point2i DisplayServerMacOS::ime_get_selection() const {
return im_selection;
}
String DisplayServerMacOS::ime_get_text() const {
return im_text;
#ifdef TOOLS_ENABLED
for (KeyValue<OS::ProcessID, EmbeddedProcessData> &E : embedded_processes) {
E.value.process->display_state_changed();
}
#endif
}
void DisplayServerMacOS::cursor_update_shape() {
@ -3031,10 +2963,6 @@ void DisplayServerMacOS::cursor_set_shape(CursorShape p_shape) {
cursor_update_shape();
}
DisplayServerMacOS::CursorShape DisplayServerMacOS::cursor_get_shape() const {
return cursor_shape;
}
void DisplayServerMacOS::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
@ -3135,7 +3063,7 @@ uint32_t DisplayServerMacOS::window_get_display_id(WindowID p_window) const {
void DisplayServerMacOS::_window_update_display_id(WindowData *p_wd) {
NSScreen *screen = [p_wd->window_object screen];
CGDirectDisplayID display_id = [[screen deviceDescription][@"NSScreenNumber"] unsignedIntValue];
CGDirectDisplayID display_id = _get_display_id_for_screen(screen);
if (p_wd->display_id == display_id) {
return;
}
@ -3925,6 +3853,15 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
}
force_process_and_drop_events();
__block DisplayServerMacOS *self = this;
screen_observer = [NSNotificationCenter.defaultCenter
addObserverForName:NSApplicationDidChangeScreenParametersNotification
object:nil
queue:nil
usingBlock:^(NSNotification *_Nonnull note) {
self->update_screen_parameters();
}];
if (rendering_driver == "dummy") {
RasterizerDummy::make_current();
}
@ -3946,16 +3883,9 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM
RendererCompositorRD::make_current();
}
#endif
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
}
DisplayServerMacOS::~DisplayServerMacOS() {
if (screen_keep_on_assertion) {
IOPMAssertionRelease(screen_keep_on_assertion);
screen_keep_on_assertion = kIOPMNullAssertionID;
}
// Destroy all status indicators.
for (HashMap<IndicatorID, IndicatorData>::Iterator E = indicators.begin(); E; ++E) {
[[NSStatusBar systemStatusBar] removeStatusItem:E->value.item];
@ -4002,6 +3932,8 @@ DisplayServerMacOS::~DisplayServerMacOS() {
memdelete(accessibility_driver);
}
#endif
[NSNotificationCenter.defaultCenter removeObserver:screen_observer];
CGDisplayRemoveReconfigurationCallback(_displays_arrangement_changed, nullptr);
cursors_cache.clear();

View file

@ -35,13 +35,18 @@
#define FontVariation __FontVariation
#import <AppKit/AppKit.h>
#import <IOKit/pwr_mgt/IOPMLib.h>
#undef FontVariation
class RenderingContextDriver;
class RenderingDevice;
class DisplayServerMacOSBase : public DisplayServer {
GDSOFTCLASS(DisplayServerMacOSBase, DisplayServer)
id tts = nullptr;
IOPMAssertionID screen_keep_on_assertion = kIOPMNullAssertionID;
struct LayoutInfo {
String name;
@ -53,21 +58,100 @@ class DisplayServerMacOSBase : public DisplayServer {
Callable system_theme_changed;
constexpr static CGFloat HARDWARE_REFERENCE_LUMINANCE_NITS = 100.0f;
public:
struct HDROutput {
static constexpr float AUTO_MAX_LUMINANCE = -1.0f;
bool requested = false;
float max_luminance = AUTO_MAX_LUMINANCE;
bool is_auto_max_luminance() const { return max_luminance < 0.0f; }
};
protected:
_THREAD_SAFE_CLASS_
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE;
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
bool mouse_mode_override_enabled = false;
void _mouse_update_mode();
virtual void _mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) = 0;
String im_text;
Point2i im_selection;
CursorShape cursor_shape = CURSOR_ARROW;
void initialize_tts() const;
static CGDirectDisplayID _get_display_id_for_screen(NSScreen *p_screen);
void _update_keyboard_layouts() const;
static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info);
#if defined(RD_ENABLED)
RenderingContextDriver *rendering_context = nullptr;
RenderingDevice *rendering_device = nullptr;
#endif
virtual HDROutput &_get_hdr_output(WindowID p_window) = 0;
virtual const HDROutput &_get_hdr_output(WindowID p_window) const = 0;
constexpr float _calculate_current_reference_luminance(CGFloat p_max_potential_edr_value, CGFloat p_max_edr_value) const;
void _update_hdr_output(WindowID p_window, const HDROutput &p_hdr);
public:
virtual bool tts_is_speaking() const override;
virtual bool tts_is_paused() const override;
virtual TypedArray<Dictionary> tts_get_voices() const override;
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int64_t p_utterance_id = 0, bool p_interrupt = false) override;
virtual void tts_pause() override;
virtual void tts_resume() override;
virtual void tts_stop() override;
void emit_system_theme_changed();
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() const override;
virtual Color get_base_color() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
virtual void mouse_set_mode_override(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode_override() const override;
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
virtual bool mouse_is_mode_override_enabled() const override;
virtual void clipboard_set(const String &p_text) override;
virtual String clipboard_get() const override;
virtual Ref<Image> clipboard_get_image() const override;
virtual bool clipboard_has() const override;
virtual bool clipboard_has_image() const override;
virtual int get_primary_screen() const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void screen_set_keep_on(bool p_enable) override;
virtual bool screen_is_kept_on() const override;
virtual int accessibility_should_increase_contrast() const override;
virtual int accessibility_should_reduce_animation() const override;
virtual int accessibility_should_reduce_transparency() const override;
virtual int accessibility_screen_reader_active() const override;
void update_im_text(const Point2i &p_selection, const String &p_text);
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
virtual CursorShape cursor_get_shape() const override;
virtual void beep() const override;
virtual int keyboard_get_layout_count() const override;
virtual int keyboard_get_current_layout() const override;
virtual void keyboard_set_current_layout(int p_index) override;
@ -77,21 +161,21 @@ public:
virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
virtual void show_emoji_and_symbol_picker() const override;
void emit_system_theme_changed();
virtual bool is_dark_mode_supported() const override;
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() const override;
virtual Color get_base_color() const override;
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
virtual void window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value = nullptr, CGFloat *r_max_edr_value = nullptr) const = 0;
virtual bool window_is_hdr_output_supported(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void window_request_hdr_output(const bool p_enable, WindowID p_window = MAIN_WINDOW_ID) override;
virtual bool window_is_hdr_output_requested(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool window_is_hdr_output_enabled(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual bool tts_is_speaking() const override;
virtual bool tts_is_paused() const override;
virtual TypedArray<Dictionary> tts_get_voices() const override;
virtual void window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_reference_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int64_t p_utterance_id = 0, bool p_interrupt = false) override;
virtual void tts_pause() override;
virtual void tts_resume() override;
virtual void tts_stop() override;
virtual void window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual float window_get_hdr_output_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_hdr_output_current_max_luminance(WindowID p_window = MAIN_WINDOW_ID) const override;
virtual float window_get_output_max_linear_value(WindowID p_window = MAIN_WINDOW_ID) const override;
DisplayServerMacOSBase();
~DisplayServerMacOSBase();

View file

@ -29,14 +29,73 @@
/**************************************************************************/
#import "display_server_macos_base.h"
#import "godot_application_delegate.h"
#import "key_mapping_macos.h"
#import "tts_macos.h"
#include "core/config/project_settings.h"
#include "core/os/main_loop.h"
#include "drivers/png/png_driver_common.h"
#if defined(RD_ENABLED)
#include "servers/rendering/rendering_context_driver.h"
#include "servers/rendering/rendering_device.h"
#endif
#import <Carbon/Carbon.h>
void DisplayServerMacOSBase::_mouse_update_mode() {
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
? mouse_mode_override
: mouse_mode_base;
if (wanted_mouse_mode == mouse_mode) {
return;
}
MouseMode prev_mode = mouse_mode;
mouse_mode = wanted_mouse_mode;
_mouse_apply_mode(prev_mode, wanted_mouse_mode);
}
void DisplayServerMacOSBase::mouse_set_mode(MouseMode p_mode) {
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
if (p_mode == mouse_mode_base) {
return;
}
mouse_mode_base = p_mode;
_mouse_update_mode();
}
DisplayServer::MouseMode DisplayServerMacOSBase::mouse_get_mode() const {
return mouse_mode;
}
void DisplayServerMacOSBase::mouse_set_mode_override(MouseMode p_mode) {
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
if (p_mode == mouse_mode_override) {
return;
}
mouse_mode_override = p_mode;
_mouse_update_mode();
}
DisplayServer::MouseMode DisplayServerMacOSBase::mouse_get_mode_override() const {
return mouse_mode_override;
}
void DisplayServerMacOSBase::mouse_set_mode_override_enabled(bool p_override_enabled) {
if (p_override_enabled == mouse_mode_override_enabled) {
return;
}
mouse_mode_override_enabled = p_override_enabled;
_mouse_update_mode();
}
bool DisplayServerMacOSBase::mouse_is_mode_override_enabled() const {
return mouse_mode_override_enabled;
}
void DisplayServerMacOSBase::clipboard_set(const String &p_text) {
_THREAD_SAFE_METHOD_
@ -103,6 +162,14 @@ bool DisplayServerMacOSBase::clipboard_has_image() const {
return result;
}
CGDirectDisplayID DisplayServerMacOSBase::_get_display_id_for_screen(NSScreen *p_screen) {
if (@available(macOS 26.0, *)) {
return [p_screen CGDirectDisplayID];
} else {
return [[p_screen deviceDescription][@"NSScreenNumber"] unsignedIntValue];
}
}
void DisplayServerMacOSBase::initialize_tts() const {
const_cast<DisplayServerMacOSBase *>(this)->tts = [[TTS_MacOS alloc] init];
}
@ -384,6 +451,85 @@ void DisplayServerMacOSBase::set_system_theme_change_callback(const Callable &p_
system_theme_changed = p_callable;
}
bool DisplayServerMacOSBase::screen_is_kept_on() const {
return (screen_keep_on_assertion);
}
void DisplayServerMacOSBase::screen_set_keep_on(bool p_enable) {
if (screen_keep_on_assertion) {
IOPMAssertionRelease(screen_keep_on_assertion);
screen_keep_on_assertion = kIOPMNullAssertionID;
}
if (p_enable) {
String app_name_string = GLOBAL_GET("application/config/name");
NSString *name = [NSString stringWithUTF8String:(app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data())];
NSString *reason = @"Godot Engine running with display/window/energy_saving/keep_screen_on = true";
IOPMAssertionCreateWithDescription(kIOPMAssertPreventUserIdleDisplaySleep, (__bridge CFStringRef)name, (__bridge CFStringRef)reason, (__bridge CFStringRef)reason, nullptr, 0, nullptr, &screen_keep_on_assertion);
}
}
int DisplayServerMacOSBase::accessibility_should_increase_contrast() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getHighContrast];
}
int DisplayServerMacOSBase::accessibility_should_reduce_animation() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceMotion];
}
int DisplayServerMacOSBase::accessibility_should_reduce_transparency() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getReduceTransparency];
}
int DisplayServerMacOSBase::accessibility_screen_reader_active() const {
return [(GodotApplicationDelegate *)[[NSApplication sharedApplication] delegate] getVoiceOver];
}
void DisplayServerMacOSBase::update_im_text(const Point2i &p_selection, const String &p_text) {
im_selection = p_selection;
im_text = p_text;
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
}
Point2i DisplayServerMacOSBase::ime_get_selection() const {
return im_selection;
}
String DisplayServerMacOSBase::ime_get_text() const {
return im_text;
}
DisplayServer::CursorShape DisplayServerMacOSBase::cursor_get_shape() const {
return cursor_shape;
}
void DisplayServerMacOSBase::beep() const {
NSBeep();
}
int DisplayServerMacOSBase::get_primary_screen() const {
return 0;
}
float DisplayServerMacOSBase::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
int screen_count = get_screen_count();
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
NSScreen *screen = [screenArray objectAtIndex:p_screen];
const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(_get_display_id_for_screen(screen));
const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode);
return (float)displayRefreshRate;
}
ERR_PRINT("An error occurred while trying to get the screen refresh rate.");
return SCREEN_REFRESH_RATE_FALLBACK;
}
void DisplayServerMacOSBase::emit_system_theme_changed() {
if (system_theme_changed.is_valid()) {
Variant ret;
@ -395,6 +541,138 @@ void DisplayServerMacOSBase::emit_system_theme_changed() {
}
}
// MARK: - HDR / EDR
void DisplayServerMacOSBase::_update_hdr_output(WindowID p_window, const HDROutput &p_hdr) {
#ifdef RD_ENABLED
if (!rendering_context) {
return;
}
CGFloat max_potential_edr, max_edr;
window_get_edr_values(p_window, &max_potential_edr, &max_edr);
bool desired_hdr_enabled = p_hdr.requested && max_potential_edr > 1.0f;
bool current_hdr_enabled = rendering_context->window_get_hdr_output_enabled(p_window);
if (current_hdr_enabled != desired_hdr_enabled) {
rendering_context->window_set_hdr_output_enabled(p_window, desired_hdr_enabled);
}
float reference_luminance = _calculate_current_reference_luminance(max_potential_edr, max_edr);
rendering_context->window_set_hdr_output_reference_luminance(p_window, reference_luminance);
float max_luminance = p_hdr.is_auto_max_luminance() ? max_potential_edr * HARDWARE_REFERENCE_LUMINANCE_NITS : p_hdr.max_luminance;
rendering_context->window_set_hdr_output_max_luminance(p_window, max_luminance);
#endif
}
bool DisplayServerMacOSBase::window_is_hdr_output_supported(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT)) {
return false;
}
#endif
CGFloat max_potential_edr;
window_get_edr_values(p_window, &max_potential_edr, nullptr);
return max_potential_edr > 1.0f;
}
void DisplayServerMacOSBase::window_request_hdr_output(const bool p_enabled, WindowID p_window) {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
ERR_FAIL_COND_MSG(p_enabled && rendering_device && !rendering_device->has_feature(RenderingDevice::Features::SUPPORTS_HDR_OUTPUT), "HDR output is not supported by the rendering device.");
#endif
HDROutput &hdr = _get_hdr_output(p_window);
hdr.requested = p_enabled;
_update_hdr_output(p_window, hdr);
}
bool DisplayServerMacOSBase::window_is_hdr_output_requested(WindowID p_window) const {
_THREAD_SAFE_METHOD_
return _get_hdr_output(p_window).requested;
}
bool DisplayServerMacOSBase::window_is_hdr_output_enabled(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_enabled(p_window);
}
#endif
return false;
}
void DisplayServerMacOSBase::window_set_hdr_output_reference_luminance(const float p_reference_luminance, WindowID p_window) {
ERR_PRINT_ONCE("Manually setting reference white luminance is not supported on Apple devices, as they provide a user-facing brightness setting that directly controls reference white luminance.");
}
float DisplayServerMacOSBase::window_get_hdr_output_reference_luminance(WindowID p_window) const {
return -1.0f; // Always auto-adjusted by the OS on Apple platforms.
}
constexpr float DisplayServerMacOSBase::_calculate_current_reference_luminance(CGFloat p_max_potential_edr_value, CGFloat p_max_edr_value) const {
return (p_max_potential_edr_value * HARDWARE_REFERENCE_LUMINANCE_NITS) / p_max_edr_value;
}
float DisplayServerMacOSBase::window_get_hdr_output_current_reference_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_hdr_output_reference_luminance(p_window);
}
#endif
return 200.0f;
}
void DisplayServerMacOSBase::window_set_hdr_output_max_luminance(const float p_max_luminance, WindowID p_window) {
_THREAD_SAFE_METHOD_
HDROutput &hdr = _get_hdr_output(p_window);
if (hdr.max_luminance == p_max_luminance) {
return;
}
hdr.max_luminance = p_max_luminance;
_update_hdr_output(p_window, hdr);
}
float DisplayServerMacOSBase::window_get_hdr_output_max_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
return _get_hdr_output(p_window).max_luminance;
}
float DisplayServerMacOSBase::window_get_hdr_output_current_max_luminance(WindowID p_window) const {
_THREAD_SAFE_METHOD_
const HDROutput &hdr = _get_hdr_output(p_window);
if (hdr.is_auto_max_luminance()) {
CGFloat max_potential_edr;
window_get_edr_values(p_window, &max_potential_edr, nullptr);
return max_potential_edr * HARDWARE_REFERENCE_LUMINANCE_NITS;
}
return hdr.max_luminance;
}
float DisplayServerMacOSBase::window_get_output_max_linear_value(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(RD_ENABLED)
if (rendering_context) {
return rendering_context->window_get_output_max_linear_value(p_window);
}
#endif
return 1.0f; // SDR
}
DisplayServerMacOSBase::DisplayServerMacOSBase() {
KeyMappingMacOS::initialize();
@ -404,6 +682,8 @@ DisplayServerMacOSBase::DisplayServerMacOSBase() {
initialize_tts();
}
screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on"));
// Register to be notified on keyboard layout changes.
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
nullptr, _keyboard_layout_changed,
@ -412,5 +692,10 @@ DisplayServerMacOSBase::DisplayServerMacOSBase() {
}
DisplayServerMacOSBase::~DisplayServerMacOSBase() {
if (screen_keep_on_assertion) {
IOPMAssertionRelease(screen_keep_on_assertion);
screen_keep_on_assertion = kIOPMNullAssertionID;
}
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr);
}

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* display_server_embedded.h */
/* display_server_macos_embedded.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -40,7 +40,7 @@ class NativeMenu;
class RenderingContextDriver;
class RenderingDevice;
struct DisplayServerEmbeddedState {
struct DisplayServerMacOSEmbeddedState {
/*! Default to a scale of 2.0, which is the most common. */
float screen_max_scale = 2.0f;
float screen_dpi = 96.0f;
@ -49,18 +49,24 @@ struct DisplayServerEmbeddedState {
/*! The display ID of the window which is displaying the embedded process content. */
uint32_t display_id = -1;
/*! The current maximum EDR value for the display. */
double screen_max_edr = 1.0;
/*! The maximum possible EDR value for the display. */
double screen_max_potential_edr = 1.0;
void serialize(PackedByteArray &r_data);
Error deserialize(const PackedByteArray &p_data);
_FORCE_INLINE_ bool operator==(const DisplayServerEmbeddedState &p_other) const {
return screen_max_scale == p_other.screen_max_scale && screen_dpi == p_other.screen_dpi && display_id == p_other.display_id;
_FORCE_INLINE_ bool operator==(const DisplayServerMacOSEmbeddedState &p_other) const {
return screen_max_scale == p_other.screen_max_scale && screen_dpi == p_other.screen_dpi && screen_window_scale == p_other.screen_window_scale && display_id == p_other.display_id && screen_max_edr == p_other.screen_max_edr && screen_max_potential_edr == p_other.screen_max_potential_edr;
}
};
class DisplayServerEmbedded : public DisplayServerMacOSBase {
GDSOFTCLASS(DisplayServerEmbedded, DisplayServerMacOSBase)
/// "Embedded" as in "Embedded in the Godot editor window".
class DisplayServerMacOSEmbedded : public DisplayServerMacOSBase {
GDSOFTCLASS(DisplayServerMacOSEmbedded, DisplayServerMacOSBase)
DisplayServerEmbeddedState state;
DisplayServerMacOSEmbeddedState state;
NativeMenu *native_menu = nullptr;
@ -82,24 +88,16 @@ class DisplayServerEmbedded : public DisplayServerMacOSBase {
GLManagerEmbedded *gl_manager = nullptr;
#endif
#if defined(RD_ENABLED)
RenderingContextDriver *rendering_context = nullptr;
RenderingDevice *rendering_device = nullptr;
#endif
String rendering_driver;
HDROutput hdr_output;
HDROutput &_get_hdr_output(WindowID p_window) override;
const HDROutput &_get_hdr_output(WindowID p_window) const override;
Point2i ime_last_position;
Point2i im_selection;
String im_text;
MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE;
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
bool mouse_mode_override_enabled = false;
void _mouse_update_mode();
CursorShape cursor_shape = CURSOR_ARROW;
void _mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) override;
struct Joy {
String name;
@ -134,16 +132,7 @@ public:
void send_window_event(DisplayServer::WindowEvent p_event, DisplayServer::WindowID p_id = MAIN_WINDOW_ID) const;
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
virtual void beep() const override;
// MARK: - Mouse
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
virtual void mouse_set_mode_override(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode_override() const override;
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
virtual bool mouse_is_mode_override_enabled() const override;
virtual void warp_mouse(const Point2i &p_position) override;
virtual Point2i mouse_get_position() const override;
virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
@ -154,14 +143,11 @@ public:
virtual String get_name() const override;
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
@ -216,17 +202,15 @@ public:
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
void update_im_text(const Point2i &p_selection, const String &p_text);
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
void set_state(const DisplayServerEmbeddedState &p_state);
void window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const override;
void update_screen_parameters();
void set_state(const DisplayServerMacOSEmbeddedState &p_state);
virtual void swap_buffers() override;
DisplayServerEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
~DisplayServerEmbedded();
DisplayServerMacOSEmbedded(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error);
~DisplayServerMacOSEmbedded();
};

View file

@ -1,5 +1,5 @@
/**************************************************************************/
/* display_server_embedded.mm */
/* display_server_macos_embedded.mm */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#import "display_server_embedded.h"
#import "display_server_macos_embedded.h"
#if defined(GLES3_ENABLED)
#import "embedded_gl_manager.h"
@ -60,7 +60,7 @@
#import "core/os/main_loop.h"
#import "servers/display/native_menu.h"
DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
DisplayServerMacOSEmbedded::DisplayServerMacOSEmbedded(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
EmbeddedDebugger::initialize(this);
r_error = OK; // default to OK
@ -215,7 +215,7 @@ DisplayServerEmbedded::DisplayServerEmbedded(const String &p_rendering_driver, W
}
}
DisplayServerEmbedded::~DisplayServerEmbedded() {
DisplayServerMacOSEmbedded::~DisplayServerMacOSEmbedded() {
if (native_menu) {
memdelete(native_menu);
native_menu = nullptr;
@ -243,11 +243,11 @@ DisplayServerEmbedded::~DisplayServerEmbedded() {
#endif
}
DisplayServer *DisplayServerEmbedded::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t /* p_parent_window */, Error &r_error) {
return memnew(DisplayServerEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
DisplayServer *DisplayServerMacOSEmbedded::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t /* p_parent_window */, Error &r_error) {
return memnew(DisplayServerMacOSEmbedded(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
}
Vector<String> DisplayServerEmbedded::get_rendering_drivers_func() {
Vector<String> DisplayServerMacOSEmbedded::get_rendering_drivers_func() {
Vector<String> drivers;
#if defined(VULKAN_ENABLED)
@ -263,74 +263,23 @@ Vector<String> DisplayServerEmbedded::get_rendering_drivers_func() {
return drivers;
}
void DisplayServerEmbedded::register_embedded_driver() {
void DisplayServerMacOSEmbedded::register_embedded_driver() {
register_create_function("embedded", create_func, get_rendering_drivers_func);
}
void DisplayServerEmbedded::beep() const {
NSBeep();
}
// MARK: - Mouse
void DisplayServerEmbedded::_mouse_update_mode() {
MouseMode wanted_mouse_mode = mouse_mode_override_enabled
? mouse_mode_override
: mouse_mode_base;
if (wanted_mouse_mode == mouse_mode) {
return;
}
EngineDebugger::get_singleton()->send_message("game_view:mouse_set_mode", { wanted_mouse_mode });
mouse_mode = wanted_mouse_mode;
void DisplayServerMacOSEmbedded::_mouse_apply_mode(MouseMode p_prev_mode, MouseMode p_new_mode) {
EngineDebugger::get_singleton()->send_message("game_view:mouse_set_mode", { p_new_mode });
}
void DisplayServerEmbedded::mouse_set_mode(MouseMode p_mode) {
if (p_mode == mouse_mode_base) {
return;
}
mouse_mode_base = p_mode;
_mouse_update_mode();
}
DisplayServerEmbedded::MouseMode DisplayServerEmbedded::mouse_get_mode() const {
return mouse_mode;
}
void DisplayServerEmbedded::mouse_set_mode_override(MouseMode p_mode) {
ERR_FAIL_INDEX(p_mode, MouseMode::MOUSE_MODE_MAX);
if (p_mode == mouse_mode_override) {
return;
}
mouse_mode_override = p_mode;
_mouse_update_mode();
}
DisplayServer::MouseMode DisplayServerEmbedded::mouse_get_mode_override() const {
return mouse_mode_override;
}
void DisplayServerEmbedded::mouse_set_mode_override_enabled(bool p_override_enabled) {
if (p_override_enabled == mouse_mode_override_enabled) {
return;
}
mouse_mode_override_enabled = p_override_enabled;
_mouse_update_mode();
}
bool DisplayServerEmbedded::mouse_is_mode_override_enabled() const {
return mouse_mode_override_enabled;
}
void DisplayServerEmbedded::warp_mouse(const Point2i &p_position) {
void DisplayServerMacOSEmbedded::warp_mouse(const Point2i &p_position) {
_THREAD_SAFE_METHOD_
Input::get_singleton()->set_mouse_position(p_position);
EngineDebugger::get_singleton()->send_message("game_view:warp_mouse", { p_position });
}
Point2i DisplayServerEmbedded::mouse_get_position() const {
Point2i DisplayServerMacOSEmbedded::mouse_get_position() const {
_THREAD_SAFE_METHOD_
const NSPoint mouse_pos = [NSEvent mouseLocation];
@ -350,7 +299,7 @@ Point2i DisplayServerEmbedded::mouse_get_position() const {
return Vector2i();
}
BitField<MouseButtonMask> DisplayServerEmbedded::mouse_get_button_state() const {
BitField<MouseButtonMask> DisplayServerMacOSEmbedded::mouse_get_button_state() const {
BitField<MouseButtonMask> last_button_state = MouseButtonMask::NONE;
NSUInteger buttons = [NSEvent pressedMouseButtons];
@ -374,41 +323,41 @@ BitField<MouseButtonMask> DisplayServerEmbedded::mouse_get_button_state() const
// MARK: Events
void DisplayServerEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
window_resize_callbacks[p_window] = p_callable;
}
void DisplayServerEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
window_event_callbacks[p_window] = p_callable;
}
void DisplayServerEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
input_event_callbacks[p_window] = p_callable;
}
void DisplayServerEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
input_text_callbacks[p_window] = p_callable;
}
void DisplayServerEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
// Not supported
}
void DisplayServerEmbedded::process_events() {
void DisplayServerMacOSEmbedded::process_events() {
Input *input = Input::get_singleton();
input->flush_buffered_events();
}
void DisplayServerEmbedded::_dispatch_input_events(const Ref<InputEvent> &p_event) {
void DisplayServerMacOSEmbedded::_dispatch_input_events(const Ref<InputEvent> &p_event) {
Ref<InputEventFromWindow> event_from_window = p_event;
WindowID window_id = INVALID_WINDOW_ID;
if (event_from_window.is_valid()) {
window_id = event_from_window->get_window_id();
}
DisplayServerEmbedded *ds = (DisplayServerEmbedded *)DisplayServer::get_singleton();
DisplayServerMacOSEmbedded *ds = (DisplayServerMacOSEmbedded *)DisplayServer::get_singleton();
ds->send_input_event(p_event, window_id);
}
void DisplayServerEmbedded::send_input_event(const Ref<InputEvent> &p_event, WindowID p_id) const {
void DisplayServerMacOSEmbedded::send_input_event(const Ref<InputEvent> &p_event, WindowID p_id) const {
if (p_id != INVALID_WINDOW_ID) {
const Callable *cb = input_event_callbacks.getptr(p_id);
if (cb) {
@ -421,21 +370,21 @@ void DisplayServerEmbedded::send_input_event(const Ref<InputEvent> &p_event, Win
}
}
void DisplayServerEmbedded::send_input_text(const String &p_text, WindowID p_id) const {
void DisplayServerMacOSEmbedded::send_input_text(const String &p_text, WindowID p_id) const {
const Callable *cb = input_text_callbacks.getptr(p_id);
if (cb) {
_window_callback(*cb, p_text);
}
}
void DisplayServerEmbedded::send_window_event(DisplayServer::WindowEvent p_event, WindowID p_id) const {
void DisplayServerMacOSEmbedded::send_window_event(DisplayServer::WindowEvent p_event, WindowID p_id) const {
const Callable *cb = window_event_callbacks.getptr(p_id);
if (cb) {
_window_callback(*cb, int(p_event));
}
}
void DisplayServerEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
void DisplayServerMacOSEmbedded::_window_callback(const Callable &p_callable, const Variant &p_arg) const {
if (p_callable.is_valid()) {
p_callable.call(p_arg);
}
@ -443,7 +392,7 @@ void DisplayServerEmbedded::_window_callback(const Callable &p_callable, const V
// MARK: -
bool DisplayServerEmbedded::has_feature(Feature p_feature) const {
bool DisplayServerMacOSEmbedded::has_feature(Feature p_feature) const {
switch (p_feature) {
#ifndef DISABLE_DEPRECATED
case FEATURE_GLOBAL_MENU: {
@ -456,6 +405,7 @@ bool DisplayServerEmbedded::has_feature(Feature p_feature) const {
// case FEATURE_HIDPI:
// case FEATURE_ICON:
// case FEATURE_MOUSE:
case FEATURE_HDR_OUTPUT:
case FEATURE_MOUSE_WARP:
// case FEATURE_NATIVE_DIALOG:
// case FEATURE_NATIVE_ICON:
@ -472,19 +422,15 @@ bool DisplayServerEmbedded::has_feature(Feature p_feature) const {
}
}
String DisplayServerEmbedded::get_name() const {
String DisplayServerMacOSEmbedded::get_name() const {
return "embedded";
}
int DisplayServerEmbedded::get_screen_count() const {
int DisplayServerMacOSEmbedded::get_screen_count() const {
return 1;
}
int DisplayServerEmbedded::get_primary_screen() const {
return 0;
}
Point2i DisplayServerEmbedded::screen_get_position(int p_screen) const {
Point2i DisplayServerMacOSEmbedded::screen_get_position(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
@ -494,7 +440,7 @@ Point2i DisplayServerEmbedded::screen_get_position(int p_screen) const {
return Point2i(0, 0);
}
Size2i DisplayServerEmbedded::screen_get_size(int p_screen) const {
Size2i DisplayServerMacOSEmbedded::screen_get_size(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
@ -504,7 +450,7 @@ Size2i DisplayServerEmbedded::screen_get_size(int p_screen) const {
return window_get_size(MAIN_WINDOW_ID);
}
Rect2i DisplayServerEmbedded::screen_get_usable_rect(int p_screen) const {
Rect2i DisplayServerMacOSEmbedded::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
@ -514,7 +460,7 @@ Rect2i DisplayServerEmbedded::screen_get_usable_rect(int p_screen) const {
return Rect2i(screen_get_position(p_screen), screen_get_size(p_screen));
}
int DisplayServerEmbedded::screen_get_dpi(int p_screen) const {
int DisplayServerMacOSEmbedded::screen_get_dpi(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
@ -524,7 +470,7 @@ int DisplayServerEmbedded::screen_get_dpi(int p_screen) const {
return 96;
}
float DisplayServerEmbedded::screen_get_scale(int p_screen) const {
float DisplayServerMacOSEmbedded::screen_get_scale(int p_screen) const {
_THREAD_SAFE_METHOD_
switch (p_screen) {
@ -539,95 +485,76 @@ float DisplayServerEmbedded::screen_get_scale(int p_screen) const {
}
}
float DisplayServerEmbedded::screen_get_refresh_rate(int p_screen) const {
_THREAD_SAFE_METHOD_
p_screen = _get_screen_index(p_screen);
int screen_count = get_screen_count();
ERR_FAIL_INDEX_V(p_screen, screen_count, SCREEN_REFRESH_RATE_FALLBACK);
p_screen = _get_screen_index(p_screen);
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode);
return (float)displayRefreshRate;
}
ERR_PRINT("An error occurred while trying to get the screen refresh rate.");
return SCREEN_REFRESH_RATE_FALLBACK;
}
Vector<DisplayServer::WindowID> DisplayServerEmbedded::get_window_list() const {
Vector<DisplayServer::WindowID> DisplayServerMacOSEmbedded::get_window_list() const {
Vector<DisplayServer::WindowID> list;
list.push_back(MAIN_WINDOW_ID);
return list;
}
DisplayServer::WindowID DisplayServerEmbedded::get_window_at_screen_position(const Point2i &p_position) const {
DisplayServer::WindowID DisplayServerMacOSEmbedded::get_window_at_screen_position(const Point2i &p_position) const {
return MAIN_WINDOW_ID;
}
void DisplayServerEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
window_attached_instance_id[p_window] = p_instance;
}
ObjectID DisplayServerEmbedded::window_get_attached_instance_id(WindowID p_window) const {
ObjectID DisplayServerMacOSEmbedded::window_get_attached_instance_id(WindowID p_window) const {
return window_attached_instance_id[p_window];
}
void DisplayServerEmbedded::window_set_title(const String &p_title, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_title(const String &p_title, WindowID p_window) {
// Not supported
}
int DisplayServerEmbedded::window_get_current_screen(WindowID p_window) const {
int DisplayServerMacOSEmbedded::window_get_current_screen(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(p_window != MAIN_WINDOW_ID, INVALID_SCREEN);
return 0;
}
void DisplayServerEmbedded::window_set_current_screen(int p_screen, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_current_screen(int p_screen, WindowID p_window) {
// Not supported
}
Point2i DisplayServerEmbedded::window_get_position(WindowID p_window) const {
Point2i DisplayServerMacOSEmbedded::window_get_position(WindowID p_window) const {
return Point2i();
}
Point2i DisplayServerEmbedded::window_get_position_with_decorations(WindowID p_window) const {
Point2i DisplayServerMacOSEmbedded::window_get_position_with_decorations(WindowID p_window) const {
return Point2i();
}
void DisplayServerEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_position(const Point2i &p_position, WindowID p_window) {
// Probably not supported for single window iOS app
}
void DisplayServerEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) {
void DisplayServerMacOSEmbedded::window_set_transient(WindowID p_window, WindowID p_parent) {
// Not supported
}
void DisplayServerEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_max_size(const Size2i p_size, WindowID p_window) {
// Not supported
}
Size2i DisplayServerEmbedded::window_get_max_size(WindowID p_window) const {
Size2i DisplayServerMacOSEmbedded::window_get_max_size(WindowID p_window) const {
return Size2i();
}
void DisplayServerEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_min_size(const Size2i p_size, WindowID p_window) {
// Not supported
}
Size2i DisplayServerEmbedded::window_get_min_size(WindowID p_window) const {
Size2i DisplayServerMacOSEmbedded::window_get_min_size(WindowID p_window) const {
return Size2i();
}
void DisplayServerEmbedded::window_set_size(const Size2i p_size, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_size(const Size2i p_size, WindowID p_window) {
print_line("Embedded window can't be resized.");
}
void DisplayServerEmbedded::_window_set_size(const Size2i p_size, WindowID p_window) {
void DisplayServerMacOSEmbedded::_window_set_size(const Size2i p_size, WindowID p_window) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
@ -656,7 +583,7 @@ void DisplayServerEmbedded::_window_set_size(const Size2i p_size, WindowID p_win
}
}
Size2i DisplayServerEmbedded::window_get_size(WindowID p_window) const {
Size2i DisplayServerMacOSEmbedded::window_get_size(WindowID p_window) const {
#if defined(RD_ENABLED)
if (rendering_context) {
RenderingContextDriver::SurfaceID surface = rendering_context->surface_get_from_window(p_window);
@ -674,73 +601,73 @@ Size2i DisplayServerEmbedded::window_get_size(WindowID p_window) const {
return Size2i();
}
Size2i DisplayServerEmbedded::window_get_size_with_decorations(WindowID p_window) const {
Size2i DisplayServerMacOSEmbedded::window_get_size_with_decorations(WindowID p_window) const {
return window_get_size(p_window);
}
void DisplayServerEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_mode(WindowMode p_mode, WindowID p_window) {
// Not supported
}
DisplayServer::WindowMode DisplayServerEmbedded::window_get_mode(WindowID p_window) const {
DisplayServer::WindowMode DisplayServerMacOSEmbedded::window_get_mode(WindowID p_window) const {
return WindowMode::WINDOW_MODE_WINDOWED;
}
bool DisplayServerEmbedded::window_is_maximize_allowed(WindowID p_window) const {
bool DisplayServerMacOSEmbedded::window_is_maximize_allowed(WindowID p_window) const {
return false;
}
void DisplayServerEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
if (p_flag == WINDOW_FLAG_TRANSPARENT && p_window == MAIN_WINDOW_ID) {
transparent = p_enabled;
layer.opaque = !(OS::get_singleton()->is_layered_allowed() && transparent);
}
}
bool DisplayServerEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
bool DisplayServerMacOSEmbedded::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
if (p_flag == WINDOW_FLAG_TRANSPARENT && p_window == MAIN_WINDOW_ID) {
return transparent;
}
return false;
}
void DisplayServerEmbedded::window_request_attention(WindowID p_window) {
void DisplayServerMacOSEmbedded::window_request_attention(WindowID p_window) {
// Not supported
}
void DisplayServerEmbedded::window_set_taskbar_progress_value(float p_value, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_taskbar_progress_value(float p_value, WindowID p_window) {
// Not supported.
}
void DisplayServerEmbedded::window_set_taskbar_progress_state(ProgressState p_state, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_taskbar_progress_state(ProgressState p_state, WindowID p_window) {
// Not supported.
}
void DisplayServerEmbedded::window_move_to_foreground(WindowID p_window) {
void DisplayServerMacOSEmbedded::window_move_to_foreground(WindowID p_window) {
// Not supported
}
bool DisplayServerEmbedded::window_is_focused(WindowID p_window) const {
bool DisplayServerMacOSEmbedded::window_is_focused(WindowID p_window) const {
return true;
}
float DisplayServerEmbedded::screen_get_max_scale() const {
float DisplayServerMacOSEmbedded::screen_get_max_scale() const {
return state.screen_max_scale;
}
bool DisplayServerEmbedded::window_can_draw(WindowID p_window) const {
bool DisplayServerMacOSEmbedded::window_can_draw(WindowID p_window) const {
return true;
}
bool DisplayServerEmbedded::can_any_window_draw() const {
bool DisplayServerMacOSEmbedded::can_any_window_draw() const {
return true;
}
void DisplayServerEmbedded::window_set_ime_active(const bool p_active, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_ime_active(const bool p_active, WindowID p_window) {
EngineDebugger::get_singleton()->send_message("game_view:window_set_ime_active", { p_active });
}
void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
if (p_pos == ime_last_position) {
return;
}
@ -748,7 +675,40 @@ void DisplayServerEmbedded::window_set_ime_position(const Point2i &p_pos, Window
ime_last_position = p_pos;
}
void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state) {
DisplayServerMacOSBase::HDROutput &DisplayServerMacOSEmbedded::_get_hdr_output(WindowID p_window) {
return hdr_output;
}
const DisplayServerMacOSBase::HDROutput &DisplayServerMacOSEmbedded::_get_hdr_output(WindowID p_window) const {
return hdr_output;
}
void DisplayServerMacOSEmbedded::window_get_edr_values(WindowID p_window, CGFloat *r_max_potential_edr_value, CGFloat *r_max_edr_value) const {
_THREAD_SAFE_METHOD_
#define SET_VAL(v, val) \
if (v) { \
*v = val; \
}
if (@available(macOS 10.15, *)) {
SET_VAL(r_max_potential_edr_value, state.screen_max_edr);
SET_VAL(r_max_edr_value, state.screen_max_potential_edr);
} else {
SET_VAL(r_max_potential_edr_value, 1.0);
SET_VAL(r_max_edr_value, 1.0);
}
#undef SET_VAL
}
void DisplayServerMacOSEmbedded::update_screen_parameters() {
if (hdr_output.requested) {
_update_hdr_output(MAIN_WINDOW_ID, hdr_output);
}
}
void DisplayServerMacOSEmbedded::set_state(const DisplayServerMacOSEmbeddedState &p_state) {
if (state == p_state) {
return;
}
@ -764,9 +724,12 @@ void DisplayServerEmbedded::set_state(const DisplayServerEmbeddedState &p_state)
}
#endif
}
if (hdr_output.requested) {
_update_hdr_output(MAIN_WINDOW_ID, hdr_output);
}
}
void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
void DisplayServerMacOSEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
#if defined(GLES3_ENABLED)
if (gl_manager) {
gl_manager->set_vsync_enabled(p_vsync_mode != DisplayServer::VSYNC_DISABLED);
@ -780,7 +743,7 @@ void DisplayServerEmbedded::window_set_vsync_mode(DisplayServer::VSyncMode p_vsy
#endif
}
DisplayServer::VSyncMode DisplayServerEmbedded::window_get_vsync_mode(WindowID p_window) const {
DisplayServer::VSyncMode DisplayServerMacOSEmbedded::window_get_vsync_mode(WindowID p_window) const {
_THREAD_SAFE_METHOD_
#if defined(GLES3_ENABLED)
if (gl_manager) {
@ -795,31 +758,12 @@ DisplayServer::VSyncMode DisplayServerEmbedded::window_get_vsync_mode(WindowID p
return DisplayServer::VSYNC_ENABLED;
}
void DisplayServerEmbedded::update_im_text(const Point2i &p_selection, const String &p_text) {
im_selection = p_selection;
im_text = p_text;
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
}
Point2i DisplayServerEmbedded::ime_get_selection() const {
return im_selection;
}
String DisplayServerEmbedded::ime_get_text() const {
return im_text;
}
void DisplayServerEmbedded::cursor_set_shape(CursorShape p_shape) {
void DisplayServerMacOSEmbedded::cursor_set_shape(CursorShape p_shape) {
cursor_shape = p_shape;
EngineDebugger::get_singleton()->send_message("game_view:cursor_set_shape", { p_shape });
}
DisplayServer::CursorShape DisplayServerEmbedded::cursor_get_shape() const {
return cursor_shape;
}
void DisplayServerEmbedded::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
void DisplayServerMacOSEmbedded::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
PackedByteArray data;
if (p_cursor.is_valid()) {
Ref<Image> image = _get_cursor_image_from_resource(p_cursor, p_hotspot);
@ -830,7 +774,7 @@ void DisplayServerEmbedded::cursor_set_custom_image(const Ref<Resource> &p_curso
EngineDebugger::get_singleton()->send_message("game_view:cursor_set_custom_image", { data, p_shape, p_hotspot });
}
void DisplayServerEmbedded::swap_buffers() {
void DisplayServerMacOSEmbedded::swap_buffers() {
#ifdef GLES3_ENABLED
if (gl_manager) {
gl_manager->swap_buffers();
@ -838,20 +782,22 @@ void DisplayServerEmbedded::swap_buffers() {
#endif
}
void DisplayServerEmbeddedState::serialize(PackedByteArray &r_data) {
r_data.resize(16);
void DisplayServerMacOSEmbeddedState::serialize(PackedByteArray &r_data) {
r_data.resize(32);
uint8_t *data = r_data.ptrw();
data += encode_float(screen_max_scale, data);
data += encode_float(screen_dpi, data);
data += encode_float(screen_window_scale, data);
data += encode_uint32(display_id, data);
data += encode_double(screen_max_edr, data);
data += encode_double(screen_max_potential_edr, data);
// Assert we had enough space.
DEV_ASSERT(r_data.size() >= (data - r_data.ptrw()));
}
Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) {
Error DisplayServerMacOSEmbeddedState::deserialize(const PackedByteArray &p_data) {
const uint8_t *data = p_data.ptr();
screen_max_scale = decode_float(data);
@ -861,6 +807,10 @@ Error DisplayServerEmbeddedState::deserialize(const PackedByteArray &p_data) {
screen_window_scale = decode_float(data);
data += sizeof(float);
display_id = decode_uint32(data);
data += sizeof(uint32_t);
screen_max_edr = decode_double(data);
data += sizeof(double);
screen_max_potential_edr = decode_double(data);
return OK;
}

View file

@ -30,8 +30,8 @@
#include "embedded_process_macos.h"
#include "platform/macos/display_server_embedded.h"
#include "platform/macos/display_server_macos.h"
#include "platform/macos/display_server_macos_embedded.h"
#include "core/input/input_event_codec.h"
#include "core/os/main_loop.h"
@ -127,13 +127,18 @@ void EmbeddedProcessMacOS::request_close() {
}
void EmbeddedProcessMacOS::display_state_changed() {
DisplayServerEmbeddedState state;
DisplayServerMacOSEmbeddedState state;
state.screen_max_scale = ds->screen_get_max_scale();
state.screen_dpi = ds->screen_get_dpi();
DisplayServer::WindowID wid = window->get_window_id();
state.screen_window_scale = ds->screen_get_scale(ds->window_get_current_screen(wid));
state.display_id = ds->window_get_display_id(wid);
CGFloat max_edr, max_potential_edr;
ds->window_get_edr_values(wid, &max_edr, &max_potential_edr);
state.screen_max_edr = max_edr;
state.screen_max_potential_edr = max_potential_edr;
PackedByteArray data;
state.serialize(data);
script_debugger->send_message("embed:ds_state", { data });

View file

@ -33,23 +33,23 @@
#include "core/templates/hash_map.h"
#include "core/variant/array.h"
class DisplayServerEmbedded;
class DisplayServerMacOSEmbedded;
/// @brief Singleton class to process embedded debugging message in the child process.
class EmbeddedDebugger {
inline static EmbeddedDebugger *singleton = nullptr;
EmbeddedDebugger(DisplayServerEmbedded *p_ds);
EmbeddedDebugger(DisplayServerMacOSEmbedded *p_ds);
public:
static void initialize(DisplayServerEmbedded *p_ds);
static void initialize(DisplayServerMacOSEmbedded *p_ds);
static void deinitialize();
~EmbeddedDebugger();
#ifdef DEBUG_ENABLED
private:
DisplayServerEmbedded *ds;
DisplayServerMacOSEmbedded *ds;
/// Message handler function for parse_message.
typedef Error (EmbeddedDebugger::*ParseMessageFunc)(const Array &p_args);

View file

@ -30,7 +30,7 @@
#include "embedded_debugger.h"
#include "display_server_embedded.h"
#include "display_server_macos_embedded.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/input.h"
@ -42,7 +42,7 @@
HashMap<String, EmbeddedDebugger::ParseMessageFunc> EmbeddedDebugger::parse_message_handlers;
#endif
EmbeddedDebugger::EmbeddedDebugger(DisplayServerEmbedded *p_ds) {
EmbeddedDebugger::EmbeddedDebugger(DisplayServerMacOSEmbedded *p_ds) {
singleton = this;
#ifdef DEBUG_ENABLED
@ -58,7 +58,7 @@ EmbeddedDebugger::~EmbeddedDebugger() {
singleton = nullptr;
}
void EmbeddedDebugger::initialize(DisplayServerEmbedded *p_ds) {
void EmbeddedDebugger::initialize(DisplayServerMacOSEmbedded *p_ds) {
if (EngineDebugger::is_active()) {
memnew(EmbeddedDebugger(p_ds));
}
@ -165,7 +165,7 @@ Error EmbeddedDebugger::_msg_notification(const Array &p_args) {
Error EmbeddedDebugger::_msg_ds_state(const Array &p_args) {
ERR_FAIL_COND_V_MSG(p_args.size() != 1, ERR_INVALID_PARAMETER, "Invalid number of arguments for 'ds_state' message.");
PackedByteArray data = p_args[0];
DisplayServerEmbeddedState state;
DisplayServerMacOSEmbeddedState state;
state.deserialize(data);
ds->set_state(state);
return OK;

View file

@ -32,7 +32,7 @@
#import "dir_access_macos.h"
#ifdef TOOLS_ENABLED
#import "display_server_embedded.h"
#import "display_server_macos_embedded.h"
#endif
#import "display_server_macos.h"
#import "godot_application.h"
@ -1282,9 +1282,9 @@ void OS_MacOS_Embedded::run() {
ret = Main::start();
}
DisplayServerEmbedded *ds = Object::cast_to<DisplayServerEmbedded>(DisplayServer::get_singleton());
DisplayServerMacOSEmbedded *ds = Object::cast_to<DisplayServerMacOSEmbedded>(DisplayServer::get_singleton());
if (!ds) {
ERR_FAIL_MSG("DisplayServerEmbedded is not initialized.");
ERR_FAIL_MSG("DisplayServerMacOSEmbedded is not initialized.");
}
if (ds && ret == EXIT_SUCCESS && main_loop) {
@ -1324,7 +1324,7 @@ void OS_MacOS_Embedded::run() {
OS_MacOS_Embedded::OS_MacOS_Embedded(const char *p_execpath, int p_argc, char **p_argv) :
OS_MacOS(p_execpath, p_argc, p_argv) {
DisplayServerEmbedded::register_embedded_driver();
DisplayServerMacOSEmbedded::register_embedded_driver();
}
#endif

View file

@ -51,4 +51,9 @@ public:
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
protected:
virtual bool _screen_hdr_is_supported() const override;
virtual float _screen_potential_edr_headroom() const override;
virtual float _screen_current_edr_headroom() const override;
};

View file

@ -77,3 +77,19 @@ float DisplayServerVisionOS::screen_get_scale(int p_screen) const {
return 1;
}
bool DisplayServerVisionOS::_screen_hdr_is_supported() const {
return true;
}
float DisplayServerVisionOS::_screen_potential_edr_headroom() const {
// Current Apple Vision Pro hardware supports an EDR headroom of 2.0 (two times the SDR range).
// See https://developer.apple.com/videos/play/wwdc2023/10089?time=603
return 2.0f;
}
float DisplayServerVisionOS::_screen_current_edr_headroom() const {
// Current Apple Vision Pro hardware supports an EDR headroom of 2.0 (two times the SDR range).
// See https://developer.apple.com/videos/play/wwdc2023/10089?time=603
return 2.0f;
}