diff options
Diffstat (limited to 'platform')
-rw-r--r-- | platform/android/export/export_plugin.cpp | 10 | ||||
-rw-r--r-- | platform/android/export/gradle_export_util.cpp | 58 | ||||
-rw-r--r-- | platform/android/export/gradle_export_util.h | 16 | ||||
-rw-r--r-- | platform/android/java/app/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | platform/ios/display_server_ios.h | 4 | ||||
-rw-r--r-- | platform/ios/display_server_ios.mm | 4 | ||||
-rw-r--r-- | platform/ios/godot_view.mm | 4 | ||||
-rw-r--r-- | platform/linuxbsd/detect.py | 3 | ||||
-rw-r--r-- | platform/linuxbsd/x11/display_server_x11.cpp | 65 | ||||
-rw-r--r-- | platform/linuxbsd/x11/display_server_x11.h | 4 | ||||
-rw-r--r-- | platform/macos/display_server_macos.h | 6 | ||||
-rw-r--r-- | platform/macos/display_server_macos.mm | 41 | ||||
-rw-r--r-- | platform/macos/godot_content_view.mm | 32 | ||||
-rw-r--r-- | platform/macos/godot_window_delegate.h | 2 | ||||
-rw-r--r-- | platform/macos/godot_window_delegate.mm | 65 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 58 | ||||
-rw-r--r-- | platform/windows/display_server_windows.h | 6 |
17 files changed, 276 insertions, 103 deletions
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 77cfa99aee..41d2579ac0 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -873,7 +873,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p int hand_tracking_frequency_index = p_preset->get("xr_features/hand_tracking_frequency"); bool backup_allowed = p_preset->get("user_data_backup/allow"); - bool classify_as_game = p_preset->get("package/classify_as_game"); + int app_category = p_preset->get("package/app_category"); bool retain_data_on_uninstall = p_preset->get("package/retain_data_on_uninstall"); bool exclude_from_recents = p_preset->get("package/exclude_from_recents"); bool is_resizeable = bool(GLOBAL_GET("display/window/size/resizable")); @@ -972,8 +972,12 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); } + if (tname == "application" && attrname == "appCategory") { + encode_uint32(_get_app_category_value(app_category), &p_manifest.write[iofs + 16]); + } + if (tname == "application" && attrname == "isGame") { - encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); + encode_uint32(app_category == APP_CATEGORY_GAME, &p_manifest.write[iofs + 16]); } if (tname == "application" && attrname == "hasFragileUserData") { @@ -1731,7 +1735,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "package/app_category", PROPERTY_HINT_ENUM, "Accessibility,Audio,Game,Image,Maps,News,Productivity,Social,Video"), APP_CATEGORY_GAME)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/retain_data_on_uninstall"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/exclude_from_recents"), false)); diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index 8d016d3fac..1cbed4b3bb 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -72,6 +72,54 @@ String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_or } } +int _get_app_category_value(int category_index) { + switch (category_index) { + case APP_CATEGORY_ACCESSIBILITY: + return 8; + case APP_CATEGORY_AUDIO: + return 1; + case APP_CATEGORY_IMAGE: + return 3; + case APP_CATEGORY_MAPS: + return 6; + case APP_CATEGORY_NEWS: + return 5; + case APP_CATEGORY_PRODUCTIVITY: + return 7; + case APP_CATEGORY_SOCIAL: + return 4; + case APP_CATEGORY_VIDEO: + return 2; + case APP_CATEGORY_GAME: + default: + return 0; + } +} + +String _get_app_category_label(int category_index) { + switch (category_index) { + case APP_CATEGORY_ACCESSIBILITY: + return "accessibility"; + case APP_CATEGORY_AUDIO: + return "audio"; + case APP_CATEGORY_IMAGE: + return "image"; + case APP_CATEGORY_MAPS: + return "maps"; + case APP_CATEGORY_NEWS: + return "news"; + case APP_CATEGORY_PRODUCTIVITY: + return "productivity"; + case APP_CATEGORY_SOCIAL: + return "social"; + case APP_CATEGORY_VIDEO: + return "video"; + case APP_CATEGORY_GAME: + default: + return "game"; + } +} + // Utility method used to create a directory. Error create_directory(const String &p_dir) { if (!DirAccess::exists(p_dir)) { @@ -253,21 +301,27 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { } String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_read_write_storage_permission) { + int app_category_index = (int)(p_preset->get("package/app_category")); + bool is_game = app_category_index == APP_CATEGORY_GAME; + int xr_mode_index = (int)(p_preset->get("xr_features/xr_mode")); bool uses_xr = xr_mode_index == XR_MODE_OPENXR; + String manifest_application_text = vformat( " <application android:label=\"@string/godot_project_name_string\"\n" " android:allowBackup=\"%s\"\n" " android:icon=\"@mipmap/icon\"\n" + " android:appCategory=\"%s\"\n" " android:isGame=\"%s\"\n" " android:hasFragileUserData=\"%s\"\n" " android:requestLegacyExternalStorage=\"%s\"\n" - " tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" + " tools:replace=\"android:allowBackup,android:appCategory,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n" " tools:ignore=\"GoogleAppIndexingWarning\">\n\n" " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_version_name\" />\n" " <meta-data tools:node=\"remove\" android:name=\"xr_hand_tracking_metadata_name\" />\n", bool_to_string(p_preset->get("user_data_backup/allow")), - bool_to_string(p_preset->get("package/classify_as_game")), + _get_app_category_label(app_category_index), + bool_to_string(is_game), bool_to_string(p_preset->get("package/retain_data_on_uninstall")), bool_to_string(p_has_read_write_storage_permission)); diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 232b4458c6..9c9c5923f7 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -44,6 +44,18 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut </resources> )"; +// Application category. +// See https://developer.android.com/guide/topics/manifest/application-element#appCategory for standards +static const int APP_CATEGORY_ACCESSIBILITY = 0; +static const int APP_CATEGORY_AUDIO = 1; +static const int APP_CATEGORY_GAME = 2; +static const int APP_CATEGORY_IMAGE = 3; +static const int APP_CATEGORY_MAPS = 4; +static const int APP_CATEGORY_NEWS = 5; +static const int APP_CATEGORY_PRODUCTIVITY = 6; +static const int APP_CATEGORY_SOCIAL = 7; +static const int APP_CATEGORY_VIDEO = 8; + // Supported XR modes. // This should match the entries in 'platform/android/java/lib/src/org/godotengine/godot/xr/XRMode.java' static const int XR_MODE_REGULAR = 0; @@ -73,6 +85,10 @@ int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orien String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation); +int _get_app_category_value(int category_index); + +String _get_app_category_label(int category_index); + // Utility method used to create a directory. Error create_directory(const String &p_dir); diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 1db135826a..8c8608cbbb 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -20,6 +20,7 @@ android:label="@string/godot_project_name_string" android:allowBackup="false" android:icon="@mipmap/icon" + android:appCategory="game" android:isGame="true" android:hasFragileUserData="false" android:requestLegacyExternalStorage="false" diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index 0ca0d3d1fe..82351a536b 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -105,10 +105,10 @@ public: // MARK: - Input - // MARK: Touches + // MARK: Touches and Apple Pencil void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_double_click); - void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y); + void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt); void touches_cancelled(int p_idx); // MARK: Keyboard diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index ce78b8d20f..789364494d 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -237,10 +237,12 @@ void DisplayServerIOS::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, perform_event(ev); } -void DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) { +void DisplayServerIOS::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, float p_pressure, Vector2 p_tilt) { Ref<InputEventScreenDrag> ev; ev.instantiate(); ev->set_index(p_idx); + ev->set_pressure(p_pressure); + ev->set_tilt(p_tilt); ev->set_position(Vector2(p_x, p_y)); ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y)); perform_event(ev); diff --git a/platform/ios/godot_view.mm b/platform/ios/godot_view.mm index 19cb914521..5aff75ea04 100644 --- a/platform/ios/godot_view.mm +++ b/platform/ios/godot_view.mm @@ -363,7 +363,9 @@ static const float earth_gravity = 9.80665; ERR_FAIL_COND(tid == -1); CGPoint touchPoint = [touch locationInView:self]; CGPoint prev_point = [touch previousLocationInView:self]; - DisplayServerIOS::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor); + CGFloat alt = [touch altitudeAngle]; + CGVector azim = [touch azimuthUnitVectorInView:self]; + DisplayServerIOS::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, [touch force] / [touch maximumPossibleForce], Vector2(azim.dx, azim.dy) * Math::cos(alt)); } } } diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index 844b15e9fb..747dcbd76c 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -339,9 +339,6 @@ def configure(env: "Environment"): env.Prepend(CPPPATH=["#platform/linuxbsd"]) if env["x11"]: - if not env["vulkan"]: - print("Error: X11 support requires vulkan=yes") - env.Exit(255) env.Append(CPPDEFINES=["X11_ENABLED"]) env.Append(CPPDEFINES=["UNIX_ENABLED"]) diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 57f125a754..c44c3635cf 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -1235,10 +1235,10 @@ Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const { return ret; } -DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { _THREAD_SAFE_METHOD_ - WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect); + WindowID id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_screen); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { window_set_flag(WindowFlags(i), true, id); @@ -1257,6 +1257,8 @@ void DisplayServerX11::show_window(WindowID p_id) { DEBUG_LOG_X11("show_window: %lu (%u) \n", wd.x11_window, p_id); XMapWindow(x11_display, wd.x11_window); + XSync(x11_display, False); + _validate_mode_on_map(p_id); } void DisplayServerX11::delete_sub_window(WindowID p_id) { @@ -1505,16 +1507,24 @@ void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window // Check if screen is valid ERR_FAIL_INDEX(p_screen, get_screen_count()); + if (window_get_current_screen(p_window) == p_screen) { + return; + } + if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) { Point2i position = screen_get_position(p_screen); Size2i size = screen_get_size(p_screen); XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y); } else { - if (p_screen != window_get_current_screen(p_window)) { - Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(ofs + screen_get_position(p_screen), p_window); - } + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); + Size2i wsize = window_get_size(p_window); + wpos += srect.position; + + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); + window_set_position(wpos, p_window); } } @@ -4531,7 +4541,7 @@ DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, W return ds; } -DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { //Create window XVisualInfo visualInfo; @@ -4607,8 +4617,38 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V valuemask |= CWOverrideRedirect | CWSaveUnder; } + Rect2i win_rect = p_rect; + if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + Rect2i screen_rect = Rect2i(screen_get_position(p_screen), screen_get_size(p_screen)); + + win_rect = screen_rect; + } else { + int nearest_area = 0; + int pos_screen = -1; + for (int i = 0; i < get_screen_count(); i++) { + Rect2i r; + r.position = screen_get_position(i); + r.size = screen_get_size(i); + Rect2 inters = r.intersection(p_rect); + + int area = inters.size.width * inters.size.height; + if (area > nearest_area) { + pos_screen = i; + nearest_area = area; + } + } + + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = p_rect.position - ((pos_screen >= 0) ? screen_get_position(pos_screen) : Vector2i()); + wpos += srect.position; + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3); + + win_rect.position = wpos; + } + { - wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); + wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo.screen), win_rect.position.x, win_rect.position.y, win_rect.size.width > 0 ? win_rect.size.width : 1, win_rect.size.height > 0 ? win_rect.size.height : 1, 0, visualInfo.depth, InputOutput, visualInfo.visual, valuemask, &windowAttributes); // Enable receiving notification when the window is initialized (MapNotify) // so the focus can be set at the right time. @@ -4729,13 +4769,13 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V #if defined(VULKAN_ENABLED) if (context_vulkan) { - Error err = context_vulkan->window_create(id, p_vsync_mode, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height); + Error err = context_vulkan->window_create(id, p_vsync_mode, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window"); } #endif #ifdef GLES3_ENABLED if (gl_manager) { - Error err = gl_manager->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height); + Error err = gl_manager->window_create(id, wd.x11_window, x11_display, win_rect.size.width, win_rect.size.height); ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL window"); window_set_vsync_mode(p_vsync_mode, id); } @@ -5038,12 +5078,13 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode Point2i window_position( (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + window_position += screen_get_position(0); if (p_position != nullptr) { window_position = *p_position; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution), 0); if (main_window == INVALID_WINDOW_ID) { r_error = ERR_CANT_CREATE; return; @@ -5054,8 +5095,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } } show_window(main_window); - XSync(x11_display, False); - _validate_mode_on_map(main_window); #if defined(VULKAN_ENABLED) if (rendering_driver == "vulkan") { diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 2578ca06fd..d8d914bffc 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -185,7 +185,7 @@ class DisplayServerX11 : public DisplayServer { WindowID last_focused_window = INVALID_WINDOW_ID; WindowID window_id_counter = MAIN_WINDOW_ID; - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect); + WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen); String internal_clipboard; String internal_clipboard_primary; @@ -365,7 +365,7 @@ public: virtual Vector<DisplayServer::WindowID> 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()) override; + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override; virtual void show_window(WindowID p_id) override; virtual void delete_sub_window(WindowID p_id) override; diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index bd26f6e417..4a2d6032b9 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -99,6 +99,8 @@ public: Callable drop_files_callback; ObjectID instance_id; + bool fs_transition = false; + bool initial_size = true; WindowID transient_parent = INVALID_WINDOW_ID; bool exclusive = false; @@ -189,7 +191,7 @@ private: const NSMenu *_get_menu_root(const String &p_menu_root) const; NSMenu *_get_menu_root(const String &p_menu_root); - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect); + WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect, int p_screen); void _update_window_style(WindowData p_wd); void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window); @@ -334,7 +336,7 @@ public: 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()) override; + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override; virtual void show_window(WindowID p_id) override; virtual void delete_sub_window(WindowID p_id) override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index 5c979dbf22..5421739971 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -109,7 +109,7 @@ NSMenu *DisplayServerMacOS::_get_menu_root(const String &p_menu_root) { return menu; } -DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) { +DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect, int p_screen) { WindowID id; const float scale = screen_get_max_scale(); { @@ -119,19 +119,36 @@ DisplayServerMacOS::WindowID DisplayServerMacOS::_create_window(WindowMode p_mod ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate"); [wd.window_delegate setWindowID:window_id_counter]; - Point2i position = p_rect.position; + int nearest_area = 0; + int pos_screen = -1; + for (int i = 0; i < get_screen_count(); i++) { + Rect2i r = screen_get_usable_rect(i); + Rect2 inters = r.intersection(p_rect); + int area = inters.size.width * inters.size.height; + if (area > nearest_area && area > 0) { + pos_screen = i; + nearest_area = area; + } + } + + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = p_rect.position - ((pos_screen >= 0) ? screen_get_position(pos_screen) : Vector2i()); + wpos += srect.position; + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3); // OS X native y-coordinate relative to _get_screens_origin() is negative, // Godot passes a positive value. - position.y *= -1; - position += _get_screens_origin(); + wpos.y *= -1; + wpos += _get_screens_origin(); // initWithContentRect uses bottom-left corner of the window’s frame as origin. wd.window_object = [[GodotWindow alloc] - initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale) + initWithContentRect:NSMakeRect(0, 0, p_rect.size.width / scale, p_rect.size.height / scale) styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable backing:NSBackingStoreBuffered defer:NO]; ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window"); + [wd.window_object setFrameTopLeftPoint:NSMakePoint(wpos.x / scale, wpos.y / scale)]; [wd.window_object setWindowID:window_id_counter]; wd.window_view = [[GodotContentView alloc] init]; @@ -2208,10 +2225,10 @@ Vector<DisplayServer::WindowID> DisplayServerMacOS::get_window_list() const { return ret; } -DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerMacOS::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { _THREAD_SAFE_METHOD_ - WindowID id = _create_window(p_mode, p_vsync_mode, p_rect); + WindowID id = _create_window(p_mode, p_vsync_mode, p_rect, p_screen); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { window_set_flag(WindowFlags(i), true, id); @@ -2328,8 +2345,14 @@ void DisplayServerMacOS::window_set_current_screen(int p_screen, WindowID p_wind was_fullscreen = true; } + Rect2i srect = screen_get_usable_rect(p_screen); Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(wpos + screen_get_position(p_screen), p_window); + Size2i wsize = window_get_size(p_window); + wpos += srect.position; + + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); + window_set_position(wpos, p_window); if (was_fullscreen) { // Re-enter fullscreen mode. @@ -3781,7 +3804,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM window_position = *p_position; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution), 0); ERR_FAIL_COND(main_window == INVALID_WINDOW_ID); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { diff --git a/platform/macos/godot_content_view.mm b/platform/macos/godot_content_view.mm index cb70a5db86..27afa60bed 100644 --- a/platform/macos/godot_content_view.mm +++ b/platform/macos/godot_content_view.mm @@ -65,16 +65,28 @@ if (ds && ds->has_window(window_id)) { DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); NSRect frameRect = [wd.window_object frame]; - bool left = (wd.last_frame_rect.origin.x != frameRect.origin.x); - bool top = (wd.last_frame_rect.origin.y == frameRect.origin.y); - if (left && top) { - self.layerContentsPlacement = NSViewLayerContentsPlacementBottomRight; - } else if (left && !top) { - self.layerContentsPlacement = NSViewLayerContentsPlacementTopRight; - } else if (!left && top) { - self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft; + if (wd.fs_transition || wd.initial_size) { + self.layerContentsPlacement = NSViewLayerContentsPlacementScaleAxesIndependently; + wd.initial_size = false; } else { - self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft; + bool left = (wd.last_frame_rect.origin.x != frameRect.origin.x); + bool bottom = (wd.last_frame_rect.origin.y != frameRect.origin.y); + bool right = (wd.last_frame_rect.origin.x + wd.last_frame_rect.size.width != frameRect.origin.x + frameRect.size.width); + bool top = (wd.last_frame_rect.origin.y + wd.last_frame_rect.size.height != frameRect.origin.y + frameRect.size.height); + + if (left && top) { + self.layerContentsPlacement = NSViewLayerContentsPlacementBottomRight; + } else if (left && bottom) { + self.layerContentsPlacement = NSViewLayerContentsPlacementTopRight; + } else if (left) { + self.layerContentsPlacement = NSViewLayerContentsPlacementRight; + } else if (right && top) { + self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft; + } else if (right && bottom) { + self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft; + } else if (right) { + self.layerContentsPlacement = NSViewLayerContentsPlacementLeft; + } } wd.last_frame_rect = frameRect; } @@ -440,7 +452,7 @@ NSEventSubtype subtype = [event subtype]; if (subtype == NSEventSubtypeTabletPoint) { const NSPoint p = [event tilt]; - mm->set_tilt(Vector2(p.x, p.y)); + mm->set_tilt(Vector2(p.x, -p.y)); mm->set_pen_inverted(last_pen_inverted); } else if (subtype == NSEventSubtypeTabletProximity) { // Check if using the eraser end of pen only on proximity event. diff --git a/platform/macos/godot_window_delegate.h b/platform/macos/godot_window_delegate.h index 01cc13a016..98c226aa2f 100644 --- a/platform/macos/godot_window_delegate.h +++ b/platform/macos/godot_window_delegate.h @@ -38,8 +38,6 @@ @interface GodotWindowDelegate : NSObject <NSWindowDelegate> { DisplayServer::WindowID window_id; - NSRect old_frame; - NSWindowStyleMask old_style_mask; } - (void)setWindowID:(DisplayServer::WindowID)wid; diff --git a/platform/macos/godot_window_delegate.mm b/platform/macos/godot_window_delegate.mm index 27efd3ebb2..aa5da14651 100644 --- a/platform/macos/godot_window_delegate.mm +++ b/platform/macos/godot_window_delegate.mm @@ -70,24 +70,24 @@ ds->window_destroy(window_id); } -- (NSArray<NSWindow *> *)customWindowsToEnterFullScreenForWindow:(NSWindow *)window { +- (void)windowWillEnterFullScreen:(NSNotification *)notification { DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); if (!ds || !ds->has_window(window_id)) { - return nullptr; + return; } - old_frame = [window frame]; - old_style_mask = [window styleMask]; - - NSMutableArray<NSWindow *> *windows = [[NSMutableArray alloc] init]; - [windows addObject:window]; - - return windows; + DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = true; } -- (void)window:(NSWindow *)window startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration { - [(GodotWindow *)window setAnimDuration:duration]; - [window setFrame:[[window screen] frame] display:YES animate:YES]; +- (void)windowDidFailToEnterFullScreen:(NSWindow *)window { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (!ds || !ds->has_window(window_id)) { + return; + } + + DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = false; } - (void)windowDidEnterFullScreen:(NSNotification *)notification { @@ -98,29 +98,31 @@ DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); wd.fullscreen = true; + wd.fs_transition = false; // Reset window size limits. [wd.window_object setContentMinSize:NSMakeSize(0, 0)]; [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; - [(GodotWindow *)wd.window_object setAnimDuration:-1.0f]; // Reset custom window buttons. if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) { ds->window_set_custom_window_buttons(wd, false); } - // Force window resize event. - [self windowDidResize:notification]; ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE); + + // Force window resize event and redraw. + [self windowDidResize:notification]; } -- (NSArray<NSWindow *> *)customWindowsToExitFullScreenForWindow:(NSWindow *)window { +- (void)windowWillExitFullScreen:(NSNotification *)notification { DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); if (!ds || !ds->has_window(window_id)) { - return nullptr; + return; } DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = true; // Restore custom window buttons. if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) { @@ -128,16 +130,22 @@ } ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE); - - NSMutableArray<NSWindow *> *windows = [[NSMutableArray alloc] init]; - [windows addObject:wd.window_object]; - return windows; } -- (void)window:(NSWindow *)window startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration { - [(GodotWindow *)window setAnimDuration:duration]; - [window setStyleMask:old_style_mask]; - [window setFrame:old_frame display:YES animate:YES]; +- (void)windowDidFailToExitFullScreen:(NSWindow *)window { + DisplayServerMacOS *ds = (DisplayServerMacOS *)DisplayServer::get_singleton(); + if (!ds || !ds->has_window(window_id)) { + return; + } + + DisplayServerMacOS::WindowData &wd = ds->get_window(window_id); + wd.fs_transition = false; + + if ([wd.window_object styleMask] & NSWindowStyleMaskFullSizeContentView) { + ds->window_set_custom_window_buttons(wd, false); + } + + ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_TITLEBAR_CHANGE); } - (void)windowDidExitFullScreen:(NSNotification *)notification { @@ -153,8 +161,7 @@ wd.fullscreen = false; wd.exclusive_fullscreen = false; - - [(GodotWindow *)wd.window_object setAnimDuration:-1.0f]; + wd.fs_transition = false; // Set window size limits. const float scale = ds->screen_get_max_scale(); @@ -177,7 +184,7 @@ [wd.window_object setLevel:NSFloatingWindowLevel]; } - // Force window resize event. + // Force window resize event and redraw. [self windowDidResize:notification]; } @@ -305,6 +312,8 @@ ds->update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); } + [self windowDidResize:notification]; // Emit resize event, to ensure content is resized if the window was resized while it was hidden. + ds->set_last_focused_window(window_id); ds->send_window_event(wd, DisplayServerMacOS::WINDOW_EVENT_FOCUS_IN); } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e7864ebac0..fb996ec1ff 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -625,10 +625,10 @@ DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(cons return INVALID_WINDOW_ID; } -DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { _THREAD_SAFE_METHOD_ - WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect); + WindowID window_id = _create_window(p_mode, p_vsync_mode, p_flags, p_rect, p_screen); ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window."); WindowData &wd = windows[window_id]; @@ -865,19 +865,24 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi ERR_FAIL_COND(!windows.has(p_window)); ERR_FAIL_INDEX(p_screen, get_screen_count()); + if (window_get_current_screen(p_window) == p_screen) { + return; + } const WindowData &wd = windows[p_window]; if (wd.fullscreen) { - int cs = window_get_current_screen(p_window); - if (cs == p_screen) { - return; - } Point2 pos = screen_get_position(p_screen); Size2 size = screen_get_size(p_screen); MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); } else { - Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(ofs + screen_get_position(p_screen), p_window); + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); + Size2i wsize = window_get_size(p_window); + wpos += srect.position; + + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - wsize.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); + window_set_position(wpos, p_window); } // Don't let the mouse leave the window when resizing to a smaller resolution. @@ -3534,7 +3539,7 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const } } -DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect) { +DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen) { DWORD dwExStyle; DWORD dwStyle; @@ -3548,24 +3553,37 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, WindowRect.bottom = p_rect.position.y + p_rect.size.y; if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + Rect2i screen_rect = Rect2i(screen_get_position(p_screen), screen_get_size(p_screen)); + + WindowRect.left = screen_rect.position.x; + WindowRect.right = screen_rect.position.x + screen_rect.size.x; + WindowRect.top = screen_rect.position.y; + WindowRect.bottom = screen_rect.position.y + screen_rect.size.y; + } else { int nearest_area = 0; - Rect2i screen_rect; + int pos_screen = -1; for (int i = 0; i < get_screen_count(); i++) { Rect2i r; r.position = screen_get_position(i); r.size = screen_get_size(i); Rect2 inters = r.intersection(p_rect); int area = inters.size.width * inters.size.height; - if (area >= nearest_area) { - screen_rect = r; + if (area > nearest_area) { + pos_screen = i; nearest_area = area; } } - WindowRect.left = screen_rect.position.x; - WindowRect.right = screen_rect.position.x + screen_rect.size.x; - WindowRect.top = screen_rect.position.y; - WindowRect.bottom = screen_rect.position.y + screen_rect.size.y; + Rect2i srect = screen_get_usable_rect(p_screen); + Point2i wpos = p_rect.position - ((pos_screen >= 0) ? screen_get_position(pos_screen) : Vector2i()); + wpos += srect.position; + wpos.x = CLAMP(wpos.x, srect.position.x, srect.position.x + srect.size.width - p_rect.size.width / 3); + wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - p_rect.size.height / 3); + + WindowRect.left = wpos.x; + WindowRect.right = wpos.x + p_rect.size.x; + WindowRect.top = wpos.y; + WindowRect.bottom = wpos.y + p_rect.size.y; } AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); @@ -3709,7 +3727,6 @@ WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; // UXTheme API. bool DisplayServerWindows::dark_title_available = false; bool DisplayServerWindows::ux_theme_available = false; -IsDarkModeAllowedForAppPtr DisplayServerWindows::IsDarkModeAllowedForApp = nullptr; ShouldAppsUseDarkModePtr DisplayServerWindows::ShouldAppsUseDarkMode = nullptr; GetImmersiveColorFromColorSetExPtr DisplayServerWindows::GetImmersiveColorFromColorSetEx = nullptr; GetImmersiveColorTypeFromNamePtr DisplayServerWindows::GetImmersiveColorTypeFromName = nullptr; @@ -3727,7 +3744,7 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS { } SHC_PROCESS_DPI_AWARENESS; bool DisplayServerWindows::is_dark_mode_supported() const { - return ux_theme_available && IsDarkModeAllowedForApp(); + return ux_theme_available; } bool DisplayServerWindows::is_dark_mode() const { @@ -3817,13 +3834,12 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win // Load UXTheme. HMODULE ux_theme_lib = LoadLibraryW(L"uxtheme.dll"); if (ux_theme_lib) { - IsDarkModeAllowedForApp = (IsDarkModeAllowedForAppPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(136)); ShouldAppsUseDarkMode = (ShouldAppsUseDarkModePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(132)); GetImmersiveColorFromColorSetEx = (GetImmersiveColorFromColorSetExPtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(95)); GetImmersiveColorTypeFromName = (GetImmersiveColorTypeFromNamePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(96)); GetImmersiveUserColorSetPreference = (GetImmersiveUserColorSetPreferencePtr)GetProcAddress(ux_theme_lib, MAKEINTRESOURCEA(98)); - ux_theme_available = IsDarkModeAllowedForApp && ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference; + ux_theme_available = ShouldAppsUseDarkMode && GetImmersiveColorFromColorSetEx && GetImmersiveColorTypeFromName && GetImmersiveUserColorSetPreference; if (os_ver.dwBuildNumber >= 22000) { dark_title_available = true; } @@ -3933,7 +3949,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win window_position = *p_position; } - WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); + WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution), 0); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd); diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 4702bb7765..fe67febe40 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -152,7 +152,6 @@ typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output); typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets); typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable); -typedef bool(WINAPI *IsDarkModeAllowedForAppPtr)(); typedef bool(WINAPI *ShouldAppsUseDarkModePtr)(); typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode); typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name); @@ -288,7 +287,6 @@ class DisplayServerWindows : public DisplayServer { // UXTheme API static bool dark_title_available; static bool ux_theme_available; - static IsDarkModeAllowedForAppPtr IsDarkModeAllowedForApp; static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode; static GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx; static GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName; @@ -428,7 +426,7 @@ class DisplayServerWindows : public DisplayServer { uint64_t time_since_popup = 0; Ref<Image> icon; - WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect); + WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, int p_screen); WindowID window_id_counter = MAIN_WINDOW_ID; RBMap<WindowID, WindowData> windows; @@ -524,7 +522,7 @@ public: virtual Vector<DisplayServer::WindowID> 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()) override; + virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), int p_screen = 0) override; virtual void show_window(WindowID p_window) override; virtual void delete_sub_window(WindowID p_window) override; |