summaryrefslogtreecommitdiffstats
path: root/platform/windows
diff options
context:
space:
mode:
Diffstat (limited to 'platform/windows')
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/detect.py37
-rw-r--r--platform/windows/display_server_windows.cpp175
-rw-r--r--platform/windows/display_server_windows.h6
-rw-r--r--platform/windows/doc_classes/EditorExportPlatformWindows.xml1
-rw-r--r--platform/windows/export/export_plugin.cpp9
-rw-r--r--platform/windows/gl_manager_windows_native.cpp24
-rw-r--r--platform/windows/gl_manager_windows_native.h4
-rw-r--r--platform/windows/os_windows.cpp112
-rw-r--r--platform/windows/os_windows.h9
-rw-r--r--platform/windows/windows_utils.cpp280
-rw-r--r--platform/windows/windows_utils.h49
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