diff options
Diffstat (limited to 'platform/windows')
-rw-r--r-- | platform/windows/SCsub | 1 | ||||
-rw-r--r-- | platform/windows/detect.py | 37 | ||||
-rw-r--r-- | platform/windows/display_server_windows.cpp | 175 | ||||
-rw-r--r-- | platform/windows/display_server_windows.h | 6 | ||||
-rw-r--r-- | platform/windows/doc_classes/EditorExportPlatformWindows.xml | 1 | ||||
-rw-r--r-- | platform/windows/export/export_plugin.cpp | 9 | ||||
-rw-r--r-- | platform/windows/gl_manager_windows_native.cpp | 24 | ||||
-rw-r--r-- | platform/windows/gl_manager_windows_native.h | 4 | ||||
-rw-r--r-- | platform/windows/os_windows.cpp | 112 | ||||
-rw-r--r-- | platform/windows/os_windows.h | 9 | ||||
-rw-r--r-- | platform/windows/windows_utils.cpp | 280 | ||||
-rw-r--r-- | platform/windows/windows_utils.h | 49 |
12 files changed, 574 insertions, 133 deletions
diff --git a/platform/windows/SCsub b/platform/windows/SCsub index cf6416b8da..159a273e70 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -17,6 +17,7 @@ common_win = [ "joypad_windows.cpp", "tts_windows.cpp", "windows_terminal_logger.cpp", + "windows_utils.cpp", "native_menu_windows.cpp", "gl_manager_windows_native.cpp", "gl_manager_windows_angle.cpp", diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 196beb423f..f34d479345 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -202,9 +202,7 @@ def get_opts(): BoolVariable("use_asan", "Use address sanitizer (ASAN)", False), BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False), BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False), - BoolVariable( - "silence_msvc", "Silence MSVC's stdout to decrease output log bloat. May hide error messages.", False - ), + BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting any errors to stderr.", True), ("angle_libs", "Path to the ANGLE static libraries", ""), # Direct3D 12 support. ( @@ -398,16 +396,35 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable. if env["silence_msvc"]: - env.Prepend(CCFLAGS=[">", "NUL"]) - env.Prepend(LINKFLAGS=[">", "NUL"]) + from tempfile import mkstemp + + old_spawn = env["SPAWN"] + + def spawn_capture(sh, escape, cmd, args, env): + # We only care about cl/link, process everything else as normal. + if args[0] not in ["cl", "link"]: + return old_spawn(sh, escape, cmd, args, env) - # "> NUL" fails if using a tempfile, circumvent by removing the argument altogether. - old_esc_func = env["TEMPFILEARGESCFUNC"] + tmp_stdout, tmp_stdout_name = mkstemp() + os.close(tmp_stdout) + args.append(f">{tmp_stdout_name}") + ret = old_spawn(sh, escape, cmd, args, env) + + try: + with open(tmp_stdout_name, "rb") as tmp_stdout: + # First line is always bloat, subsequent lines are always errors. If content + # exists after discarding the first line, safely decode & send to stderr. + tmp_stdout.readline() + content = tmp_stdout.read() + if content: + sys.stderr.write(content.decode(sys.stdout.encoding, "replace")) + os.remove(tmp_stdout_name) + except OSError: + pass - def trim_nul(arg): - return "" if arg in [">", "NUL"] else old_esc_func(arg) + return ret - env["TEMPFILEARGESCFUNC"] = trim_nul + env["SPAWN"] = spawn_capture if env["debug_crt"]: # Always use dynamic runtime, static debug CRT breaks thread_local. diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index e540b7617f..b6b713687f 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1895,7 +1895,7 @@ Size2i DisplayServerWindows::window_get_size_with_decorations(WindowID p_window) return Size2(); } -void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { +void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) { // Windows docs for window styles: // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles @@ -1909,7 +1909,17 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre if (p_fullscreen || p_borderless) { r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past. - if ((p_fullscreen && p_multiwindow_fs) || p_maximized) { + if (p_maximized) { + r_style |= WS_MAXIMIZE; + } + if (!p_fullscreen) { + r_style |= WS_SYSMENU | WS_MINIMIZEBOX; + + if (p_resizable) { + r_style |= WS_MAXIMIZEBOX; + } + } + if ((p_fullscreen && p_multiwindow_fs) || p_maximized_fs) { r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen. } } else { @@ -1945,7 +1955,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain DWORD style = 0; DWORD style_ex = 0; - _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.no_focus || wd.is_popup, style, style_ex); + _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.maximized_fs, wd.no_focus || wd.is_popup, style, style_ex); SetWindowLongPtr(wd.hWnd, GWL_STYLE, style); SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex); @@ -1988,6 +1998,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) wd.pre_fs_valid = true; } + ShowWindow(wd.hWnd, SW_RESTORE); MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); if (restore_mouse_trails > 1) { @@ -2023,7 +2034,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) } if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) { - if (wd.minimized) { + if (wd.minimized || wd.maximized) { ShowWindow(wd.hWnd, SW_RESTORE); } wd.was_maximized = wd.maximized; @@ -2951,7 +2962,7 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { } void DisplayServerWindows::process_events() { - _THREAD_SAFE_METHOD_ + _THREAD_SAFE_LOCK_ MSG msg; @@ -2966,7 +2977,10 @@ void DisplayServerWindows::process_events() { if (!drop_events) { _process_key_events(); + _THREAD_SAFE_UNLOCK_ Input::get_singleton()->flush_buffered_events(); + } else { + _THREAD_SAFE_UNLOCK_ } } @@ -2979,9 +2993,14 @@ void DisplayServerWindows::force_process_and_drop_events() { } void DisplayServerWindows::release_rendering_thread() { -} - -void DisplayServerWindows::make_rendering_thread() { +#if defined(GLES3_ENABLED) + if (gl_manager_angle) { + gl_manager_angle->release_current(); + } + if (gl_manager_native) { + gl_manager_native->release_current(); + } +#endif } void DisplayServerWindows::swap_buffers() { @@ -3422,7 +3441,6 @@ void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event } void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) { - _THREAD_SAFE_METHOD_ if (in_dispatch_input_event) { return; } @@ -3709,63 +3727,18 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return MA_NOACTIVATE; // Do not activate, but process mouse messages. } } break; - case WM_SETFOCUS: { - windows[window_id].window_has_focus = true; - last_focused_window = window_id; - - // Restore mouse mode. - _set_mouse_mode_impl(mouse_mode); - - if (!app_focused) { - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN); - } - app_focused = true; - } - } break; - case WM_KILLFOCUS: { - windows[window_id].window_has_focus = false; - last_focused_window = window_id; - - // Release capture unconditionally because it can be set due to dragging, in addition to captured mode. - ReleaseCapture(); - - // Release every touch to avoid sticky points. - for (const KeyValue<int, Vector2> &E : touch_state) { - _touch_event(window_id, false, E.value.x, E.value.y, E.key); - } - touch_state.clear(); - - bool self_steal = false; - HWND new_hwnd = (HWND)wParam; - if (IsWindow(new_hwnd)) { - self_steal = true; + case WM_ACTIVATEAPP: { + bool new_app_focused = (bool)wParam; + if (new_app_focused == app_focused) { + break; } - - if (!self_steal) { - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); - } - app_focused = false; + app_focused = new_app_focused; + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(app_focused ? MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); } } break; - case WM_ACTIVATE: { // Watch for window activate message. - if (!windows[window_id].window_focused) { - _process_activate_event(window_id, wParam, lParam); - } else { - windows[window_id].saved_wparam = wParam; - windows[window_id].saved_lparam = lParam; - - // Run a timer to prevent event catching warning if the focused window is closing. - windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); - } - if (wParam != WA_INACTIVE) { - track_mouse_leave_event(hWnd); - - if (!IsIconic(hWnd)) { - SetFocus(hWnd); - } - } + case WM_ACTIVATE: { + _process_activate_event(window_id, wParam, lParam); return 0; // Return to the message loop. } break; case WM_GETMINMAXINFO: { @@ -3782,6 +3755,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x; min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y; } + if (windows[window_id].borderless) { + Rect2i screen_rect = screen_get_usable_rect(window_get_current_screen(window_id)); + + // Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present. + min_max_info->ptMaxPosition.x = screen_rect.position.x; + min_max_info->ptMaxPosition.y = screen_rect.position.y; + min_max_info->ptMaxSize.x = screen_rect.size.x; + min_max_info->ptMaxSize.y = screen_rect.size.y; + } return 0; } } break; @@ -3833,9 +3815,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA case SC_MONITORPOWER: // Monitor trying to enter powersave? return 0; // Prevent from happening. case SC_KEYMENU: - if ((lParam >> 16) <= 0) { + Engine *engine = Engine::get_singleton(); + if (((lParam >> 16) <= 0) && !engine->is_project_manager_hint() && !engine->is_editor_hint() && !GLOBAL_GET("application/run/enable_alt_space_menu")) { + return 0; + } + if (!alt_mem || !(GetAsyncKeyState(VK_SPACE) & (1 << 15))) { return 0; } + SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_SPACE, 0); + SendMessage(windows[window_id].hWnd, WM_SYSKEYUP, VK_MENU, 0); } } break; case WM_INDICATOR_CALLBACK_MESSAGE: { @@ -3864,9 +3852,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_CLOSE: // Did we receive a close message? { - if (windows[window_id].focus_timer_id != 0U) { - KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id); - } _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); return 0; // Jump back. @@ -3978,7 +3963,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } mm->set_relative_screen_position(mm->get_relative()); - if ((windows[window_id].window_has_focus || windows[window_id].is_popup) && mm->get_relative() != Vector2()) { + if ((windows[window_id].window_focused || windows[window_id].is_popup) && mm->get_relative() != Vector2()) { Input::get_singleton()->parse_input_event(mm); } } @@ -4026,7 +4011,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA windows[window_id].last_pen_inverted = inverted; // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. - if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) { + if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) { break; } @@ -4076,7 +4061,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative_screen_position(mm->get_relative()); old_x = mm->get_position().x; old_y = mm->get_position().y; - if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { + if (windows[window_id].window_focused || window_get_active_popup() == window_id) { Input::get_singleton()->parse_input_event(mm); } } @@ -4162,7 +4147,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. - if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) { + if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) { break; } @@ -4225,7 +4210,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative_screen_position(mm->get_relative()); old_x = mm->get_position().x; old_y = mm->get_position().y; - if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { + if (windows[window_id].window_focused || window_get_active_popup() == window_id) { Input::get_singleton()->parse_input_event(mm); } @@ -4277,7 +4262,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. - if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) { + if (!windows[window_id].window_focused && mouse_mode == MOUSE_MODE_CAPTURED) { break; } @@ -4569,10 +4554,23 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA window.minimized = true; } else if (IsZoomed(hWnd)) { window.maximized = true; + + // If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs. + if (!window.maximized_fs && window.borderless && window_rect.position == screen_position && window_rect.size == screen_size) { + // Window (borderless) was just maximized and the covers the entire screen. + window.maximized_fs = true; + _update_window_style(window_id, false); + } } else if (window_rect.position == screen_position && window_rect.size == screen_size) { window.fullscreen = true; } + if (window.maximized_fs && !window.maximized) { + // Window (maximized and covering fullscreen) was just non-maximized. + window.maximized_fs = false; + _update_window_style(window_id, false); + } + if (!window.minimized) { window.width = window_client_rect.size.width; window.height = window_client_rect.size.height; @@ -4625,10 +4623,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA if (!Main::is_iterating()) { Main::iteration(); } - } else if (wParam == windows[window_id].focus_timer_id) { - _process_activate_event(window_id, windows[window_id].saved_wparam, windows[window_id].saved_lparam); - KillTimer(windows[window_id].hWnd, wParam); - windows[window_id].focus_timer_id = 0U; } } break; case WM_SYSKEYUP: @@ -4777,7 +4771,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } break; case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) { - if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) { + if (windows[window_id].window_focused && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) { // Hide the cursor. if (hCursor == nullptr) { hCursor = SetCursor(nullptr); @@ -4834,20 +4828,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) { if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { - _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN); - windows[p_window_id].window_focused = true; + last_focused_window = p_window_id; alt_mem = false; control_mem = false; shift_mem = false; gr_mem = false; - - // Restore mouse mode. _set_mouse_mode_impl(mouse_mode); + if (!IsIconic(windows[p_window_id].hWnd)) { + SetFocus(windows[p_window_id].hWnd); + } + windows[p_window_id].window_focused = true; + _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN); } else { // WM_INACTIVE. Input::get_singleton()->release_pressed_events(); - _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); - windows[p_window_id].window_focused = false; + track_mouse_leave_event(windows[p_window_id].hWnd); + // Release capture unconditionally because it can be set due to dragging, in addition to captured mode. + ReleaseCapture(); alt_mem = false; + windows[p_window_id].window_focused = false; + _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); } if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window_id].wtctx) { @@ -5051,7 +5050,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, DWORD dwExStyle; DWORD dwStyle; - _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); + _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, false, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) | (p_flags & WINDOW_FLAG_POPUP), dwStyle, dwExStyle); RECT WindowRect; @@ -5284,6 +5283,12 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, wd.height = p_rect.size.height; } + // Set size of maximized borderless window (by default it covers the entire screen). + if (p_mode == WINDOW_MODE_MAXIMIZED && (p_flags & WINDOW_FLAG_BORDERLESS_BIT)) { + Rect2i srect = screen_get_usable_rect(rq_screen); + SetWindowPos(wd.hWnd, HWND_TOP, srect.position.x, srect.position.y, srect.size.width, srect.size.height, SWP_NOZORDER | SWP_NOACTIVATE); + } + window_id_counter++; } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 1191f22968..2fe1b0733d 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -382,6 +382,7 @@ class DisplayServerWindows : public DisplayServer { bool pre_fs_valid = false; RECT pre_fs_rect; bool maximized = false; + bool maximized_fs = false; bool minimized = false; bool fullscreen = false; bool multiwindow_fs = false; @@ -391,7 +392,6 @@ class DisplayServerWindows : public DisplayServer { bool was_maximized = false; bool always_on_top = false; bool no_focus = false; - bool window_has_focus = false; bool exclusive = false; bool context_created = false; bool mpass = false; @@ -402,7 +402,6 @@ class DisplayServerWindows : public DisplayServer { // Timers. uint32_t move_timer_id = 0U; - uint32_t focus_timer_id = 0U; HANDLE wtctx; LOGCONTEXTW wtlc; @@ -472,7 +471,7 @@ class DisplayServerWindows : public DisplayServer { HashMap<IndicatorID, IndicatorData> indicators; void _send_window_event(const WindowData &wd, WindowEvent p_event); - void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); + void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex); MouseMode mouse_mode; int restore_mouse_trails = 0; @@ -680,7 +679,6 @@ public: virtual void force_process_and_drop_events() override; virtual void release_rendering_thread() override; - virtual void make_rendering_thread() override; virtual void swap_buffers() override; virtual void set_native_icon(const String &p_filename) override; diff --git a/platform/windows/doc_classes/EditorExportPlatformWindows.xml b/platform/windows/doc_classes/EditorExportPlatformWindows.xml index 1239a2b32f..06b272c10e 100644 --- a/platform/windows/doc_classes/EditorExportPlatformWindows.xml +++ b/platform/windows/doc_classes/EditorExportPlatformWindows.xml @@ -4,6 +4,7 @@ Exporter for Windows. </brief_description> <description> + The Windows exporter customizes how a Windows build is handled. In the editor's "Export" window, it is created when adding a new "Windows" preset. </description> <tutorials> <link title="Exporting for Windows">$DOCS_URL/tutorials/export/exporting_for_windows.html</link> diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index fe2a930cc8..a3c86611a4 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -367,11 +367,17 @@ String EditorExportPlatformWindows::get_export_option_warning(const EditorExport } bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const { + if (p_preset == nullptr) { + return true; + } + // This option is not supported by "osslsigncode", used on non-Windows host. if (!OS::get_singleton()->has_feature("windows") && p_option == "codesign/identity_type") { return false; } + bool advanced_options_enabled = p_preset->are_advanced_options_enabled(); + // Hide codesign. bool codesign = p_preset->get("codesign/enable"); if (!codesign && p_option != "codesign/enable" && p_option.begins_with("codesign/")) { @@ -390,6 +396,9 @@ bool EditorExportPlatformWindows::get_export_option_visibility(const EditorExpor return false; } + if (p_option == "dotnet/embed_build_outputs") { + return advanced_options_enabled; + } return true; } diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp index da837b3f94..80cf818199 100644 --- a/platform/windows/gl_manager_windows_native.cpp +++ b/platform/windows/gl_manager_windows_native.cpp @@ -427,10 +427,6 @@ Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id return OK; } -void GLManagerNative_Windows::_internal_set_current_window(GLWindow *p_win) { - _current_window = p_win; -} - void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id) { GLWindow &win = get_window(p_window_id); if (_current_window == &win) { @@ -447,6 +443,8 @@ void GLManagerNative_Windows::release_current() { if (!gd_wglMakeCurrent(_current_window->hDC, nullptr)) { ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError())); } + + _current_window = nullptr; } void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_window_id) { @@ -467,17 +465,7 @@ void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_wind ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError())); } - _internal_set_current_window(&win); -} - -void GLManagerNative_Windows::make_current() { - if (!_current_window) { - return; - } - const GLDisplay &disp = get_current_display(); - if (!gd_wglMakeCurrent(_current_window->hDC, disp.hRC)) { - ERR_PRINT("Could not switch OpenGL context to window marked current: " + format_error_message(GetLastError())); - } + _current_window = &win; } void GLManagerNative_Windows::swap_buffers() { @@ -491,7 +479,6 @@ Error GLManagerNative_Windows::initialize() { void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use) { GLWindow &win = get_window(p_window_id); - GLWindow *current = _current_window; if (&win != _current_window) { window_make_current(p_window_id); @@ -506,11 +493,6 @@ void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, } else { WARN_PRINT("Could not set V-Sync mode. V-Sync is not supported."); } - - if (current != _current_window) { - _current_window = current; - make_current(); - } } bool GLManagerNative_Windows::is_using_vsync(DisplayServer::WindowID p_window_id) const { diff --git a/platform/windows/gl_manager_windows_native.h b/platform/windows/gl_manager_windows_native.h index 829c53b3d2..b4e2a3acdf 100644 --- a/platform/windows/gl_manager_windows_native.h +++ b/platform/windows/gl_manager_windows_native.h @@ -68,9 +68,6 @@ private: PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; - // funcs - void _internal_set_current_window(GLWindow *p_win); - GLWindow &get_window(unsigned int id) { return _windows[id]; } const GLWindow &get_window(unsigned int id) const { return _windows[id]; } @@ -91,7 +88,6 @@ public: void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {} void release_current(); - void make_current(); void swap_buffers(); void window_make_current(DisplayServer::WindowID p_window_id); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index c39b327953..abed93d414 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -34,6 +34,7 @@ #include "joypad_windows.h" #include "lang_table.h" #include "windows_terminal_logger.h" +#include "windows_utils.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" @@ -269,6 +270,10 @@ void OS_Windows::finalize() { } void OS_Windows::finalize_core() { + while (!temp_libraries.is_empty()) { + _remove_temp_library(temp_libraries.last()->key); + } + FileAccessWindows::finalize(); timeEndPeriod(1); @@ -354,7 +359,7 @@ void debug_dynamic_library_check_dependencies(const String &p_root_path, const S } #endif -Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path, String *r_resolved_path) { +Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) { String path = p_path.replace("/", "\\"); if (!FileAccess::exists(path)) { @@ -364,6 +369,35 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND); + // Here we want a copy to be loaded. + // This is so the original file isn't locked and can be updated by a compiler. + if (p_data != nullptr && p_data->generate_temp_files) { + // Copy the file to the same directory as the original with a prefix in the name. + // This is so relative path to dependencies are satisfied. + String copy_path = path.get_base_dir().path_join("~" + path.get_file()); + + // If there's a left-over copy (possibly from a crash) then delete it first. + if (FileAccess::exists(copy_path)) { + DirAccess::remove_absolute(copy_path); + } + + Error copy_err = DirAccess::copy_absolute(path, copy_path); + if (copy_err) { + ERR_PRINT("Error copying library: " + path); + return ERR_CANT_CREATE; + } + + FileAccess::set_hidden_attribute(copy_path, true); + + // Save the copied path so it can be deleted later. + path = copy_path; + + Error pdb_err = WindowsUtils::copy_and_rename_pdb(path); + if (pdb_err != OK && pdb_err != ERR_SKIP) { + WARN_PRINT(vformat("Failed to rename the PDB file. The original PDB file for '%s' will be loaded.", path)); + } + } + typedef DLL_DIRECTORY_COOKIE(WINAPI * PAddDllDirectory)(PCWSTR); typedef BOOL(WINAPI * PRemoveDllDirectory)(DLL_DIRECTORY_COOKIE); @@ -373,13 +407,17 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha bool has_dll_directory_api = ((add_dll_directory != nullptr) && (remove_dll_directory != nullptr)); DLL_DIRECTORY_COOKIE cookie = nullptr; - if (p_also_set_library_path && has_dll_directory_api) { + if (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) { cookie = add_dll_directory((LPCWSTR)(path.get_base_dir().utf16().get_data())); } - p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); -#ifdef DEBUG_ENABLED + p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_data != nullptr && p_data->also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); if (!p_library_handle) { + if (p_data != nullptr && p_data->generate_temp_files) { + DirAccess::remove_absolute(path); + } + +#ifdef DEBUG_ENABLED DWORD err_code = GetLastError(); HashSet<String> checekd_libs; @@ -397,8 +435,10 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha } else { ERR_FAIL_V_MSG(ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, format_error_message(err_code))); } +#endif } -#else + +#ifndef DEBUG_ENABLED ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, format_error_message(GetLastError()))); #endif @@ -406,8 +446,12 @@ Error OS_Windows::open_dynamic_library(const String &p_path, void *&p_library_ha remove_dll_directory(cookie); } - if (r_resolved_path != nullptr) { - *r_resolved_path = path; + if (p_data != nullptr && p_data->r_resolved_path != nullptr) { + *p_data->r_resolved_path = path; + } + + if (p_data != nullptr && p_data->generate_temp_files) { + temp_libraries[p_library_handle] = path; } return OK; @@ -417,9 +461,22 @@ Error OS_Windows::close_dynamic_library(void *p_library_handle) { if (!FreeLibrary((HMODULE)p_library_handle)) { return FAILED; } + + // Delete temporary copy of library if it exists. + _remove_temp_library(p_library_handle); + return OK; } +void OS_Windows::_remove_temp_library(void *p_library_handle) { + if (temp_libraries.has(p_library_handle)) { + String path = temp_libraries[p_library_handle]; + DirAccess::remove_absolute(path); + WindowsUtils::remove_temp_pdbs(path); + temp_libraries.erase(p_library_handle); + } +} + Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional) { p_symbol_handle = (void *)GetProcAddress((HMODULE)p_library_handle, p_name.utf8().get_data()); if (!p_symbol_handle) { @@ -810,7 +867,9 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String CloseHandle(pipe_err[1]); ProcessID pid = pi.pi.dwProcessId; + process_map_mutex.lock(); process_map->insert(pid, pi); + process_map_mutex.unlock(); Ref<FileAccessWindowsPipe> main_pipe; main_pipe.instantiate(); @@ -957,13 +1016,16 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg if (r_child_id) { *r_child_id = pid; } + process_map_mutex.lock(); process_map->insert(pid, pi); + process_map_mutex.unlock(); return OK; } Error OS_Windows::kill(const ProcessID &p_pid) { int ret = 0; + MutexLock lock(process_map_mutex); if (process_map->has(p_pid)) { const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi; process_map->erase(p_pid); @@ -989,24 +1051,58 @@ int OS_Windows::get_process_id() const { } bool OS_Windows::is_process_running(const ProcessID &p_pid) const { + MutexLock lock(process_map_mutex); if (!process_map->has(p_pid)) { return false; } - const PROCESS_INFORMATION &pi = (*process_map)[p_pid].pi; + const ProcessInfo &info = (*process_map)[p_pid]; + if (!info.is_running) { + return false; + } + const PROCESS_INFORMATION &pi = info.pi; DWORD dw_exit_code = 0; if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) { return false; } if (dw_exit_code != STILL_ACTIVE) { + info.is_running = false; + info.exit_code = dw_exit_code; return false; } return true; } +int OS_Windows::get_process_exit_code(const ProcessID &p_pid) const { + MutexLock lock(process_map_mutex); + if (!process_map->has(p_pid)) { + return -1; + } + + const ProcessInfo &info = (*process_map)[p_pid]; + if (!info.is_running) { + return info.exit_code; + } + + const PROCESS_INFORMATION &pi = info.pi; + + DWORD dw_exit_code = 0; + if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) { + return -1; + } + + if (dw_exit_code == STILL_ACTIVE) { + return -1; + } + + info.is_running = false; + info.exit_code = dw_exit_code; + return dw_exit_code; +} + Error OS_Windows::set_cwd(const String &p_cwd) { if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) { return ERR_CANT_OPEN; diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c703a3ddca..b6a21ed42d 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -127,6 +127,9 @@ class OS_Windows : public OS { bool dwrite_init = false; bool dwrite2_init = false; + HashMap<void *, String> temp_libraries; + + void _remove_temp_library(void *p_library_handle); String _get_default_fontname(const String &p_font_name) const; DWRITE_FONT_WEIGHT _weight_to_dw(int p_weight) const; DWRITE_FONT_STRETCH _stretch_to_dw(int p_stretch) const; @@ -147,15 +150,18 @@ protected: struct ProcessInfo { STARTUPINFO si; PROCESS_INFORMATION pi; + mutable bool is_running = true; + mutable int exit_code = -1; }; HashMap<ProcessID, ProcessInfo> *process_map = nullptr; + Mutex process_map_mutex; public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; - virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override; + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) override; virtual Error close_dynamic_library(void *p_library_handle) override; virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) override; @@ -186,6 +192,7 @@ public: virtual Error kill(const ProcessID &p_pid) override; virtual int get_process_id() const override; virtual bool is_process_running(const ProcessID &p_pid) const override; + virtual int get_process_exit_code(const ProcessID &p_pid) const override; virtual bool has_environment(const String &p_var) const override; virtual String get_environment(const String &p_var) const override; diff --git a/platform/windows/windows_utils.cpp b/platform/windows/windows_utils.cpp new file mode 100644 index 0000000000..147b8bcbae --- /dev/null +++ b/platform/windows/windows_utils.cpp @@ -0,0 +1,280 @@ +/**************************************************************************/ +/* windows_utils.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "windows_utils.h" + +#ifdef WINDOWS_ENABLED + +#include "core/error/error_macros.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef FAILED // Overrides Error::FAILED + +// dbghelp is linked only in DEBUG_ENABLED builds. +#ifdef DEBUG_ENABLED +#include <dbghelp.h> +#endif +#include <winnt.h> + +HashMap<String, Vector<String>> WindowsUtils::temp_pdbs; + +Error WindowsUtils::copy_and_rename_pdb(const String &p_dll_path) { +#ifdef DEBUG_ENABLED + // 1000 ought to be enough for anybody, in case the debugger does not unblock previous PDBs. + // Usually no more than 2 will be used. + const int max_pdb_names = 1000; + + struct PDBResourceInfo { + uint32_t address = 0; + String path; + } pdb_info; + + // Open and read the PDB information if available. + { + ULONG dbg_info_size = 0; + DWORD dbg_info_position = 0; + + { + // The custom LoadLibraryExW is used instead of open_dynamic_library + // to avoid loading the original PDB into the debugger. + HMODULE library_ptr = LoadLibraryExW((LPCWSTR)(p_dll_path.utf16().get_data()), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE); + + ERR_FAIL_NULL_V_MSG(library_ptr, ERR_FILE_CANT_OPEN, vformat("Failed to load library '%s'.", p_dll_path)); + + IMAGE_DEBUG_DIRECTORY *dbg_dir = (IMAGE_DEBUG_DIRECTORY *)ImageDirectoryEntryToDataEx(library_ptr, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &dbg_info_size, NULL); + + bool has_debug = dbg_dir && dbg_dir->Type == IMAGE_DEBUG_TYPE_CODEVIEW; + if (has_debug) { + dbg_info_position = dbg_dir->PointerToRawData; + dbg_info_size = dbg_dir->SizeOfData; + } + + ERR_FAIL_COND_V_MSG(!FreeLibrary((HMODULE)library_ptr), FAILED, vformat("Failed to free library '%s'.", p_dll_path)); + + if (!has_debug) { + // Skip with no debugging symbols. + return ERR_SKIP; + } + } + + struct CV_HEADER { + DWORD Signature; + DWORD Offset; + }; + + const DWORD nb10_magic = '01BN'; + struct CV_INFO_PDB20 { + CV_HEADER CvHeader; // CvHeader.Signature = "NB10" + DWORD Signature; + DWORD Age; + BYTE PdbFileName[1]; + }; + + const DWORD rsds_magic = 'SDSR'; + struct CV_INFO_PDB70 { + DWORD Signature; // "RSDS" + BYTE Guid[16]; + DWORD Age; + BYTE PdbFileName[1]; + }; + + Vector<uint8_t> dll_data; + + { + Error err = OK; + Ref<FileAccess> file = FileAccess::open(p_dll_path, FileAccess::READ, &err); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to read library '%s'.", p_dll_path)); + + file->seek(dbg_info_position); + dll_data = file->get_buffer(dbg_info_size); + ERR_FAIL_COND_V_MSG(file->get_error() != OK, file->get_error(), vformat("Failed to read data from library '%s'.", p_dll_path)); + } + + const char *raw_pdb_path = nullptr; + int raw_pdb_offset = 0; + DWORD *pdb_info_signature = (DWORD *)dll_data.ptr(); + + if (*pdb_info_signature == rsds_magic) { + raw_pdb_path = (const char *)(((CV_INFO_PDB70 *)pdb_info_signature)->PdbFileName); + raw_pdb_offset = offsetof(CV_INFO_PDB70, PdbFileName); + } else if (*pdb_info_signature == nb10_magic) { + // Not even sure if this format still exists anywhere... + raw_pdb_path = (const char *)(((CV_INFO_PDB20 *)pdb_info_signature)->PdbFileName); + raw_pdb_offset = offsetof(CV_INFO_PDB20, PdbFileName); + } else { + ERR_FAIL_V_MSG(FAILED, vformat("Unknown PDB format in '%s'.", p_dll_path)); + } + + String utf_path; + Error err = utf_path.parse_utf8(raw_pdb_path); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to read PDB path from '%s'.", p_dll_path)); + + pdb_info.path = utf_path; + pdb_info.address = dbg_info_position + raw_pdb_offset; + } + + String dll_base_dir = p_dll_path.get_base_dir(); + String copy_pdb_path = pdb_info.path; + + // Attempting to find the PDB by absolute and relative paths. + if (copy_pdb_path.is_relative_path()) { + copy_pdb_path = dll_base_dir.path_join(copy_pdb_path); + if (!FileAccess::exists(copy_pdb_path)) { + copy_pdb_path = dll_base_dir.path_join(copy_pdb_path.get_file()); + } + } else if (!FileAccess::exists(copy_pdb_path)) { + copy_pdb_path = dll_base_dir.path_join(copy_pdb_path.get_file()); + } + ERR_FAIL_COND_V_MSG(!FileAccess::exists(copy_pdb_path), FAILED, vformat("File '%s' does not exist.", copy_pdb_path)); + + String new_pdb_base_name = p_dll_path.get_file().get_basename() + "_"; + + // Checking the available space for the updated string + // and trying to shorten it if there is not much space. + { + // e.g. 999.pdb + const uint8_t suffix_size = String::num_characters((int64_t)max_pdb_names - 1) + 4; + // e.g. ~lib_ + 1 for the \0 + const uint8_t min_base_size = 5 + 1; + int original_path_size = pdb_info.path.utf8().length(); + CharString utf8_name = new_pdb_base_name.utf8(); + int new_expected_buffer_size = utf8_name.length() + suffix_size; + + // Since we have limited space inside the DLL to patch the path to the PDB, + // it is necessary to limit the size based on the number of bytes occupied by the string. + if (new_expected_buffer_size > original_path_size) { + ERR_FAIL_COND_V_MSG(original_path_size < min_base_size + suffix_size, FAILED, vformat("The original PDB path size in bytes is too small: '%s'. Expected size: %d or more bytes, but available %d.", pdb_info.path, min_base_size + suffix_size, original_path_size)); + + utf8_name.resize(original_path_size - suffix_size + 1); // +1 for the \0 + utf8_name[utf8_name.size() - 1] = '\0'; + new_pdb_base_name.parse_utf8(utf8_name); + new_pdb_base_name[new_pdb_base_name.length() - 1] = '_'; // Restore the last '_' + WARN_PRINT(vformat("The original path size of '%s' in bytes was too small to fit the new name, so it was shortened to '%s%d.pdb'.", pdb_info.path, new_pdb_base_name, max_pdb_names - 1)); + } + } + + // Delete old PDB files. + for (const String &file : DirAccess::get_files_at(dll_base_dir)) { + if (file.begins_with(new_pdb_base_name) && file.ends_with(".pdb")) { + String path = dll_base_dir.path_join(file); + + // Just try to delete without showing any errors. + Error err = DirAccess::remove_absolute(path); + if (err == OK && temp_pdbs[p_dll_path].has(path)) { + temp_pdbs[p_dll_path].erase(path); + } + } + } + + // Try to copy PDB with new name and patch DLL. + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < max_pdb_names; i++) { + String new_pdb_name = vformat("%s%d.pdb", new_pdb_base_name, i); + String new_pdb_path = dll_base_dir.path_join(new_pdb_name); + Error err = OK; + + Ref<FileAccess> test_pdb_is_locked = FileAccess::open(new_pdb_path, FileAccess::READ_WRITE, &err); + if (err == ERR_FILE_CANT_OPEN) { + // If the file is blocked, continue searching. + continue; + } else if (err != OK && err != ERR_FILE_NOT_FOUND) { + ERR_FAIL_V_MSG(err, vformat("Failed to open '%s' to check if it is locked.", new_pdb_path)); + } + + err = d->copy(copy_pdb_path, new_pdb_path); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to copy PDB from '%s' to '%s'.", copy_pdb_path, new_pdb_path)); + temp_pdbs[p_dll_path].append(new_pdb_path); + + Ref<FileAccess> file = FileAccess::open(p_dll_path, FileAccess::READ_WRITE, &err); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s' to patch the PDB path.", p_dll_path)); + + int original_path_size = pdb_info.path.utf8().length(); + // Double-check file bounds. + ERR_FAIL_INDEX_V_MSG(pdb_info.address + original_path_size, file->get_length(), FAILED, vformat("Failed to write a new PDB path. Probably '%s' has been changed.", p_dll_path)); + + Vector<uint8_t> u8 = new_pdb_name.to_utf8_buffer(); + file->seek(pdb_info.address); + file->store_buffer(u8); + + // Terminate string and fill the remaining part of the original string with the '\0'. + // Can be replaced by file->store_8('\0'); + Vector<uint8_t> padding_buffer; + padding_buffer.resize((int64_t)original_path_size - u8.size()); + padding_buffer.fill('\0'); + file->store_buffer(padding_buffer); + ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to write a new PDB path to '%s'.", p_dll_path)); + + return OK; + } + + ERR_FAIL_V_MSG(FAILED, vformat("Failed to find an unblocked PDB name for '%s' among %d files.", p_dll_path, max_pdb_names)); +#else + WARN_PRINT_ONCE("Renaming PDB files is only available in debug builds. If your libraries use PDB files, then the original ones will be used."); + return ERR_SKIP; +#endif +} + +void WindowsUtils::remove_temp_pdbs(const String &p_dll_path) { +#ifdef DEBUG_ENABLED + if (temp_pdbs.has(p_dll_path)) { + Vector<String> removed; + int failed = 0; + const int failed_limit = 10; + for (const String &pdb : temp_pdbs[p_dll_path]) { + if (FileAccess::exists(pdb)) { + Error err = DirAccess::remove_absolute(pdb); + if (err == OK) { + removed.append(pdb); + } else { + failed++; + if (failed <= failed_limit) { + print_verbose("Failed to remove temp PDB: " + pdb); + } + } + } else { + removed.append(pdb); + } + } + + if (failed > failed_limit) { + print_verbose(vformat("And %d more PDB files could not be removed....", failed - failed_limit)); + } + + for (const String &pdb : removed) { + temp_pdbs[p_dll_path].erase(pdb); + } + } +#endif +} + +#endif // WINDOWS_ENABLED diff --git a/platform/windows/windows_utils.h b/platform/windows/windows_utils.h new file mode 100644 index 0000000000..3f5d75294e --- /dev/null +++ b/platform/windows/windows_utils.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* windows_utils.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef WINDOWS_UTILS_H +#define WINDOWS_UTILS_H + +#ifdef WINDOWS_ENABLED + +#include "core/string/ustring.h" +#include "core/templates/hash_map.h" + +class WindowsUtils { + static HashMap<String, Vector<String>> temp_pdbs; + +public: + static Error copy_and_rename_pdb(const String &p_dll_path); + static void remove_temp_pdbs(const String &p_dll_path); +}; + +#endif // WINDOWS_ENABLED + +#endif // WINDOWS_UTILS_H |