summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/DisplayServer.xml8
-rw-r--r--editor/editor_node.cpp70
-rw-r--r--editor/editor_node.h1
-rw-r--r--platform/android/display_server_android.cpp10
-rw-r--r--platform/android/display_server_android.h1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java8
-rw-r--r--platform/android/java_godot_io_wrapper.cpp14
-rw-r--r--platform/android/java_godot_io_wrapper.h2
-rw-r--r--platform/iphone/display_server_iphone.h1
-rw-r--r--platform/iphone/display_server_iphone.mm4
-rw-r--r--platform/javascript/display_server_javascript.cpp4
-rw-r--r--platform/javascript/display_server_javascript.h1
-rw-r--r--platform/linuxbsd/display_server_x11.cpp60
-rw-r--r--platform/linuxbsd/display_server_x11.h1
-rw-r--r--platform/osx/display_server_osx.h1
-rw-r--r--platform/osx/display_server_osx.mm18
-rw-r--r--platform/windows/display_server_windows.cpp167
-rw-r--r--platform/windows/display_server_windows.h2
-rw-r--r--servers/display_server.cpp1
-rw-r--r--servers/display_server.h3
-rw-r--r--servers/display_server_headless.h1
21 files changed, 267 insertions, 111 deletions
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 8be944b105..307dbf9d88 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -399,6 +399,14 @@
<description>
</description>
</method>
+ <method name="screen_get_refresh_rate" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="screen" type="int" default="-1" />
+ <description>
+ Returns the current refresh rate of the specified screen. If [code]screen[/code] is [code]SCREEN_OF_MAIN_WINDOW[/code] (the default value), a screen with the main window will be used.
+ [b]Note:[/b] Returns [code]60.0[/code] if the DisplayServer fails to find the refresh rate for the specified screen. On HTML5, [method screen_get_refresh_rate] will always return [code]60.0[/code] as there is no way to retrieve the refresh rate on that platform.
+ </description>
+ </method>
<method name="screen_get_scale" qualifiers="const">
<return type="float" />
<argument index="0" name="screen" type="int" default="-1" />
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 076a774efa..716d4a50c3 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -312,7 +312,7 @@ void EditorNode::_update_scene_tabs() {
DisplayServer::get_singleton()->global_menu_clear("_dock");
}
- // Get all scene names, which may be ambiguous
+ // Get all scene names, which may be ambiguous.
Vector<String> disambiguated_scene_names;
Vector<String> full_path_names;
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
@@ -349,34 +349,35 @@ void EditorNode::_update_scene_tabs() {
DisplayServer::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), callable_mp(this, &EditorNode::_global_menu_new_window));
}
- scene_tabs->set_current_tab(editor_data.get_edited_scene());
+ if (scene_tabs->get_tab_count() > 0) {
+ scene_tabs->set_current_tab(editor_data.get_edited_scene());
+ }
if (scene_tabs->get_offset_buttons_visible()) {
- // move add button to fixed position on the tabbar
+ // Move the add button to a fixed position.
if (scene_tab_add->get_parent() == scene_tabs) {
- if (scene_tabs->is_layout_rtl()) {
- scene_tab_add->set_position(Point2(tabbar_container->get_size().x - scene_tab_add->get_size().x, 0));
- } else {
- scene_tab_add->set_position(Point2(0, 0));
- }
scene_tabs->remove_child(scene_tab_add);
- tabbar_container->add_child(scene_tab_add);
- tabbar_container->move_child(scene_tab_add, 1);
+ scene_tab_add_ph->add_child(scene_tab_add);
+ scene_tab_add->set_position(Point2());
}
} else {
- // move add button to after last tab
- if (scene_tab_add->get_parent() == tabbar_container) {
- tabbar_container->remove_child(scene_tab_add);
+ // Move the add button to be after the last tab.
+ if (scene_tab_add->get_parent() == scene_tab_add_ph) {
+ scene_tab_add_ph->remove_child(scene_tab_add);
scene_tabs->add_child(scene_tab_add);
}
- Rect2 last_tab = Rect2();
- if (scene_tabs->get_tab_count() != 0) {
- last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
+
+ if (scene_tabs->get_tab_count() == 0) {
+ scene_tab_add->set_position(Point2());
+ return;
}
+
+ Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
+ int hsep = scene_tabs->get_theme_constant(SNAME("hseparation"));
if (scene_tabs->is_layout_rtl()) {
- scene_tab_add->set_position(Point2(last_tab.get_position().x - scene_tab_add->get_size().x - 3, last_tab.get_position().y));
+ scene_tab_add->set_position(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y));
} else {
- scene_tab_add->set_position(Point2(last_tab.get_position().x + last_tab.get_size().x + 3, last_tab.get_position().y));
+ scene_tab_add->set_position(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y));
}
}
}
@@ -624,6 +625,10 @@ void EditorNode::_notification(int p_what) {
editor_data.clear_edited_scenes();
} break;
+ case Control::NOTIFICATION_THEME_CHANGED: {
+ scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
+ } break;
+
case NOTIFICATION_READY: {
{
_initializing_addons = true;
@@ -6227,6 +6232,9 @@ EditorNode::EditorNode() {
tab_preview->set_position(Point2(2, 2) * EDSCALE);
tab_preview_panel->add_child(tab_preview);
+ tabbar_container = memnew(HBoxContainer);
+ srt->add_child(tabbar_container);
+
scene_tabs = memnew(TabBar);
scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles")));
scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles")));
@@ -6244,16 +6252,26 @@ EditorNode::EditorNode() {
scene_tabs->connect("gui_input", callable_mp(this, &EditorNode::_scene_tab_input));
scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorNode::_reposition_active_tab));
scene_tabs->connect("resized", callable_mp(this, &EditorNode::_update_scene_tabs));
-
- tabbar_container = memnew(HBoxContainer);
scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tabbar_container->add_child(scene_tabs);
scene_tabs_context_menu = memnew(PopupMenu);
tabbar_container->add_child(scene_tabs_context_menu);
scene_tabs_context_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
- srt->add_child(tabbar_container);
- tabbar_container->add_child(scene_tabs);
+ scene_tab_add = memnew(Button);
+ scene_tab_add->set_flat(true);
+ scene_tab_add->set_tooltip(TTR("Add a new scene."));
+ scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f));
+ scene_tabs->add_child(scene_tab_add);
+ scene_tab_add->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(FILE_NEW_SCENE));
+
+ scene_tab_add_ph = memnew(Control);
+ scene_tab_add_ph->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
+ tabbar_container->add_child(scene_tab_add_ph);
+
distraction_free = memnew(Button);
distraction_free->set_flat(true);
ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F11);
@@ -6263,15 +6281,7 @@ EditorNode::EditorNode() {
distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode));
distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons")));
distraction_free->set_toggle_mode(true);
-
- scene_tab_add = memnew(Button);
- scene_tab_add->set_flat(true);
- tabbar_container->add_child(scene_tab_add);
tabbar_container->add_child(distraction_free);
- scene_tab_add->set_tooltip(TTR("Add a new scene."));
- scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f));
- scene_tab_add->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(FILE_NEW_SCENE));
scene_root_parent = memnew(PanelContainer);
scene_root_parent->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 5c013a18d9..8d322a1bfd 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -384,6 +384,7 @@ private:
HBoxContainer *tabbar_container;
Button *distraction_free;
Button *scene_tab_add;
+ Control *scene_tab_add_ph;
bool scene_distraction;
bool script_distraction;
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 3d0dabc56e..b0f16337ed 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -161,6 +161,16 @@ int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
return godot_io_java->get_screen_dpi();
}
+float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ if (!godot_io_java) {
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+
+ return godot_io_java->get_screen_refresh_rate(SCREEN_REFRESH_RATE_FALLBACK);
+}
+
bool DisplayServerAndroid::screen_is_touchscreen(int p_screen) const {
return true;
}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index e52e07bf1a..23077a6529 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -106,6 +106,7 @@ public:
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_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index d679fd92c0..b151e7eec1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -226,6 +226,14 @@ public class GodotIO {
return (int)(metrics.density * 160f);
}
+ public double getScreenRefreshRate(double fallback) {
+ Display display = activity.getWindowManager().getDefaultDisplay();
+ if (display != null) {
+ return display.getRefreshRate();
+ }
+ return fallback;
+ }
+
public int[] screenGetUsableRect() {
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
Display display = activity.getWindowManager().getDefaultDisplay();
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index e0a535f16e..8a2788e848 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -53,6 +53,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
+ _get_screen_refresh_rate = p_env->GetMethodID(cls, "getScreenRefreshRate", "(D)D");
_screen_get_usable_rect = p_env->GetMethodID(cls, "screenGetUsableRect", "()[I"),
_get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;");
_show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;ZIII)V");
@@ -136,6 +137,19 @@ int GodotIOJavaWrapper::get_screen_dpi() {
}
}
+float GodotIOJavaWrapper::get_screen_refresh_rate(float fallback) {
+ if (_get_screen_refresh_rate) {
+ JNIEnv *env = get_jni_env();
+ if (env == nullptr) {
+ ERR_PRINT("An error occured while trying to get screen refresh rate.");
+ return fallback;
+ }
+ return (float)env->CallDoubleMethod(godot_io_instance, _get_screen_refresh_rate, (double)fallback);
+ }
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return fallback;
+}
+
void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
if (_screen_get_usable_rect) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index c96abf1101..38a2b710a9 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -51,6 +51,7 @@ private:
jmethodID _get_locale = 0;
jmethodID _get_model = 0;
jmethodID _get_screen_DPI = 0;
+ jmethodID _get_screen_refresh_rate = 0;
jmethodID _screen_get_usable_rect = 0;
jmethodID _get_unique_id = 0;
jmethodID _show_keyboard = 0;
@@ -71,6 +72,7 @@ public:
String get_locale();
String get_model();
int get_screen_dpi();
+ float get_screen_refresh_rate(float fallback);
void screen_get_usable_rect(int (&p_rect_xywh)[4]);
String get_unique_id();
bool has_vk();
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
index 6434483641..7441550f67 100644
--- a/platform/iphone/display_server_iphone.h
+++ b/platform/iphone/display_server_iphone.h
@@ -129,6 +129,7 @@ public:
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;
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index 48bda89fc3..9491c9cf90 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -393,6 +393,10 @@ int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
}
}
+float DisplayServerIPhone::screen_get_refresh_rate(int p_screen) const {
+ return [UIScreen mainScreen].maximumFramesPerSecond;
+}
+
float DisplayServerIPhone::screen_get_scale(int p_screen) const {
return [UIScreen mainScreen].nativeScale;
}
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index 2842fc2f5e..cd0ac519f6 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -794,6 +794,10 @@ float DisplayServerJavaScript::screen_get_scale(int p_screen) const {
return godot_js_display_pixel_ratio_get();
}
+float DisplayServerJavaScript::screen_get_refresh_rate(int p_screen) const {
+ return SCREEN_REFRESH_RATE_FALLBACK; // Javascript doesn't have much of a need for the screen refresh rate, and there's no native way to do so.
+}
+
Vector<DisplayServer::WindowID> DisplayServerJavaScript::get_window_list() const {
Vector<WindowID> ret;
ret.push_back(MAIN_WINDOW_ID);
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
index 1ae5d68787..b50956d91c 100644
--- a/platform/javascript/display_server_javascript.h
+++ b/platform/javascript/display_server_javascript.h
@@ -139,6 +139,7 @@ public:
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 void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
virtual void virtual_keyboard_hide() override;
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 08216b8eef..76a3eaf9f3 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -1054,6 +1054,66 @@ int DisplayServerX11::screen_get_dpi(int p_screen) const {
return 96;
}
+float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ //invalid screen?
+ ERR_FAIL_INDEX_V(p_screen, get_screen_count(), SCREEN_REFRESH_RATE_FALLBACK);
+
+ //Use xrandr to get screen refresh rate.
+ if (xrandr_ext_ok) {
+ XRRScreenResources *screen_info = XRRGetScreenResources(x11_display, windows[MAIN_WINDOW_ID].x11_window);
+ if (screen_info) {
+ RRMode current_mode = 0;
+ xrr_monitor_info *monitors = nullptr;
+
+ if (xrr_get_monitors) {
+ int count = 0;
+ monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count);
+ ERR_FAIL_INDEX_V(p_screen, count, SCREEN_REFRESH_RATE_FALLBACK);
+ } else {
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+
+ bool found_active_mode = false;
+ for (int crtc = 0; crtc < screen_info->ncrtc; crtc++) { // Loop through outputs to find which one is currently outputting.
+ XRRCrtcInfo *monitor_info = XRRGetCrtcInfo(x11_display, screen_info, screen_info->crtcs[crtc]);
+ if (monitor_info->x != monitors[p_screen].x || monitor_info->y != monitors[p_screen].y) { // If X and Y aren't the same as the monitor we're looking for, this isn't the right monitor. Continue.
+ continue;
+ }
+
+ if (monitor_info->mode != None) {
+ current_mode = monitor_info->mode;
+ found_active_mode = true;
+ break;
+ }
+ }
+
+ if (found_active_mode) {
+ for (int mode = 0; mode < screen_info->nmode; mode++) {
+ XRRModeInfo m_info = screen_info->modes[mode];
+ if (m_info.id == current_mode) {
+ return (float)m_info.dotClock / ((float)m_info.hTotal * (float)m_info.vTotal);
+ }
+ }
+ }
+
+ ERR_PRINT("An error occured while trying to get the screen refresh rate."); // We should have returned the refresh rate by now. An error must have occured.
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ } else {
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+ }
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+}
+
bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
_THREAD_SAFE_METHOD_
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index c17e120db5..2d07361deb 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -303,6 +303,7 @@ public:
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_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
#if defined(DBUS_ENABLED)
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 719a45c2a8..4fc733fe2c 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -230,6 +230,7 @@ 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 Vector<int> get_window_list() const override;
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index f7add5b688..fec1fffe43 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -2203,6 +2203,24 @@ Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
return Rect2i();
}
+float DisplayServerOSX::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_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 occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+}
+
Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const {
_THREAD_SAFE_METHOD_
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d288c27016..74de8fb663 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -326,6 +326,12 @@ typedef struct {
Rect2i rect;
} EnumRectData;
+typedef struct {
+ int count;
+ int screen;
+ float rate;
+} EnumRefreshRateData;
+
static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumSizeData *data = (EnumSizeData *)dwData;
if (data->count == data->screen) {
@@ -363,6 +369,26 @@ static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonito
return TRUE;
}
+static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumRefreshRateData *data = (EnumRefreshRateData *)dwData;
+ if (data->count == data->screen) {
+ MONITORINFOEXW minfo;
+ memset(&minfo, 0, sizeof(minfo));
+ minfo.cbSize = sizeof(minfo);
+ GetMonitorInfoW(hMonitor, &minfo);
+
+ DEVMODEW dm;
+ memset(&dm, 0, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+ EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
+
+ data->rate = dm.dmDisplayFrequency;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -446,6 +472,13 @@ int DisplayServerWindows::screen_get_dpi(int p_screen) const {
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);
return data.dpi;
}
+float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumRefreshRateData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, SCREEN_REFRESH_RATE_FALLBACK };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);
+ return data.rate;
+}
bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
#ifndef _MSC_VER
@@ -2649,98 +2682,72 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
} break;
- case WM_MOVE: {
- if (!IsIconic(windows[window_id].hWnd)) {
- int x = int16_t(LOWORD(lParam));
- int y = int16_t(HIWORD(lParam));
- windows[window_id].last_pos = Point2(x, y);
-
- if (!windows[window_id].rect_changed_callback.is_null()) {
- Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
- Variant *sizep = &size;
- Variant ret;
- Callable::CallError ce;
- windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+
+ case WM_WINDOWPOSCHANGED: {
+ Rect2i window_client_rect;
+ {
+ RECT rect;
+ GetClientRect(hWnd, &rect);
+ ClientToScreen(hWnd, (POINT *)&rect.left);
+ ClientToScreen(hWnd, (POINT *)&rect.right);
+ window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+ }
+
+ WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;
+ WindowData &window = windows[window_id];
+
+ bool rect_changed = false;
+ if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {
+ int screen_id = window_get_current_screen(window_id);
+ Size2i screen_size = screen_get_size(screen_id);
+ Point2i screen_position = screen_get_position(screen_id);
+
+ window.maximized = false;
+ window.minimized = false;
+ window.fullscreen = false;
+
+ if (IsIconic(hWnd)) {
+ window.minimized = true;
+ } else if (IsZoomed(hWnd)) {
+ window.maximized = true;
+ } else if (window_client_rect.position == screen_position && window_client_rect.size == screen_size) {
+ window.fullscreen = true;
}
- }
- } break;
- case WM_SIZE: {
- // Ignore window size change when a SIZE_MINIMIZED event is triggered.
- if (wParam != SIZE_MINIMIZED) {
- // The new width and height of the client area.
- int window_w = LOWORD(lParam);
- int window_h = HIWORD(lParam);
-
- // Set new value to the size if it isn't preserved.
- if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) {
- windows[window_id].width = window_w;
- windows[window_id].height = window_h;
+
+ if (!window.minimized) {
+ window.width = window_client_rect.size.width;
+ window.height = window_client_rect.size.height;
#if defined(VULKAN_ENABLED)
if (context_vulkan && window_created) {
- context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height);
+ context_vulkan->window_resize(window_id, window.width, window.height);
}
#endif
+ rect_changed = true;
+ }
+ }
- } else { // If the size is preserved.
- windows[window_id].preserve_window_size = false;
+ if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {
+ window.last_pos = window_client_rect.position;
+ rect_changed = true;
+ }
- // Restore the old size.
- window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id);
+ if (rect_changed) {
+ if (!window.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height);
+ const Variant *args[] = { &size };
+ Variant ret;
+ Callable::CallError ce;
+ window.rect_changed_callback.call(args, 1, ret, ce);
}
- } else { // When the window has been minimized, preserve its size.
- windows[window_id].preserve_window_size = true;
}
- // Call windows rect change callback.
- if (!windows[window_id].rect_changed_callback.is_null()) {
- Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
- Variant *size_ptr = &size;
- Variant ret;
- Callable::CallError ce;
- windows[window_id].rect_changed_callback.call((const Variant **)&size_ptr, 1, ret, ce);
- }
-
- // The window has been maximized.
- if (wParam == SIZE_MAXIMIZED) {
- windows[window_id].maximized = true;
- windows[window_id].minimized = false;
- }
- // The window has been minimized.
- else if (wParam == SIZE_MINIMIZED) {
- windows[window_id].maximized = false;
- windows[window_id].minimized = true;
- windows[window_id].preserve_window_size = false;
- }
- // The window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies.
- else if (wParam == SIZE_RESTORED) {
- windows[window_id].maximized = false;
- windows[window_id].minimized = false;
- }
-#if 0
- if (is_layered_allowed() && layered_window) {
- DeleteObject(hBitmap);
-
- RECT r;
- GetWindowRect(hWnd, &r);
- dib_size = Size2i(r.right - r.left, r.bottom - r.top);
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(BITMAPINFO));
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = dib_size.x;
- bmi.bmiHeader.biHeight = dib_size.y;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
- hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, nullptr, 0x0);
- SelectObject(hDC_dib, hBitmap);
-
- ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
- }
-#endif
+ // Return here to prevent WM_MOVE and WM_SIZE from being sent
+ // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks
+ return 0;
+
} break;
+
case WM_ENTERSIZEMOVE: {
Input::get_singleton()->release_pressed_events();
windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 803c2d4836..b73583b64a 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -326,7 +326,6 @@ class DisplayServerWindows : public DisplayServer {
Vector<Vector2> mpath;
- bool preserve_window_size = false;
bool pre_fs_valid = false;
RECT pre_fs_rect;
bool maximized = false;
@@ -458,6 +457,7 @@ public:
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_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 5ded5cf214..2cdf194320 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -380,6 +380,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("screen_get_scale", "screen"), &DisplayServer::screen_get_scale, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_is_touchscreen", "screen"), &DisplayServer::screen_is_touchscreen, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_max_scale"), &DisplayServer::screen_get_max_scale);
+ ClassDB::bind_method(D_METHOD("screen_get_refresh_rate", "screen"), &DisplayServer::screen_get_refresh_rate, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
diff --git a/servers/display_server.h b/servers/display_server.h
index 8c6586dc20..b54bba5058 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -174,6 +174,8 @@ public:
SCREEN_OF_MAIN_WINDOW = -1
};
+ const float SCREEN_REFRESH_RATE_FALLBACK = 60.0; // Returned by screen_get_refresh_rate if the method fails. Most screens are 60hz as of 2022.
+
virtual int get_screen_count() const = 0;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
@@ -188,6 +190,7 @@ public:
}
return scale;
}
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
// Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation`
diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h
index 4ef9dc622f..f74a8fad23 100644
--- a/servers/display_server_headless.h
+++ b/servers/display_server_headless.h
@@ -62,6 +62,7 @@ public:
int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return 96; /* 0 might cause issues */ }
float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return 1; }
float screen_get_max_scale() const override { return 1; }
+ float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return SCREEN_REFRESH_RATE_FALLBACK; }
Vector<DisplayServer::WindowID> get_window_list() const override { return Vector<DisplayServer::WindowID>(); }