[macOS, 3.2] Implement seamless display scaling.

This commit is contained in:
bruvzg 2020-07-07 21:37:05 +03:00
parent 07b24de868
commit e9ab41b71d
No known key found for this signature in database
GPG Key ID: FCED35F1CECE0D3A
9 changed files with 143 additions and 81 deletions

View File

@ -320,6 +320,14 @@ int _OS::get_screen_dpi(int p_screen) const {
return OS::get_singleton()->get_screen_dpi(p_screen); return OS::get_singleton()->get_screen_dpi(p_screen);
} }
float _OS::get_screen_scale(int p_screen) const {
return OS::get_singleton()->get_screen_scale(p_screen);
}
float _OS::get_screen_max_scale() const {
return OS::get_singleton()->get_screen_max_scale();
}
Point2 _OS::get_window_position() const { Point2 _OS::get_window_position() const {
return OS::get_singleton()->get_window_position(); return OS::get_singleton()->get_window_position();
} }
@ -1252,6 +1260,8 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_screen_position", "screen"), &_OS::get_screen_position, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_screen_position", "screen"), &_OS::get_screen_position, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_screen_size", "screen"), &_OS::get_screen_size, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_screen_size", "screen"), &_OS::get_screen_size, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_screen_dpi", "screen"), &_OS::get_screen_dpi, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_screen_dpi", "screen"), &_OS::get_screen_dpi, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_screen_scale", "screen"), &_OS::get_screen_scale, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("get_screen_max_scale"), &_OS::get_screen_max_scale);
ClassDB::bind_method(D_METHOD("get_window_position"), &_OS::get_window_position); ClassDB::bind_method(D_METHOD("get_window_position"), &_OS::get_window_position);
ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position); ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position);
ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size); ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size);

View File

@ -178,6 +178,8 @@ public:
virtual Point2 get_screen_position(int p_screen = -1) const; virtual Point2 get_screen_position(int p_screen = -1) const;
virtual Size2 get_screen_size(int p_screen = -1) const; virtual Size2 get_screen_size(int p_screen = -1) const;
virtual int get_screen_dpi(int p_screen = -1) const; virtual int get_screen_dpi(int p_screen = -1) const;
virtual float get_screen_scale(int p_screen = -1) const;
virtual float get_screen_max_scale() const;
virtual Point2 get_window_position() const; virtual Point2 get_window_position() const;
virtual void set_window_position(const Point2 &p_position); virtual void set_window_position(const Point2 &p_position);
virtual Size2 get_max_window_size() const; virtual Size2 get_max_window_size() const;

View File

@ -209,6 +209,8 @@ public:
virtual Point2 get_screen_position(int p_screen = -1) const { return Point2(); } virtual Point2 get_screen_position(int p_screen = -1) const { return Point2(); }
virtual Size2 get_screen_size(int p_screen = -1) const { return get_window_size(); } virtual Size2 get_screen_size(int p_screen = -1) const { return get_window_size(); }
virtual int get_screen_dpi(int p_screen = -1) const { return 72; } virtual int get_screen_dpi(int p_screen = -1) const { return 72; }
virtual float get_screen_scale(int p_screen = -1) const { return 1.0; }
virtual float get_screen_max_scale() const { return 1.0; };
virtual Point2 get_window_position() const { return Vector2(); } virtual Point2 get_window_position() const { return Vector2(); }
virtual void set_window_position(const Point2 &p_position) {} virtual void set_window_position(const Point2 &p_position) {}
virtual Size2 get_max_window_size() const { return Size2(); }; virtual Size2 get_max_window_size() const { return Size2(); };

View File

