From 59085d50517368f99b925dbe7d6fa0aab9fded9c Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 16 Dec 2021 13:08:09 +0200 Subject: [PATCH] [Windows] Improve console handling and execute. Always build with the GUI subsystem. Redirect stdout and stderr output to the parent process console. Use CreateProcessW for blocking `execute` calls with piped stdout and stderr (prevent console windows for popping up when used with the GUI subsystem build, and have more consistent behavior with non-blocking calls). Add `open_console` argument to the `execute` to open a new console window (for both blocking and non-blocking calls). Remove `interface/editor/hide_console_window` editor setting. Remove `Toggle System Console` menu option. Remove `set_console_visible` and `is_console_visible` functions. --- core/bind/core_bind.cpp | 7 +- core/bind/core_bind.h | 2 +- core/os/os.h | 4 +- doc/classes/OS.xml | 2 + drivers/unix/os_unix.cpp | 2 +- drivers/unix/os_unix.h | 2 +- editor/editor_node.cpp | 8 -- editor/editor_node.h | 1 - editor/editor_settings.cpp | 1 - main/main.cpp | 4 - platform/javascript/os_javascript.cpp | 2 +- platform/javascript/os_javascript.h | 2 +- platform/osx/os_osx.h | 2 +- platform/osx/os_osx.mm | 6 +- platform/uwp/os_uwp.cpp | 2 +- platform/uwp/os_uwp.h | 2 +- platform/windows/detect.py | 12 +- platform/windows/os_windows.cpp | 192 +++++++++----------------- platform/windows/os_windows.h | 7 +- 19 files changed, 87 insertions(+), 173 deletions(-) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 0186798073b..bb57474e376 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -36,6 +36,7 @@ #include "core/io/json.h" #include "core/io/marshalls.h" #include "core/math/geometry.h" +#include "core/method_bind_ext.gen.inc" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/project_settings.h" @@ -471,7 +472,7 @@ Error _OS::shell_open(String p_uri) { return OS::get_singleton()->shell_open(p_uri); }; -int _OS::execute(const String &p_path, const Vector &p_arguments, bool p_blocking, Array p_output, bool p_read_stderr) { +int _OS::execute(const String &p_path, const Vector &p_arguments, bool p_blocking, Array p_output, bool p_read_stderr, bool p_open_console) { OS::ProcessID pid = -2; int exitcode = 0; List args; @@ -479,7 +480,7 @@ int _OS::execute(const String &p_path, const Vector &p_arguments, bool p args.push_back(p_arguments[i]); } String pipe; - Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe, &exitcode, p_read_stderr); + Error err = OS::get_singleton()->execute(p_path, args, p_blocking, &pid, &pipe, &exitcode, p_read_stderr, nullptr, p_open_console); p_output.clear(); p_output.push_back(pipe); if (err != OK) { @@ -1311,7 +1312,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_processor_count"), &_OS::get_processor_count); ClassDB::bind_method(D_METHOD("get_executable_path"), &_OS::get_executable_path); - ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "blocking", "output", "read_stderr"), &_OS::execute, DEFVAL(true), DEFVAL(Array()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "blocking", "output", "read_stderr", "open_console"), &_OS::execute, DEFVAL(true), DEFVAL(Array()), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("kill", "pid"), &_OS::kill); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &_OS::shell_open); ClassDB::bind_method(D_METHOD("get_process_id"), &_OS::get_process_id); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index d2c611b91fa..4f9ddf91767 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -241,7 +241,7 @@ public: int get_low_processor_usage_mode_sleep_usec() const; String get_executable_path() const; - int execute(const String &p_path, const Vector &p_arguments, bool p_blocking = true, Array p_output = Array(), bool p_read_stderr = false); + int execute(const String &p_path, const Vector &p_arguments, bool p_blocking = true, Array p_output = Array(), bool p_read_stderr = false, bool p_open_console = false); Error kill(int p_pid); Error shell_open(String p_uri); diff --git a/core/os/os.h b/core/os/os.h index 5be9b1d9d12..f37e763f4fb 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -234,8 +234,6 @@ public: virtual void set_window_always_on_top(bool p_enabled) {} virtual bool is_window_always_on_top() const { return false; } virtual bool is_window_focused() const { return true; } - virtual void set_console_visible(bool p_enabled) {} - virtual bool is_console_visible() const { return false; } virtual void request_attention() {} virtual void center_window(); @@ -290,7 +288,7 @@ public: virtual int get_low_processor_usage_mode_sleep_usec() const; virtual String get_executable_path() const; - virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) = 0; + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0; virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual void vibrate_handheld(int p_duration_ms = 500); diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 80266b6c14b..6f837a56ba3 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -92,12 +92,14 @@ + Execute the file at the given path with the arguments passed as an array of strings. Platform path resolution will take place. The resolved file must exist and be executable. The arguments are used in the given order and separated by a space, so [code]OS.execute("ping", ["-w", "3", "godotengine.org"], false)[/code] will resolve to [code]ping -w 3 godotengine.org[/code] in the system's shell. This method has slightly different behavior based on whether the [code]blocking[/code] mode is enabled. If [code]blocking[/code] is [code]true[/code], the Godot thread will pause its execution while waiting for the process to terminate. The shell output of the process will be written to the [code]output[/code] array as a single string. When the process terminates, the Godot thread will resume execution. If [code]blocking[/code] is [code]false[/code], the Godot thread will continue while the new process runs. It is not possible to retrieve the shell output in non-blocking mode, so [code]output[/code] will be empty. + On Windows, if [code]open_console[/code] is [code]true[/code] and process is console app, new terminal window will be opened, it's ignored on other platforms. The return value also depends on the blocking mode. When blocking, the method will return an exit code of the process. When non-blocking, the method returns a process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). If the process forking (non-blocking) or opening (blocking) fails, the method will return [code]-1[/code] or another exit code. Example of blocking mode and retrieving the shell output: [codeblock] diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index c358ee93f1c..73bfb8ec8f0 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -260,7 +260,7 @@ uint64_t OS_Unix::get_ticks_usec() const { return longtime; } -Error OS_Unix::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_Unix::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { #ifdef __EMSCRIPTEN__ // Don't compile this code at all to avoid undefined references. // Actual virtual call goes to OS_JavaScript. diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index ffbfccc1f7b..f5dee44fcf0 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -84,7 +84,7 @@ public: virtual void delay_usec(uint32_t p_usec) const; virtual uint64_t get_ticks_usec() const; - virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr); + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false); virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 6d2321aeb54..00680ac4d83 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2803,11 +2803,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { OS::get_singleton()->set_window_fullscreen(!OS::get_singleton()->is_window_fullscreen()); } break; - case SETTINGS_TOGGLE_CONSOLE: { - bool was_visible = OS::get_singleton()->is_console_visible(); - OS::get_singleton()->set_console_visible(!was_visible); - EditorSettings::get_singleton()->set_setting("interface/editor/hide_console_window", was_visible); - } break; case EDITOR_SCREENSHOT: { screenshot_timer->start(); } break; @@ -6437,9 +6432,6 @@ EditorNode::EditorNode() { p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_CMD | KEY_MASK_CTRL | KEY_F), SETTINGS_TOGGLE_FULLSCREEN); #else p->add_shortcut(ED_SHORTCUT("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KEY_MASK_SHIFT | KEY_F11), SETTINGS_TOGGLE_FULLSCREEN); -#endif -#ifdef WINDOWS_ENABLED - p->add_item(TTR("Toggle System Console"), SETTINGS_TOGGLE_CONSOLE); #endif p->add_separator(); diff --git a/editor/editor_node.h b/editor/editor_node.h index bbd1ed7dfd2..33e1bdcf00c 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -190,7 +190,6 @@ private: SETTINGS_MANAGE_FEATURE_PROFILES, SETTINGS_INSTALL_ANDROID_BUILD_TEMPLATE, SETTINGS_PICK_MAIN_SCENE, - SETTINGS_TOGGLE_CONSOLE, SETTINGS_TOGGLE_FULLSCREEN, SETTINGS_HELP, SCENE_TAB_CLOSE, diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index e22a1585c6c..74271da0fba 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -335,7 +335,6 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { hints["interface/editor/unfocused_low_processor_mode_sleep_usec"] = PropertyInfo(Variant::REAL, "interface/editor/unfocused_low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "1,1000000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED); _initial_set("interface/editor/separate_distraction_mode", false); _initial_set("interface/editor/automatically_open_screenshots", true); - _initial_set("interface/editor/hide_console_window", false); _initial_set("interface/editor/save_each_scene_on_quit", true); // Regression _initial_set("interface/editor/quit_confirmation", true); diff --git a/main/main.cpp b/main/main.cpp index c43980fbaf0..a5b03576c8d 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2089,10 +2089,6 @@ bool Main::start() { } if (project_manager || editor) { - // Hide console window if requested (Windows-only). - bool hide_console = EditorSettings::get_singleton()->get_setting("interface/editor/hide_console_window"); - OS::get_singleton()->set_console_visible(!hide_console); - // Load SSL Certificates from Editor Settings (or builtin) Crypto::load_default_certificates(EditorSettings::get_singleton()->get_setting("network/ssl/editor_ssl_certificates").operator String()); } diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 951bdeae67c..ea8f56f2578 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -890,7 +890,7 @@ void OS_JavaScript::finalize() { // Miscellaneous -Error OS_JavaScript::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_JavaScript::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { Array args; for (const List::Element *E = p_arguments.front(); E; E = E->next()) { args.push_back(E->get()); diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 208fd73dd35..e050182facb 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -168,7 +168,7 @@ public: virtual MainLoop *get_main_loop() const; bool main_loop_iterate(); - virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL, bool p_open_console = false); virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const; int get_processor_count() const; diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 845788759d4..8b8afb15fa0 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -255,7 +255,7 @@ public: virtual void set_offscreen_gl_current(bool p_current); virtual String get_executable_path() const; - virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr); + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false); virtual LatinKeyboardVariant get_latin_keyboard_variant() const; virtual int keyboard_get_layout_count() const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 87a6f376f02..024bf1b16f5 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -2967,7 +2967,7 @@ String OS_OSX::get_executable_path() const { } } -Error OS_OSX::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_OSX::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { if (@available(macOS 10.15, *)) { NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; // If executable is bundled, always execute editor instances using NSWorkspace to ensure app window is registered and activated correctly. @@ -3014,10 +3014,10 @@ Error OS_OSX::execute(const String &p_path, const List &p_arguments, boo return err; } else { - return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex); + return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex, p_open_console); } } else { - return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex); + return OS_Unix::execute(p_path, p_arguments, p_blocking, r_child_id, r_pipe, r_exitcode, read_stderr, p_pipe_mutex, p_open_console); } } diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index dc941eb49c5..10239734049 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -669,7 +669,7 @@ void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c // TODO } -Error OS_UWP::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_UWP::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { return FAILED; }; diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 51fbf155388..43d110e82fc 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -200,7 +200,7 @@ public: virtual void delay_usec(uint32_t p_usec) const; virtual uint64_t get_ticks_usec() const; - virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL, bool p_open_console = false); virtual Error kill(const ProcessID &p_pid); virtual bool has_environment(const String &p_var) const; diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 56efb5f0a05..b6ef60b2e51 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -183,9 +183,6 @@ def configure_msvc(env, manual_msvc_config): env.Append(CCFLAGS=["/O1"]) env.Append(LINKFLAGS=["/OPT:REF"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) - env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) - elif env["target"] == "release_debug": if env["optimize"] == "speed": # optimize for speed (default) env.Append(CCFLAGS=["/O2"]) @@ -193,15 +190,16 @@ def configure_msvc(env, manual_msvc_config): elif env["optimize"] == "size": # optimize for size env.Append(CCFLAGS=["/O1"]) env.Append(LINKFLAGS=["/OPT:REF"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) elif env["target"] == "debug": env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) - env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) # Allow big objects. Only needed for debug, see MinGW branch for rationale. env.AppendUnique(CCFLAGS=["/bigobj"]) env.Append(LINKFLAGS=["/DEBUG"]) + env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) + env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) + if env["debug_symbols"]: env.AppendUnique(CCFLAGS=["/Zi", "/FS"]) env.AppendUnique(LINKFLAGS=["/DEBUG"]) @@ -316,8 +314,6 @@ def configure_mingw(env): env.Append(CCFLAGS=["-O2"]) else: # optimize for size env.Prepend(CCFLAGS=["-Os"]) - env.Append(LINKFLAGS=["-Wl,--subsystem,windows"]) - if env["debug_symbols"]: env.Prepend(CCFLAGS=["-g2"]) @@ -337,6 +333,8 @@ def configure_mingw(env): # and are the only ones with too big objects). env.Append(CCFLAGS=["-Wa,-mbig-obj"]) + env.Append(LINKFLAGS=["-Wl,--subsystem,windows"]) + ## Compiler configuration if os.name == "nt": diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 6cbd3373b37..85081ebcb53 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -108,69 +108,17 @@ static String format_error_message(DWORD id) { extern HINSTANCE godot_hinstance; void RedirectIOToConsole() { - int hConHandle; + if (AttachConsole(ATTACH_PARENT_PROCESS)) { + FILE *fpstdin = stdin; + FILE *fpstdout = stdout; + FILE *fpstderr = stderr; - intptr_t lStdHandle; + freopen_s(&fpstdin, "CONIN$", "r", stdin); + freopen_s(&fpstdout, "CONOUT$", "w", stdout); + freopen_s(&fpstderr, "CONOUT$", "w", stderr); - 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, NULL, _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, NULL, _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, NULL, _IONBF, 0); - - // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog - - // point to console as well + printf("\n"); // Make sure our output is starting from the new line. + } } BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { @@ -210,7 +158,8 @@ void OS_Windows::initialize_core() { last_button_state = 0; restore_mouse_trails = 0; - //RedirectIOToConsole(); + RedirectIOToConsole(); + maximized = false; minimized = false; borderless = false; @@ -2247,31 +2196,6 @@ bool OS_Windows::is_window_focused() const { return window_focused; } -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; - } -} - -void OS_Windows::set_console_visible(bool p_enabled) { - if (console_visible == p_enabled) - return; - - if (!_is_win11_terminal()) { - // GetConsoleWindow is not supported by the Windows Terminal. - ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE); - console_visible = p_enabled; - } -} - -bool OS_Windows::is_console_visible() const { - return console_visible; -} - bool OS_Windows::get_window_per_pixel_transparency_enabled() const { if (!is_layered_allowed()) return false; @@ -2815,43 +2739,9 @@ 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 &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { +Error OS_Windows::execute(const String &p_path, const List &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) { String path = p_path.replace("/", "\\"); - if (p_blocking && r_pipe) { - String argss = _quote_command_line_argument(path); - for (const List::Element *E = p_arguments.front(); E; E = E->next()) { - argss += " " + _quote_command_line_argument(E->get()); - } - - if (read_stderr) { - argss += " 2>&1"; // Read stderr too - } - // Note: _wpopen is calling command as "cmd.exe /c argss", instead of executing it directly, add extra quotes around full command, to prevent it from stripping quotes in the command. - argss = _quote_command_line_argument(argss); - - FILE *f = _wpopen(argss.c_str(), L"r"); - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); - - char buf[65535]; - while (fgets(buf, 65535, f)) { - if (p_pipe_mutex) { - p_pipe_mutex->lock(); - } - (*r_pipe) += String::utf8(buf); - if (p_pipe_mutex) { - p_pipe_mutex->unlock(); - } - } - - int rv = _pclose(f); - if (r_exitcode) { - *r_exitcode = rv; - } - - return OK; - } - String cmdline = _quote_command_line_argument(path); const List::Element *I = p_arguments.front(); while (I) { @@ -2871,17 +2761,62 @@ Error OS_Windows::execute(const String &p_path, const List &p_arguments, modstr.write[i] = cmdline[i]; } - DWORD creation_flags = NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW; - if (p_path == get_executable_path() && GetConsoleWindow() != NULL && _is_win11_terminal()) { - // Open a new terminal as a workaround for Windows Terminal bug. - creation_flags |= CREATE_NEW_CONSOLE; + bool inherit_handles = false; + HANDLE pipe[2] = { NULL, NULL }; + if (p_blocking && r_pipe) { + // Create pipe for StdOut and StdErr. + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + sa.lpSecurityDescriptor = NULL; + + 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) { + pi.si.hStdError = pipe[1]; + } + inherit_handles = true; + } + DWORD creaton_flags = NORMAL_PRIORITY_CLASS; + if (p_open_console) { + creaton_flags |= CREATE_NEW_CONSOLE; + } else { + creaton_flags |= CREATE_NO_WINDOW; } - int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, 0, creation_flags, NULL, NULL, si_w, &pi.pi); + int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, inherit_handles, creaton_flags, NULL, NULL, si_w, &pi.pi); + if (!ret && r_pipe) { + CloseHandle(pipe[0]); // Cleanup pipe handles. + CloseHandle(pipe[1]); + } ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); if (p_blocking) { - WaitForSingleObject(pi.pi.hProcess, INFINITE); + 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, read); + if (p_pipe_mutex) { + p_pipe_mutex->unlock(); + } + }; + CloseHandle(pipe[0]); // Close pipe read handle. + } else { + WaitForSingleObject(pi.pi.hProcess, INFINITE); + } + if (r_exitcode) { DWORD ret2; GetExitCodeProcess(pi.pi.hProcess, &ret2); @@ -3689,7 +3624,6 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) { minimized = false; was_maximized = false; window_focused = true; - console_visible = IsWindowVisible(GetConsoleWindow()); //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index c45fb1aa2c6..993156dd604 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -371,8 +371,6 @@ class OS_Windows : public OS { CrashHandler crash_handler; - bool _is_win11_terminal() const; - void _drag_event(float p_x, float p_y, int idx); void _touch_event(bool p_pressed, float p_x, float p_y, int idx); @@ -414,7 +412,6 @@ protected: bool minimized; bool borderless; bool window_focused; - bool console_visible; bool was_maximized; public: @@ -469,8 +466,6 @@ public: virtual void set_window_always_on_top(bool p_enabled); virtual bool is_window_always_on_top() const; virtual bool is_window_focused() const; - virtual void set_console_visible(bool p_enabled); - virtual bool is_console_visible() const; virtual void request_attention(); virtual void *get_native_handle(int p_handle_type); @@ -501,7 +496,7 @@ public: virtual void delay_usec(uint32_t p_usec) const; virtual uint64_t get_ticks_usec() const; - virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL, bool p_open_console = false); virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const;