summaryrefslogtreecommitdiff
path: root/platform/windows
diff options
context:
space:
mode:
Diffstat (limited to 'platform/windows')
-rw-r--r--platform/windows/detect.py20
-rw-r--r--platform/windows/display_server_windows.cpp86
-rw-r--r--platform/windows/display_server_windows.h8
-rw-r--r--platform/windows/export/export_plugin.cpp58
-rw-r--r--platform/windows/export/export_plugin.h8
-rw-r--r--platform/windows/godot_windows.cpp7
-rw-r--r--platform/windows/joypad_windows.cpp42
-rw-r--r--platform/windows/joypad_windows.h2
-rw-r--r--platform/windows/os_windows.cpp193
-rw-r--r--platform/windows/os_windows.h5
10 files changed, 230 insertions, 199 deletions
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index e9ecc99ef5..249a0d2e79 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -65,7 +65,7 @@ def get_opts():
# Vista support dropped after EOL due to GH-10243
("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"),
BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
- EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")),
+ EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")),
BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False),
("msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", None),
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
@@ -178,15 +178,6 @@ def configure_msvc(env, manual_msvc_config):
# Build type
- if env["tests"]:
- env["windows_subsystem"] = "console"
- elif env["windows_subsystem"] == "default":
- # Default means we use console for debug, gui for release.
- if "debug" in env["target"]:
- env["windows_subsystem"] = "console"
- else:
- env["windows_subsystem"] = "gui"
-
if env["target"] == "release":
if env["optimize"] == "speed": # optimize for speed (default)
env.Append(CCFLAGS=["/O2"])
@@ -326,15 +317,6 @@ def configure_mingw(env):
## Build type
- if env["tests"]:
- env["windows_subsystem"] = "console"
- elif env["windows_subsystem"] == "default":
- # Default means we use console for debug, gui for release.
- if "debug" in env["target"]:
- env["windows_subsystem"] = "console"
- else:
- env["windows_subsystem"] = "gui"
-
if env["target"] == "release":
env.Append(CCFLAGS=["-msse2"])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 091bed36ea..d288c27016 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -77,7 +77,6 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_CLIPBOARD:
case FEATURE_CURSOR_SHAPE:
case FEATURE_CUSTOM_CURSOR_SHAPE:
- case FEATURE_CONSOLE_WINDOW:
case FEATURE_IME:
case FEATURE_WINDOW_TRANSPARENCY:
case FEATURE_HIDPI:
@@ -98,7 +97,10 @@ String DisplayServerWindows::get_name() const {
void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
if (windows.has(MAIN_WINDOW_ID) && (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED || p_mode == MOUSE_MODE_CONFINED_HIDDEN)) {
// Mouse is grabbed (captured or confined).
- WindowData &wd = windows[MAIN_WINDOW_ID];
+
+ WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
+
+ WindowData &wd = windows[window_id];
RECT clipRect;
GetClientRect(wd.hWnd, &clipRect);
@@ -571,6 +573,24 @@ void DisplayServerWindows::gl_window_make_current(DisplayServer::WindowID p_wind
#endif
}
+int64_t DisplayServerWindows::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(!windows.has(p_window), 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)windows[p_window].hWnd;
+ }
+ case WINDOW_VIEW: {
+ return 0; // Not supported.
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -675,8 +695,29 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi
ERR_FAIL_COND(!windows.has(p_window));
ERR_FAIL_INDEX(p_screen, get_screen_count());
- Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
- window_set_position(ofs + screen_get_position(p_screen), p_window);
+ const WindowData &wd = windows[p_window];
+ if (wd.fullscreen) {
+ int cs = window_get_current_screen(p_window);
+ if (cs == p_screen) {
+ return;
+ }
+ Point2 pos = screen_get_position(p_screen);
+ Size2 size = screen_get_size(p_screen);
+
+ MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
+ } else {
+ Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(ofs + screen_get_position(p_screen), p_window);
+ }
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution.
+ if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
+ }
}
Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {
@@ -1039,6 +1080,15 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0);
}
}
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution.
+ if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
+ }
}
DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
@@ -1207,23 +1257,6 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI
ImmReleaseContext(wd.hWnd, himc);
}
-void DisplayServerWindows::console_set_visible(bool p_enabled) {
- _THREAD_SAFE_METHOD_
-
- if (console_visible == p_enabled) {
- return;
- }
- if (!((OS_Windows *)OS::get_singleton())->_is_win11_terminal()) {
- // GetConsoleWindow is not supported by the Windows Terminal.
- ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE);
- console_visible = p_enabled;
- }
-}
-
-bool DisplayServerWindows::is_console_visible() const {
- return console_visible;
-}
-
void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
_THREAD_SAFE_METHOD_
@@ -2078,7 +2111,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
mm->set_position(c);
mm->set_global_position(c);
- Input::get_singleton()->set_mouse_position(c);
mm->set_velocity(Vector2(0, 0));
if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
@@ -2183,7 +2215,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SetCursorPos(pos.x, pos.y);
}
- Input::get_singleton()->set_mouse_position(mm->get_position());
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
if (old_invalid) {
@@ -2325,7 +2356,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SetCursorPos(pos.x, pos.y);
}
- Input::get_singleton()->set_mouse_position(mm->get_position());
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
if (old_invalid) {
@@ -2426,7 +2456,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
SetCursorPos(pos.x, pos.y);
}
- Input::get_singleton()->set_mouse_position(mm->get_position());
mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
if (old_invalid) {
@@ -2819,6 +2848,9 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
case WM_DEVICECHANGE: {
joypad->probe_joypads();
} break;
+ case WM_DESTROY: {
+ Input::get_singleton()->flush_buffered_events();
+ } 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)) {
@@ -2886,6 +2918,9 @@ void DisplayServerWindows::_process_activate_event(WindowID p_window_id, WPARAM
alt_mem = false;
control_mem = false;
shift_mem = false;
+
+ // Restore mouse mode.
+ _set_mouse_mode_impl(mouse_mode);
} else { // WM_INACTIVE.
Input::get_singleton()->release_pressed_events();
_send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT);
@@ -3240,7 +3275,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
shift_mem = false;
control_mem = false;
meta_mem = false;
- console_visible = IsWindowVisible(GetConsoleWindow());
hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
pressrc = 0;
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 409335b41c..803c2d4836 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -414,7 +414,6 @@ class DisplayServerWindows : public DisplayServer {
bool use_raw_input = false;
bool drop_events = false;
bool in_dispatch_input_event = false;
- bool console_visible = false;
WNDCLASSEXW wc;
@@ -473,11 +472,13 @@ public:
virtual void show_window(WindowID p_window) override;
virtual void delete_sub_window(WindowID p_window) override;
+ virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
+
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
- virtual void gl_window_make_current(DisplayServer::WindowID p_window_id);
+ virtual void gl_window_make_current(DisplayServer::WindowID p_window_id) override;
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
@@ -529,9 +530,6 @@ public:
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
- virtual void console_set_visible(bool p_enabled) override;
- virtual bool is_console_visible() const override;
-
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 02b2d026b5..d30d0afc5c 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -54,13 +54,19 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
return err;
}
+bool EditorExportPlatformWindows::get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ // 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;
+ }
+ return true;
+}
+
void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) {
EditorExportPlatformPC::get_export_options(r_options);
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false));
-#ifdef WINDOWS_ENABLED
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0));
-#endif
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
@@ -70,8 +76,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray()));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0.0"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), ""));
@@ -83,6 +89,7 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset>
String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
if (rcedit_path.is_empty()) {
+ WARN_PRINT("The rcedit tool is not configured in the Editor Settings (Export > Windows > Rcedit). No custom icon or app information data will be embedded in the exported executable.");
return;
}
@@ -321,3 +328,46 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
return OK;
}
+
+bool EditorExportPlatformWindows::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ String err = "";
+ bool valid = EditorExportPlatformPC::can_export(p_preset, err, r_missing_templates);
+
+ String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit");
+ if (rcedit_path.is_empty()) {
+ err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > Rcedit) to change the icon or app information data.") + "\n";
+ }
+
+ String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon"));
+ if (!icon_path.is_empty() && !FileAccess::exists(icon_path)) {
+ err += TTR("Invalid icon path:") + " " + icon_path + "\n";
+ }
+
+ // Only non-negative integers can exist in the version string.
+
+ String file_version = p_preset->get("application/file_version");
+ if (!file_version.is_empty()) {
+ PackedStringArray version_array = file_version.split(".", false);
+ if (version_array.size() != 4 || !version_array[0].is_valid_int() ||
+ !version_array[1].is_valid_int() || !version_array[2].is_valid_int() ||
+ !version_array[3].is_valid_int() || file_version.find("-") > -1) {
+ err += TTR("Invalid file version:") + " " + file_version + "\n";
+ }
+ }
+
+ String product_version = p_preset->get("application/product_version");
+ if (!product_version.is_empty()) {
+ PackedStringArray version_array = product_version.split(".", false);
+ if (version_array.size() != 4 || !version_array[0].is_valid_int() ||
+ !version_array[1].is_valid_int() || !version_array[2].is_valid_int() ||
+ !version_array[3].is_valid_int() || product_version.find("-") > -1) {
+ err += TTR("Invalid product version:") + " " + product_version + "\n";
+ }
+ }
+
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index 4ec9342cdf..89e5b1b635 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -43,9 +43,11 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC {
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
public:
- virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
- virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
- virtual void get_export_options(List<ExportOption> *r_options);
+ virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
+ virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override;
+ virtual void get_export_options(List<ExportOption> *r_options) override;
+ virtual bool get_export_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+ virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
};
#endif
diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp
index 7819ab9a32..618d5670d2 100644
--- a/platform/windows/godot_windows.cpp
+++ b/platform/windows/godot_windows.cpp
@@ -39,7 +39,7 @@
#ifndef TOOLS_ENABLED
#if defined _MSC_VER
#pragma section("pck", read)
-__declspec(allocate("pck")) static char dummy[8] = { 0 };
+__declspec(allocate("pck")) static const char dummy[8] = { 0 };
#elif defined __GNUC__
static const char dummy[8] __attribute__((section("pck"), used)) = { 0 };
#endif
@@ -140,6 +140,11 @@ int widechar_main(int argc, wchar_t **argv) {
setlocale(LC_CTYPE, "");
+#ifndef TOOLS_ENABLED
+ // Workaround to prevent LTCG (MSVC LTO) from removing "pck" section
+ const char *dummy_guard = dummy;
+#endif
+
char **argv_utf8 = new char *[argc];
for (int i = 0; i < argc; ++i) {
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 8b6081d606..b0dd86a4b7 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -448,33 +448,27 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
input->joy_hat(p_device, dpad_val);
};
-Input::JoyAxisValue JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
- Input::JoyAxisValue jx;
+float JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
if (Math::abs(p_val) < MIN_JOY_AXIS) {
- jx.min = p_trigger ? 0 : -1;
- jx.value = 0.0f;
- return jx;
+ return p_trigger ? -1.0f : 0.0f;
}
- if (p_xinput) {
- if (p_trigger) {
- jx.min = 0;
- jx.value = (float)p_val / MAX_TRIGGER;
- return jx;
- }
- jx.min = -1;
- if (p_val < 0) {
- jx.value = (float)p_val / MAX_JOY_AXIS;
- } else {
- jx.value = (float)p_val / (MAX_JOY_AXIS - 1);
- }
- if (p_negate) {
- jx.value = -jx.value;
- }
- return jx;
+ if (!p_xinput) {
+ return (float)p_val / MAX_JOY_AXIS;
+ }
+ if (p_trigger) {
+ // Convert to a value between -1.0f and 1.0f.
+ return 2.0f * p_val / MAX_TRIGGER - 1.0f;
+ }
+ float value;
+ if (p_val < 0) {
+ value = (float)p_val / MAX_JOY_AXIS;
+ } else {
+ value = (float)p_val / (MAX_JOY_AXIS - 1);
+ }
+ if (p_negate) {
+ value = -value;
}
- jx.min = -1;
- jx.value = (float)p_val / MAX_JOY_AXIS;
- return jx;
+ return value;
}
void JoypadWindows::joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index 4faefe932f..0e3d03fa52 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -132,7 +132,7 @@ private:
void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
- Input::JoyAxisValue axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
+ float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
XInputGetState_t xinput_get_state;
XInputSetState_t xinput_set_state;
};
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 203e078cb4..d844531071 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -52,8 +52,6 @@
#include <regstr.h>
#include <shlobj.h>
-static const WORD MAX_CONSOLE_LINES = 1500;
-
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 1;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
@@ -85,66 +83,26 @@ static String format_error_message(DWORD id) {
return msg;
}
-void RedirectIOToConsole() {
- int hConHandle;
-
- intptr_t lStdHandle;
-
- CONSOLE_SCREEN_BUFFER_INFO coninfo;
-
- FILE *fp;
-
- // allocate a console for this app
-
- AllocConsole();
-
- // set the screen buffer to be big enough to let us scroll text
-
- GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
-
- coninfo.dwSize.Y = MAX_CONSOLE_LINES;
-
- SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
-
- // redirect unbuffered STDOUT to the console
-
- lStdHandle = (intptr_t)GetStdHandle(STD_OUTPUT_HANDLE);
-
- hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
-
- fp = _fdopen(hConHandle, "w");
-
- *stdout = *fp;
-
- setvbuf(stdout, nullptr, _IONBF, 0);
-
- // redirect unbuffered STDIN to the console
-
- lStdHandle = (intptr_t)GetStdHandle(STD_INPUT_HANDLE);
-
- hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
-
- fp = _fdopen(hConHandle, "r");
-
- *stdin = *fp;
-
- setvbuf(stdin, nullptr, _IONBF, 0);
-
- // redirect unbuffered STDERR to the console
-
- lStdHandle = (intptr_t)GetStdHandle(STD_ERROR_HANDLE);
-
- hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
-
- fp = _fdopen(hConHandle, "w");
-
- *stderr = *fp;
-
- setvbuf(stderr, nullptr, _IONBF, 0);
+void RedirectStream(const char *p_file_name, const char *p_mode, FILE *p_cpp_stream, const DWORD p_std_handle) {
+ const HANDLE h_existing = GetStdHandle(p_std_handle);
+ if (h_existing != INVALID_HANDLE_VALUE) { // Redirect only if attached console have a valid handle.
+ const HANDLE h_cpp = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(p_cpp_stream)));
+ if (h_cpp == INVALID_HANDLE_VALUE) { // Redirect only if it's not already redirected to the pipe or file.
+ FILE *fp = p_cpp_stream;
+ freopen_s(&fp, p_file_name, p_mode, p_cpp_stream); // Redirect stream.
+ setvbuf(p_cpp_stream, nullptr, _IONBF, 0); // Disable stream buffering.
+ }
+ }
+}
- // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
+void RedirectIOToConsole() {
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ RedirectStream("CONIN$", "r", stdin, STD_INPUT_HANDLE);
+ RedirectStream("CONOUT$", "w", stdout, STD_OUTPUT_HANDLE);
+ RedirectStream("CONOUT$", "w", stderr, STD_ERROR_HANDLE);
- // point to console as well
+ printf("\n"); // Make sure our output is starting from the new line.
+ }
}
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
@@ -172,7 +130,9 @@ void OS_Windows::initialize_debugging() {
void OS_Windows::initialize() {
crash_handler.initialize();
- //RedirectIOToConsole();
+#ifndef WINDOWS_SUBSYSTEM_CONSOLE
+ RedirectIOToConsole();
+#endif
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
@@ -401,78 +361,87 @@ String OS_Windows::_quote_command_line_argument(const String &p_text) const {
return p_text;
}
-Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
+Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
String path = p_path.replace("/", "\\");
String command = _quote_command_line_argument(path);
for (const String &E : p_arguments) {
command += " " + _quote_command_line_argument(E);
}
+ ProcessInfo pi;
+ ZeroMemory(&pi.si, sizeof(pi.si));
+ pi.si.cb = sizeof(pi.si);
+ ZeroMemory(&pi.pi, sizeof(pi.pi));
+ LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
+
+ bool inherit_handles = false;
+ HANDLE pipe[2] = { nullptr, nullptr };
if (r_pipe) {
+ // Create pipe for StdOut and StdErr.
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true;
+ sa.lpSecurityDescriptor = nullptr;
+
+ ERR_FAIL_COND_V(!CreatePipe(&pipe[0], &pipe[1], &sa, 0), ERR_CANT_FORK);
+ ERR_FAIL_COND_V(!SetHandleInformation(pipe[0], HANDLE_FLAG_INHERIT, 0), ERR_CANT_FORK); // Read handle is for host process only and should not be inherited.
+
+ pi.si.dwFlags |= STARTF_USESTDHANDLES;
+ pi.si.hStdOutput = pipe[1];
if (read_stderr) {
- command += " 2>&1"; // Include stderr
+ pi.si.hStdError = pipe[1];
}
- // Add extra quotes around the full command, to prevent it from stripping quotes in the command,
- // because _wpopen calls command as "cmd.exe /c command", instead of executing it directly
- command = _quote_command_line_argument(command);
-
- FILE *f = _wpopen((LPCWSTR)(command.utf16().get_data()), L"r");
- ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot create pipe from command: " + command);
- char buf[65535];
- while (fgets(buf, 65535, f)) {
+ inherit_handles = true;
+ }
+ DWORD creation_flags = NORMAL_PRIORITY_CLASS;
+ if (p_open_console) {
+ creation_flags |= CREATE_NEW_CONSOLE;
+ } else {
+ creation_flags |= CREATE_NO_WINDOW;
+ }
+
+ int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, inherit_handles, creation_flags, nullptr, nullptr, si_w, &pi.pi);
+ if (!ret && r_pipe) {
+ CloseHandle(pipe[0]); // Cleanup pipe handles.
+ CloseHandle(pipe[1]);
+ }
+ ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
+
+ if (r_pipe) {
+ CloseHandle(pipe[1]); // Close pipe write handle (only child process is writing).
+ char buf[4096];
+ DWORD read = 0;
+ for (;;) { // Read StdOut and StdErr from pipe.
+ bool success = ReadFile(pipe[0], buf, 4096, &read, NULL);
+ if (!success || read == 0) {
+ break;
+ }
if (p_pipe_mutex) {
p_pipe_mutex->lock();
}
- (*r_pipe) += String::utf8(buf);
+ (*r_pipe) += String::utf8(buf, read);
if (p_pipe_mutex) {
p_pipe_mutex->unlock();
}
- }
- int rv = _pclose(f);
-
- if (r_exitcode) {
- *r_exitcode = rv;
- }
- return OK;
+ };
+ CloseHandle(pipe[0]); // Close pipe read handle.
+ } else {
+ WaitForSingleObject(pi.pi.hProcess, INFINITE);
}
- ProcessInfo pi;
- ZeroMemory(&pi.si, sizeof(pi.si));
- pi.si.cb = sizeof(pi.si);
- ZeroMemory(&pi.pi, sizeof(pi.pi));
- LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
-
- DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
-#ifndef DEBUG_ENABLED
- dwCreationFlags |= CREATE_NO_WINDOW;
-#endif
-
- int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, dwCreationFlags, nullptr, nullptr, si_w, &pi.pi);
- ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
-
- WaitForSingleObject(pi.pi.hProcess, INFINITE);
if (r_exitcode) {
DWORD ret2;
GetExitCodeProcess(pi.pi.hProcess, &ret2);
*r_exitcode = ret2;
}
+
CloseHandle(pi.pi.hProcess);
CloseHandle(pi.pi.hThread);
return OK;
};
-bool OS_Windows::_is_win11_terminal() const {
- HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
- DWORD dwMode = 0;
- if (GetConsoleMode(hStdOut, &dwMode)) {
- return ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == ENABLE_VIRTUAL_TERMINAL_PROCESSING);
- } else {
- return false;
- }
-}
-
-Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) {
+Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
String path = p_path.replace("/", "\\");
String command = _quote_command_line_argument(path);
for (const String &E : p_arguments) {
@@ -485,16 +454,14 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
ZeroMemory(&pi.pi, sizeof(pi.pi));
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
- DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS;
-#ifndef DEBUG_ENABLED
- dwCreationFlags |= CREATE_NO_WINDOW;
-#endif
- if (p_path == get_executable_path() && GetConsoleWindow() != nullptr && _is_win11_terminal()) {
- // Open a new terminal as a workaround for Windows Terminal bug.
- dwCreationFlags |= CREATE_NEW_CONSOLE;
+ DWORD creation_flags = NORMAL_PRIORITY_CLASS;
+ if (p_open_console) {
+ creation_flags |= CREATE_NEW_CONSOLE;
+ } else {
+ creation_flags |= CREATE_NO_WINDOW;
}
- int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, dwCreationFlags, nullptr, nullptr, si_w, &pi.pi);
+ int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, creation_flags, nullptr, nullptr, si_w, &pi.pi);
ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command);
ProcessID pid = pi.pi.dwProcessId;
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 4e61f3be7e..28baa855b4 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -128,8 +128,8 @@ public:
virtual void delay_usec(uint32_t p_usec) const override;
virtual uint64_t get_ticks_usec() const override;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override;
- virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
+ virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override;
@@ -157,7 +157,6 @@ public:
void run();
- bool _is_win11_terminal() const;
virtual bool _check_internal_feature_support(const String &p_feature) override;
virtual void disable_crash_handler() override;