@ -362,6 +362,15 @@
[b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows. Returns [code]72[/code] on unsupported platforms. [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows. Returns [code]72[/code] on unsupported platforms.
</description> </description>
</method> </method>
<method name="get_screen_max_scale" qualifiers="const">
<return type="float">
</return>
<description>
Return the greatest scale factor of all screens.
[b]Note:[/b] On macOS returned value is [code]2.0[/code] if there is at least one hiDPI (Retina) screen in the system, and [code]1.0[/code] in all other cases.
[b]Note:[/b] This method is implemented on macOS.
</description>
</method>
<method name="get_screen_position" qualifiers="const"> <method name="get_screen_position" qualifiers="const">
<return type="Vector2"> <return type="Vector2">
</return> </return>
@ -371,6 +380,17 @@
Returns the position of the specified screen by index. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used. Returns the position of the specified screen by index. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used.
</description> </description>
</method> </method>
<method name="get_screen_scale" qualifiers="const">
<return type="float">
</return>
<argument index="0" name="screen" type="int" default="-1">
</argument>
<description>
Return the scale factor of the specified screen by index. If [code]screen[/code] is [/code]-1[/code] (the default value), the current screen will be used.
[b]Note:[/b] On macOS returned value is [code]2.0[/code] for hiDPI (Retina) screen, and [code]1.0[/code] for all other cases.
[b]Note:[/b] This method is implemented on macOS.
</description>
</method>
<method name="get_screen_size" qualifiers="const"> <method name="get_screen_size" qualifiers="const">
<return type="Vector2"> <return type="Vector2">
</return> </return>

View File

@ -5652,8 +5652,12 @@ EditorNode::EditorNode() {
switch (display_scale) { switch (display_scale) {
case 0: { case 0: {
// Try applying a suitable display scale automatically // Try applying a suitable display scale automatically
#ifdef OSX_ENABLED
editor_set_scale(OS::get_singleton()->get_screen_max_scale());
#else
const int screen = OS::get_singleton()->get_current_screen(); const int screen = OS::get_singleton()->get_current_screen();
editor_set_scale(OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000 ? 2.0 : 1.0); editor_set_scale(OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000 ? 2.0 : 1.0);
#endif
} break; } break;
case 1: { case 1: {

View File

@ -112,22 +112,31 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
} }
int window_placement = EditorSettings::get_singleton()->get("run/window_placement/rect"); int window_placement = EditorSettings::get_singleton()->get("run/window_placement/rect");
bool hidpi_proj = ProjectSettings::get_singleton()->get("display/window/dpi/allow_hidpi");
int display_scale = 1;
if (OS::get_singleton()->is_hidpi_allowed()) {
if (hidpi_proj) {
display_scale = 1; // Both editor and project runs in hiDPI mode, do not scale.
} else {
display_scale = OS::get_singleton()->get_screen_max_scale(); // Editor is in hiDPI mode, project is not, scale down.
}
} else {
if (hidpi_proj) {
display_scale = (1.f / OS::get_singleton()->get_screen_max_scale()); // Editor is not in hiDPI mode, project is, scale up.
} else {
display_scale = 1; // Both editor and project runs in lowDPI mode, do not scale.
}
}
screen_rect.position /= display_scale;
screen_rect.size /= display_scale;
switch (window_placement) { switch (window_placement) {
case 0: { // top left case 0: { // top left
args.push_back("--position"); args.push_back("--position");
args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y)); args.push_back(itos(screen_rect.position.x) + "," + itos(screen_rect.position.y));
} break; } break;
case 1: { // centered case 1: { // centered
int display_scale = 1; Vector2 pos = screen_rect.position + ((screen_rect.size - desired_size) / 2).floor();
#ifdef OSX_ENABLED
if (OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000) {
display_scale = 2;
}
#endif
Vector2 pos = screen_rect.position + ((screen_rect.size / display_scale - desired_size) / 2).floor();
args.push_back("--position"); args.push_back("--position");
args.push_back(itos(pos.x) + "," + itos(pos.y)); args.push_back(itos(pos.x) + "," + itos(pos.y));
} break; } break;
@ -142,10 +151,8 @@ Error EditorRun::run(const String &p_scene, const String &p_custom_args, const L
args.push_back("--position"); args.push_back("--position");
args.push_back(itos(pos.x) + "," + itos(pos.y)); args.push_back(itos(pos.x) + "," + itos(pos.y));
args.push_back("--maximized"); args.push_back("--maximized");
} break; } break;
case 4: { // force fullscreen case 4: { // force fullscreen
Vector2 pos = screen_rect.position; Vector2 pos = screen_rect.position;
args.push_back("--position"); args.push_back("--position");
args.push_back(itos(pos.x) + "," + itos(pos.y)); args.push_back(itos(pos.x) + "," + itos(pos.y));

View File

@ -2406,8 +2406,12 @@ ProjectManager::ProjectManager() {
switch (display_scale) { switch (display_scale) {
case 0: { case 0: {
// Try applying a suitable display scale automatically // Try applying a suitable display scale automatically
#ifdef OSX_ENABLED
editor_set_scale(OS::get_singleton()->get_screen_max_scale());
#else
const int screen = OS::get_singleton()->get_current_screen(); const int screen = OS::get_singleton()->get_current_screen();
editor_set_scale(OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000 ? 2.0 : 1.0); editor_set_scale(OS::get_singleton()->get_screen_dpi(screen) >= 192 && OS::get_singleton()->get_screen_size(screen).x > 2000 ? 2.0 : 1.0);
#endif
} break; } break;
case 1: editor_set_scale(0.75); break; case 1: editor_set_scale(0.75); break;
@ -2425,11 +2429,8 @@ ProjectManager::ProjectManager() {
// Define a minimum window size to prevent UI elements from overlapping or being cut off // Define a minimum window size to prevent UI elements from overlapping or being cut off
OS::get_singleton()->set_min_window_size(Size2(750, 420) * EDSCALE); OS::get_singleton()->set_min_window_size(Size2(750, 420) * EDSCALE);
#ifndef OSX_ENABLED
// The macOS platform implementation uses its own hiDPI window resizing code
// TODO: Resize windows on hiDPI displays on Windows and Linux and remove the line below // TODO: Resize windows on hiDPI displays on Windows and Linux and remove the line below
OS::get_singleton()->set_window_size(OS::get_singleton()->get_window_size() * MAX(1, EDSCALE)); OS::get_singleton()->set_window_size(OS::get_singleton()->get_window_size() * MAX(1, EDSCALE));
#endif
} }
FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files")); FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));

View File

@ -145,16 +145,6 @@ public:
CrashHandler crash_handler; CrashHandler crash_handler;
float _mouse_scale(float p_scale) {
if (_display_scale() > 1.0)
return p_scale;
else
return 1.0;
}
float _display_scale() const;
float _display_scale(id screen) const;
void _update_window(); void _update_window();
int video_driver_index; int video_driver_index;
@ -269,6 +259,8 @@ public:
virtual Point2 get_screen_position(int p_screen = -1) const; virtual Point2 get_screen_position(int p_screen = -1) const;
virtual Size2 get_screen_size(int p_screen = -1) const; virtual Size2 get_screen_size(int p_screen = -1) const;
virtual int get_screen_dpi(int p_screen = -1) const; virtual int get_screen_dpi(int p_screen = -1) const;
virtual float get_screen_scale(int p_screen = -1) const;
virtual float get_screen_max_scale() const;
virtual Point2 get_window_position() const; virtual Point2 get_window_position() const;
virtual void set_window_position(const Point2 &p_position); virtual void set_window_position(const Point2 &p_position);

View File

@ -105,11 +105,11 @@ static int mouse_y = 0;
static int button_mask = 0; static int button_mask = 0;
static bool mouse_down_control = false; static bool mouse_down_control = false;
static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFactor) { static Vector2 get_mouse_pos(NSPoint locationInWindow) {
const NSRect contentRect = [OS_OSX::singleton->window_view frame]; const NSRect contentRect = [OS_OSX::singleton->window_view frame];
const NSPoint p = locationInWindow; const NSPoint p = locationInWindow;
const float s = OS_OSX::singleton->_mouse_scale(backingScaleFactor); const float s = OS_OSX::singleton->get_screen_max_scale();
mouse_x = p.x * s; mouse_x = p.x * s;
mouse_y = (contentRect.size.height - p.y) * s; mouse_y = (contentRect.size.height - p.y) * s;
return Vector2(mouse_x, mouse_y); return Vector2(mouse_x, mouse_y);
@ -317,11 +317,11 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
OS_OSX::singleton->zoomed = false; OS_OSX::singleton->zoomed = false;
if (OS_OSX::singleton->min_size != Size2()) { if (OS_OSX::singleton->min_size != Size2()) {
Size2 size = OS_OSX::singleton->min_size / OS_OSX::singleton->_display_scale(); Size2 size = OS_OSX::singleton->min_size / OS_OSX::singleton->get_screen_max_scale();
[OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(size.x, size.y)]; [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(size.x, size.y)];
} }
if (OS_OSX::singleton->max_size != Size2()) { if (OS_OSX::singleton->max_size != Size2()) {
Size2 size = OS_OSX::singleton->max_size / OS_OSX::singleton->_display_scale(); Size2 size = OS_OSX::singleton->max_size / OS_OSX::singleton->get_screen_max_scale();
[OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
} }
@ -344,7 +344,7 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
if (newBackingScaleFactor != oldBackingScaleFactor) { if (newBackingScaleFactor != oldBackingScaleFactor) {
//Set new display scale and window size //Set new display scale and window size
float newDisplayScale = OS_OSX::singleton->is_hidpi_allowed() ? newBackingScaleFactor : 1.0; float newDisplayScale = OS_OSX::singleton->get_screen_max_scale();
const NSRect contentRect = [OS_OSX::singleton->window_view frame]; const NSRect contentRect = [OS_OSX::singleton->window_view frame];
const NSRect fbRect = contentRect; const NSRect fbRect = contentRect;
@ -352,6 +352,14 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale; OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale;
OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale; OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale;
if (OS_OSX::singleton->context) {
GLint dim[2];
dim[0] = OS_OSX::singleton->window_size.width;
dim[1] = OS_OSX::singleton->window_size.height;
CGLSetParameter((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]);
CGLEnable((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCESurfaceBackingSize);
}
//Update context //Update context
if (OS_OSX::singleton->main_loop) { if (OS_OSX::singleton->main_loop) {
//Force window resize event //Force window resize event
@ -366,10 +374,18 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
const NSRect contentRect = [OS_OSX::singleton->window_view frame]; const NSRect contentRect = [OS_OSX::singleton->window_view frame];
const NSRect fbRect = contentRect; const NSRect fbRect = contentRect;
float displayScale = OS_OSX::singleton->_display_scale(); float displayScale = OS_OSX::singleton->get_screen_max_scale();
OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale; OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale;
OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale; OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale;
if (OS_OSX::singleton->context) {
GLint dim[2];
dim[0] = OS_OSX::singleton->window_size.width;
dim[1] = OS_OSX::singleton->window_size.height;
CGLSetParameter((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]);
CGLEnable((CGLContextObj)[OS_OSX::singleton->context CGLContextObj], kCGLCESurfaceBackingSize);
}
if (OS_OSX::singleton->main_loop) { if (OS_OSX::singleton->main_loop) {
Main::force_redraw(); Main::force_redraw();
//Event retrieval blocks until resize is over. Call Main::iteration() directly. //Event retrieval blocks until resize is over. Call Main::iteration() directly.
@ -408,9 +424,7 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
- (void)windowDidBecomeKey:(NSNotification *)notification { - (void)windowDidBecomeKey:(NSNotification *)notification {
if (OS_OSX::singleton->get_main_loop()) { if (OS_OSX::singleton->get_main_loop()) {
get_mouse_pos( get_mouse_pos([OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream]);
[OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream],
[OS_OSX::singleton->window_view backingScaleFactor]);
OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y)); OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
@ -555,7 +569,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange { - (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
const NSRect contentRect = [OS_OSX::singleton->window_view frame]; const NSRect contentRect = [OS_OSX::singleton->window_view frame];
float displayScale = OS_OSX::singleton->_display_scale(); float displayScale = OS_OSX::singleton->get_screen_max_scale();
NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0); NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0);
NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin; NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
@ -678,8 +692,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
Ref<InputEventMouseButton> mb; Ref<InputEventMouseButton> mb;
mb.instance(); mb.instance();
const CGFloat backingScaleFactor = [[event window] backingScaleFactor]; const Vector2 pos = get_mouse_pos([event locationInWindow]);
const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor);
get_key_modifier_state([event modifierFlags], mb); get_key_modifier_state([event modifierFlags], mb);
mb->set_button_index(index); mb->set_button_index(index);
mb->set_pressed(pressed); mb->set_pressed(pressed);
@ -766,8 +779,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
mm.instance(); mm.instance();
mm->set_button_mask(button_mask); mm->set_button_mask(button_mask);
const CGFloat backingScaleFactor = [[event window] backingScaleFactor]; const Vector2 pos = get_mouse_pos(mpos);
const Vector2 pos = get_mouse_pos(mpos, backingScaleFactor);
mm->set_position(pos); mm->set_position(pos);
mm->set_pressure([event pressure]); mm->set_pressure([event pressure]);
if ([event subtype] == NSEventSubtypeTabletPoint) { if ([event subtype] == NSEventSubtypeTabletPoint) {
@ -776,7 +788,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
} }
mm->set_global_position(pos); mm->set_global_position(pos);
mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed()); mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed());
const Vector2 relativeMotion = Vector2(delta.x, delta.y) * OS_OSX::singleton->_mouse_scale(backingScaleFactor); const Vector2 relativeMotion = Vector2(delta.x, delta.y) * OS_OSX::singleton->get_screen_max_scale();
mm->set_relative(relativeMotion); mm->set_relative(relativeMotion);
get_key_modifier_state([event modifierFlags], mm); get_key_modifier_state([event modifierFlags], mm);
@ -855,7 +867,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
Ref<InputEventMagnifyGesture> ev; Ref<InputEventMagnifyGesture> ev;
ev.instance(); ev.instance();
get_key_modifier_state([event modifierFlags], ev); get_key_modifier_state([event modifierFlags], ev);
ev->set_position(get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor])); ev->set_position(get_mouse_pos([event locationInWindow]));
ev->set_factor([event magnification] + 1.0); ev->set_factor([event magnification] + 1.0);
OS_OSX::singleton->push_input(ev); OS_OSX::singleton->push_input(ev);
} }
@ -1336,7 +1348,7 @@ inline void sendPanEvent(double dx, double dy, int modifierFlags) {
- (void)scrollWheel:(NSEvent *)event { - (void)scrollWheel:(NSEvent *)event {
double deltaX, deltaY; double deltaX, deltaY;
get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]); get_mouse_pos([event locationInWindow]);
deltaX = [event scrollingDeltaX]; deltaX = [event scrollingDeltaX];
deltaY = [event scrollingDeltaY]; deltaY = [event scrollingDeltaY];
@ -1509,8 +1521,10 @@ static void keyboard_layout_changed(CFNotificationCenterRef center, void *observ
} }
static bool displays_arrangement_dirty = true; static bool displays_arrangement_dirty = true;
static bool displays_scale_dirty = true;
static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) { static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
displays_arrangement_dirty = true; displays_arrangement_dirty = true;
displays_scale_dirty = true;
} }
int OS_OSX::get_current_video_driver() const { int OS_OSX::get_current_video_driver() const {
@ -1525,6 +1539,7 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
keyboard_layout_dirty = true; keyboard_layout_dirty = true;
displays_arrangement_dirty = true; displays_arrangement_dirty = true;
displays_scale_dirty = true;
// Register to be notified on keyboard layout changes // Register to be notified on keyboard layout changes
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
@ -1549,8 +1564,10 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0); styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0);
} }
float displayScale = get_screen_max_scale();
window_object = [[GodotWindow alloc] window_object = [[GodotWindow alloc]
initWithContentRect:NSMakeRect(0, 0, p_desired.width, p_desired.height) initWithContentRect:NSMakeRect(0, 0, p_desired.width / displayScale, p_desired.height / displayScale)
styleMask:styleMask styleMask:styleMask
backing:NSBackingStoreBuffered backing:NSBackingStoreBuffered
defer:NO]; defer:NO];
@ -1562,17 +1579,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
[window_view setWantsLayer:TRUE]; [window_view setWantsLayer:TRUE];
} }
float displayScale = 1.0; window_size.width = p_desired.width;
if (is_hidpi_allowed()) { window_size.height = p_desired.height;
// note that mainScreen is not screen #0 but the one with the keyboard focus.
NSScreen *screen = [NSScreen mainScreen];
if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
displayScale = fmax(displayScale, [screen backingScaleFactor]);
}
}
window_size.width = p_desired.width * displayScale;
window_size.height = p_desired.height * displayScale;
if (displayScale > 1.0) { if (displayScale > 1.0) {
[window_view setWantsBestResolutionOpenGLSurface:YES]; [window_view setWantsBestResolutionOpenGLSurface:YES];
@ -1660,6 +1668,12 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
[context makeCurrentContext]; [context makeCurrentContext];
GLint dim[2];
dim[0] = window_size.width;
dim[1] = window_size.height;
CGLSetParameter((CGLContextObj)[context CGLContextObj], kCGLCPSurfaceBackingSize, &dim[0]);
CGLEnable((CGLContextObj)[context CGLContextObj], kCGLCESurfaceBackingSize);
set_use_vsync(p_desired.use_vsync); set_use_vsync(p_desired.use_vsync);
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
@ -2074,7 +2088,7 @@ void OS_OSX::warp_mouse_position(const Point2 &p_to) {
//local point in window coords //local point in window coords
const NSRect contentRect = [window_view frame]; const NSRect contentRect = [window_view frame];
float displayScale = _display_scale(); float displayScale = get_screen_max_scale();
NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0); NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin; NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
@ -2094,7 +2108,7 @@ void OS_OSX::warp_mouse_position(const Point2 &p_to) {
void OS_OSX::update_real_mouse_position() { void OS_OSX::update_real_mouse_position() {
get_mouse_pos([window_object mouseLocationOutsideOfEventStream], [window_view backingScaleFactor]); get_mouse_pos([window_object mouseLocationOutsideOfEventStream]);
input->set_mouse_position(Point2(mouse_x, mouse_y)); input->set_mouse_position(Point2(mouse_x, mouse_y));
} }
@ -2415,7 +2429,7 @@ Point2 OS_OSX::get_native_screen_position(int p_screen) const {
NSArray *screenArray = [NSScreen screens]; NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) { if ((NSUInteger)p_screen < [screenArray count]) {
float display_scale = _display_scale([screenArray objectAtIndex:p_screen]); float display_scale = get_screen_max_scale();
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
// Return the top-left corner of the screen, for OS X the y starts at the bottom // Return the top-left corner of the screen, for OS X the y starts at the bottom
return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale; return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale;
@ -2439,13 +2453,9 @@ int OS_OSX::get_screen_dpi(int p_screen) const {
NSArray *screenArray = [NSScreen screens]; NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) { if ((NSUInteger)p_screen < [screenArray count]) {
float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; NSSize displayDPI = [[description objectForKey:NSDeviceResolution] sizeValue];
CGSize displayPhysicalSize = CGDisplayScreenSize( return (displayDPI.width + displayDPI.height) / 2;
[[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale;
} }
return 72; return 72;
@ -2458,7 +2468,7 @@ Size2 OS_OSX::get_screen_size(int p_screen) const {
NSArray *screenArray = [NSScreen screens]; NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) { if ((NSUInteger)p_screen < [screenArray count]) {
float displayScale = _display_scale([screenArray objectAtIndex:p_screen]); float displayScale = get_screen_max_scale();
// Note: Use frame to get the whole screen size // Note: Use frame to get the whole screen size
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
return Size2(nsrect.size.width, nsrect.size.height) * displayScale; return Size2(nsrect.size.width, nsrect.size.height) * displayScale;
@ -2492,28 +2502,40 @@ void OS_OSX::_update_window() {
} }
} }
float OS_OSX::_display_scale() const { float OS_OSX::get_screen_scale(int p_screen) const {
if (window_object) { if (p_screen < 0) {
return _display_scale([window_object screen]); p_screen = get_current_screen();
} else {
return _display_scale([NSScreen mainScreen]);
} }
if (is_hidpi_allowed()) {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
if ([[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) {
return fmax(1.0, [[screenArray objectAtIndex:p_screen] backingScaleFactor]);
}
}
}
return 1.f;
} }
float OS_OSX::_display_scale(id screen) const { float OS_OSX::get_screen_max_scale() const {
if (is_hidpi_allowed()) { static float scale = 1.f;
if ([screen respondsToSelector:@selector(backingScaleFactor)]) { if (displays_scale_dirty) {
return fmax(1.0, [screen backingScaleFactor]); int screen_count = get_screen_count();
for (int i = 0; i < screen_count; i++) {
scale = fmax(scale, get_screen_scale(i));
} }
displays_scale_dirty = false;
} }
return 1.0; return scale;
} }
Point2 OS_OSX::get_native_window_position() const { Point2 OS_OSX::get_native_window_position() const {
NSRect nsrect = [window_object frame]; NSRect nsrect = [window_object frame];
Point2 pos; Point2 pos;
float display_scale = _display_scale(); float display_scale = get_screen_max_scale();
// Return the position of the top-left corner, for OS X the y starts at the bottom // Return the position of the top-left corner, for OS X the y starts at the bottom
pos.x = nsrect.origin.x * display_scale; pos.x = nsrect.origin.x * display_scale;
@ -2533,7 +2555,7 @@ Point2 OS_OSX::get_window_position() const {
void OS_OSX::set_native_window_position(const Point2 &p_position) { void OS_OSX::set_native_window_position(const Point2 &p_position) {
NSPoint pos; NSPoint pos;
float displayScale = _display_scale(); float displayScale = get_screen_max_scale();
pos.x = p_position.x / displayScale; pos.x = p_position.x / displayScale;
pos.y = p_position.y / displayScale; pos.y = p_position.y / displayScale;
@ -2561,7 +2583,7 @@ Size2 OS_OSX::get_window_size() const {
Size2 OS_OSX::get_real_window_size() const { Size2 OS_OSX::get_real_window_size() const {
NSRect frame = [window_object frame]; NSRect frame = [window_object frame];
return Size2(frame.size.width, frame.size.height) * _display_scale(); return Size2(frame.size.width, frame.size.height) * get_screen_max_scale();
} }
Size2 OS_OSX::get_max_window_size() const { Size2 OS_OSX::get_max_window_size() const {
@ -2581,7 +2603,7 @@ void OS_OSX::set_min_window_size(const Size2 p_size) {
min_size = p_size; min_size = p_size;
if ((min_size != Size2()) && !zoomed) { if ((min_size != Size2()) && !zoomed) {
Size2 size = min_size / _display_scale(); Size2 size = min_size / get_screen_max_scale();
[window_object setContentMinSize:NSMakeSize(size.x, size.y)]; [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
} else { } else {
[window_object setContentMinSize:NSMakeSize(0, 0)]; [window_object setContentMinSize:NSMakeSize(0, 0)];
@ -2597,7 +2619,7 @@ void OS_OSX::set_max_window_size(const Size2 p_size) {
max_size = p_size; max_size = p_size;
if ((max_size != Size2()) && !zoomed) { if ((max_size != Size2()) && !zoomed) {
Size2 size = max_size / _display_scale(); Size2 size = max_size / get_screen_max_scale();
[window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
} else { } else {
[window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
@ -2606,7 +2628,7 @@ void OS_OSX::set_max_window_size(const Size2 p_size) {
void OS_OSX::set_window_size(const Size2 p_size) { void OS_OSX::set_window_size(const Size2 p_size) {
Size2 size = p_size / _display_scale(); Size2 size = p_size / get_screen_max_scale();
if (get_borderless_window() == false) { if (get_borderless_window() == false) {
// NSRect used by setFrame includes the title bar, so add it to our size.y // NSRect used by setFrame includes the title bar, so add it to our size.y
@ -2638,11 +2660,11 @@ void OS_OSX::set_window_fullscreen(bool p_enabled) {
[window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
} else { } else {
if (min_size != Size2()) { if (min_size != Size2()) {
Size2 size = min_size / _display_scale(); Size2 size = min_size / get_screen_max_scale();
[window_object setContentMinSize:NSMakeSize(size.x, size.y)]; [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
} }
if (max_size != Size2()) { if (max_size != Size2()) {
Size2 size = max_size / _display_scale(); Size2 size = max_size / get_screen_max_scale();
[window_object setContentMaxSize:NSMakeSize(size.x, size.y)]; [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
} }
} }
@ -3202,6 +3224,8 @@ OS_OSX *OS_OSX::singleton = NULL;
OS_OSX::OS_OSX() { OS_OSX::OS_OSX() {
context = nullptr;
memset(cursors, 0, sizeof(cursors)); memset(cursors, 0, sizeof(cursors));
key_event_pos = 0; key_event_pos = 0;
mouse_mode = OS::MOUSE_MODE_VISIBLE; mouse_mode = OS::MOUSE_MODE_VISIBLE;