Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OS] Add functions to determine standard I/O device type. #91201

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/core_bind.compat.inc
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ void Semaphore::_bind_compatibility_methods() {

// OS

String OS::_read_string_from_stdin_bind_compat_91201() {
return read_string_from_stdin(1024);
}

Dictionary OS::_execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments) {
return execute_with_pipe(p_path, p_arguments, true);
}

void OS::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("read_string_from_stdin"), &OS::_read_string_from_stdin_bind_compat_91201);
ClassDB::bind_compatibility_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::_execute_with_pipe_bind_compat_94434);
}

Expand Down
34 changes: 31 additions & 3 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,24 @@ Error OS::shell_show_in_file_manager(const String &p_path, bool p_open_folder) {
return ::OS::get_singleton()->shell_show_in_file_manager(p_path, p_open_folder);
}

String OS::read_string_from_stdin() {
return ::OS::get_singleton()->get_stdin_string();
String OS::read_string_from_stdin(int64_t p_buffer_size) {
return ::OS::get_singleton()->get_stdin_string(p_buffer_size);
}

PackedByteArray OS::read_buffer_from_stdin(int64_t p_buffer_size) {
return ::OS::get_singleton()->get_stdin_buffer(p_buffer_size);
}

OS::StdHandleType OS::get_stdin_type() const {
return (OS::StdHandleType)::OS::get_singleton()->get_stdin_type();
}

OS::StdHandleType OS::get_stdout_type() const {
return (OS::StdHandleType)::OS::get_singleton()->get_stdout_type();
}

OS::StdHandleType OS::get_stderr_type() const {
return (OS::StdHandleType)::OS::get_singleton()->get_stderr_type();
}

int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
Expand Down Expand Up @@ -628,7 +644,13 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_system_font_path", "font_name", "weight", "stretch", "italic"), &OS::get_system_font_path, DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_system_font_path_for_text", "font_name", "text", "locale", "script", "weight", "stretch", "italic"), &OS::get_system_font_path_for_text, DEFVAL(String()), DEFVAL(String()), DEFVAL(400), DEFVAL(100), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);

ClassDB::bind_method(D_METHOD("read_string_from_stdin", "buffer_size"), &OS::read_string_from_stdin);
ClassDB::bind_method(D_METHOD("read_buffer_from_stdin", "buffer_size"), &OS::read_buffer_from_stdin);
ClassDB::bind_method(D_METHOD("get_stdin_type"), &OS::get_stdin_type);
ClassDB::bind_method(D_METHOD("get_stdout_type"), &OS::get_stdout_type);
ClassDB::bind_method(D_METHOD("get_stderr_type"), &OS::get_stderr_type);

ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL_ARRAY, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments", "blocking"), &OS::execute_with_pipe, DEFVAL(true));
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
Expand Down Expand Up @@ -720,6 +742,12 @@ void OS::_bind_methods() {
BIND_ENUM_CONSTANT(SYSTEM_DIR_MUSIC);
BIND_ENUM_CONSTANT(SYSTEM_DIR_PICTURES);
BIND_ENUM_CONSTANT(SYSTEM_DIR_RINGTONES);

BIND_ENUM_CONSTANT(STD_HANDLE_INVALID);
BIND_ENUM_CONSTANT(STD_HANDLE_CONSOLE);
BIND_ENUM_CONSTANT(STD_HANDLE_FILE);
BIND_ENUM_CONSTANT(STD_HANDLE_PIPE);
BIND_ENUM_CONSTANT(STD_HANDLE_UNKNOWN);
}

////// Geometry2D //////
Expand Down
18 changes: 17 additions & 1 deletion core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class OS : public Object {
#ifndef DISABLE_DEPRECATED
Dictionary _execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments);

String _read_string_from_stdin_bind_compat_91201();
static void _bind_compatibility_methods();
#endif

Expand All @@ -146,6 +147,14 @@ class OS : public Object {
PackedByteArray get_entropy(int p_bytes);
String get_system_ca_certificates();

enum StdHandleType {
STD_HANDLE_INVALID,
STD_HANDLE_CONSOLE,
STD_HANDLE_FILE,
STD_HANDLE_PIPE,
STD_HANDLE_UNKNOWN,
};

virtual PackedStringArray get_connected_midi_inputs();
virtual void open_midi_inputs();
virtual void close_midi_inputs();
Expand All @@ -166,7 +175,13 @@ class OS : public Object {
String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const;
String get_executable_path() const;
String read_string_from_stdin();

String read_string_from_stdin(int64_t p_buffer_size = 1024);
PackedByteArray read_buffer_from_stdin(int64_t p_buffer_size = 1024);
StdHandleType get_stdin_type() const;
StdHandleType get_stdout_type() const;
StdHandleType get_stderr_type() const;

int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = ClassDB::default_array_arg, bool p_read_stderr = false, bool p_open_console = false);
Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true);
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
Expand Down Expand Up @@ -642,6 +657,7 @@ VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags);

VARIANT_ENUM_CAST(core_bind::OS::RenderingDriver);
VARIANT_ENUM_CAST(core_bind::OS::SystemDir);
VARIANT_ENUM_CAST(core_bind::OS::StdHandleType);

VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation);
VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyJoinType);
Expand Down
17 changes: 15 additions & 2 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,15 @@ class OS {
enum RenderThreadMode {
RENDER_THREAD_UNSAFE,
RENDER_THREAD_SAFE,
RENDER_SEPARATE_THREAD
RENDER_SEPARATE_THREAD,
};

enum StdHandleType {
STD_HANDLE_INVALID,
STD_HANDLE_CONSOLE,
STD_HANDLE_FILE,
STD_HANDLE_PIPE,
STD_HANDLE_UNKNOWN,
};

protected:
Expand Down Expand Up @@ -146,7 +154,12 @@ class OS {
void print_rich(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;

virtual String get_stdin_string() = 0;
virtual String get_stdin_string(int64_t p_buffer_size = 1024) = 0;
virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) = 0;

virtual StdHandleType get_stdin_type() const { return STD_HANDLE_UNKNOWN; }
virtual StdHandleType get_stdout_type() const { return STD_HANDLE_UNKNOWN; }
virtual StdHandleType get_stderr_type() const { return STD_HANDLE_UNKNOWN; }

virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) = 0; // Should return cryptographically-safe random bytes.
virtual String get_system_ca_certificates() { return ""; } // Concatenated certificates in PEM format.
Expand Down
61 changes: 55 additions & 6 deletions doc/classes/OS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<return type="void" />
<description>
Shuts down the system MIDI driver. Godot will no longer receive [InputEventMIDI]. See also [method open_midi_inputs] and [method get_connected_midi_inputs].
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
</description>
</method>
<method name="crash">
Expand Down Expand Up @@ -244,7 +244,7 @@
<return type="PackedStringArray" />
<description>
Returns an array of connected MIDI device names, if they exist. Returns an empty array if the system MIDI driver has not previously been initialized with [method open_midi_inputs]. See also [method close_midi_inputs].
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
</description>
</method>
<method name="get_data_dir" qualifiers="const">
Expand Down Expand Up @@ -466,6 +466,24 @@
Returns the amount of static memory being used by the program in bytes. Only works in debug builds.
</description>
</method>
<method name="get_stderr_type" qualifiers="const">
<return type="int" enum="OS.StdHandleType" />
<description>
Returns type of the standard error device.
</description>
</method>
<method name="get_stdin_type" qualifiers="const">
<return type="int" enum="OS.StdHandleType" />
<description>
Returns type of the standard input device.
</description>
</method>
<method name="get_stdout_type" qualifiers="const">
<return type="int" enum="OS.StdHandleType" />
<description>
Returns type of the standard output device.
</description>
</method>
<method name="get_system_ca_certificates">
<return type="String" />
<description>
Expand Down Expand Up @@ -680,15 +698,31 @@
<return type="void" />
<description>
Initializes the singleton for the system MIDI driver, allowing Godot to receive [InputEventMIDI]. See also [method get_connected_midi_inputs] and [method close_midi_inputs].
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
</description>
</method>
<method name="read_buffer_from_stdin">
<return type="PackedByteArray" />
<param index="0" name="buffer_size" type="int" />
<description>
Reads a user input as raw data from the standard input. This operation can be [i]blocking[/i], which causes the window to freeze if [method read_string_from_stdin] is called on the main thread.
- If standard input is console, this method will block until the program receives a line break in standard input (usually by the user pressing [kbd]Enter[/kbd]).
- If standard input is pipe, this method will block until a specific amount of data is read or pipe is closed.
- If standard input is a file, this method will read a specific amount of data (or less if end-of-file is reached) and return immediately.
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
[b]Note:[/b] On exported Windows builds, run the console wrapper executable to access the terminal. If standard input is console, calling this method without console wrapped will freeze permanently. If standard input is pipe or file, it can be used without console wrapper. If you need a single executable with full console support, use a custom build compiled with the [code]windows_subsystem=console[/code] flag.
</description>
</method>
<method name="read_string_from_stdin">
<return type="String" />
<param index="0" name="buffer_size" type="int" />
<description>
Reads a user input string from the standard input (usually the terminal). This operation is [i]blocking[/i], which causes the window to freeze if [method read_string_from_stdin] is called on the main thread. The thread calling [method read_string_from_stdin] will block until the program receives a line break in standard input (usually by the user pressing [kbd]Enter[/kbd]).
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
[b]Note:[/b] On exported Windows builds, run the console wrapper executable to access the terminal. Otherwise, the standard input will not work correctly. If you need a single executable with console support, use a custom build compiled with the [code]windows_subsystem=console[/code] flag.
Reads a user input as a UTF-8 encoded string from the standard input. This operation can be [i]blocking[/i], which causes the window to freeze if [method read_string_from_stdin] is called on the main thread.
- If standard input is console, this method will block until the program receives a line break in standard input (usually by the user pressing [kbd]Enter[/kbd]).
- If standard input is pipe, this method will block until a specific amount of data is read or pipe is closed.
- If standard input is a file, this method will read a specific amount of data (or less if end-of-file is reached) and return immediately.
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
[b]Note:[/b] On exported Windows builds, run the console wrapper executable to access the terminal. If standard input is console, calling this method without console wrapped will freeze permanently. If standard input is pipe or file, it can be used without console wrapper. If you need a single executable with full console support, use a custom build compiled with the [code]windows_subsystem=console[/code] flag.
</description>
</method>
<method name="request_permission">
Expand Down Expand Up @@ -831,5 +865,20 @@
<constant name="SYSTEM_DIR_RINGTONES" value="7" enum="SystemDir">
Refers to the Ringtones directory path.
</constant>
<constant name="STD_HANDLE_INVALID" value="0" enum="StdHandleType">
Standard I/O device is invalid. No data can be received from or sent to these standard I/O devices.
</constant>
<constant name="STD_HANDLE_CONSOLE" value="1" enum="StdHandleType">
Standard I/O device is a console. This typically occurs when Godot is run from a terminal with no redirection. This is also used for all standard I/O devices when running Godot from the editor, at least on desktop platforms.
</constant>
<constant name="STD_HANDLE_FILE" value="2" enum="StdHandleType">
Standard I/O device is a regular file. This typically occurs with redirection from a terminal, e.g. [code]godot &gt; stdout.txt[/code], [code]godot &lt; stdin.txt[/code] or [code]godot &gt; stdout_stderr.txt 2&gt;&amp;1[/code].
</constant>
<constant name="STD_HANDLE_PIPE" value="3" enum="StdHandleType">
Standard I/O device is a FIFO/pipe. This typically occurs with pipe usage from a terminal, e.g. [code]echo "Hello" | godot[/code].
</constant>
<constant name="STD_HANDLE_UNKNOWN" value="4" enum="StdHandleType">
Standard I/O device type is unknown.
</constant>
</constants>
</class>
85 changes: 82 additions & 3 deletions drivers/unix/os_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
Expand Down Expand Up @@ -182,9 +183,87 @@ Vector<String> OS_Unix::get_video_adapter_driver_info() const {
return Vector<String>();
}

String OS_Unix::get_stdin_string() {
char buff[1024];
return String::utf8(fgets(buff, 1024, stdin));
String OS_Unix::get_stdin_string(int64_t p_buffer_size) {
Vector<uint8_t> data;
data.resize(p_buffer_size);
if (fgets((char *)data.ptrw(), data.size(), stdin)) {
return String::utf8((char *)data.ptr());
}
return String();
}

PackedByteArray OS_Unix::get_stdin_buffer(int64_t p_buffer_size) {
Vector<uint8_t> data;
data.resize(p_buffer_size);
size_t sz = fread((void *)data.ptrw(), 1, data.size(), stdin);
if (sz > 0) {
data.resize(sz);
return data;
}
return PackedByteArray();
}

OS_Unix::StdHandleType OS_Unix::get_stdin_type() const {
int h = fileno(stdin);
if (h == -1) {
return STD_HANDLE_INVALID;
}

if (isatty(h)) {
return STD_HANDLE_CONSOLE;
}
struct stat statbuf;
if (fstat(h, &statbuf) < 0) {
return STD_HANDLE_UNKNOWN;
}
if (S_ISFIFO(statbuf.st_mode)) {
return STD_HANDLE_PIPE;
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
return STD_HANDLE_FILE;
}
return STD_HANDLE_UNKNOWN;
}

OS_Unix::StdHandleType OS_Unix::get_stdout_type() const {
int h = fileno(stdout);
if (h == -1) {
return STD_HANDLE_INVALID;
}

if (isatty(h)) {
return STD_HANDLE_CONSOLE;
}
struct stat statbuf;
if (fstat(h, &statbuf) < 0) {
return STD_HANDLE_UNKNOWN;
}
if (S_ISFIFO(statbuf.st_mode)) {
return STD_HANDLE_PIPE;
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
return STD_HANDLE_FILE;
}
return STD_HANDLE_UNKNOWN;
}

OS_Unix::StdHandleType OS_Unix::get_stderr_type() const {
int h = fileno(stderr);
if (h == -1) {
return STD_HANDLE_INVALID;
}

if (isatty(h)) {
return STD_HANDLE_CONSOLE;
}
struct stat statbuf;
if (fstat(h, &statbuf) < 0) {
return STD_HANDLE_UNKNOWN;
}
if (S_ISFIFO(statbuf.st_mode)) {
return STD_HANDLE_PIPE;
} else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
return STD_HANDLE_FILE;
}
return STD_HANDLE_UNKNOWN;
}

Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) {
Expand Down
6 changes: 5 additions & 1 deletion drivers/unix/os_unix.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ class OS_Unix : public OS {

virtual Vector<String> get_video_adapter_driver_info() const override;

virtual String get_stdin_string() override;
virtual String get_stdin_string(int64_t p_buffer_size = 1024) override;
virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) override;
virtual StdHandleType get_stdin_type() const override;
virtual StdHandleType get_stdout_type() const override;
virtual StdHandleType get_stderr_type() const override;

virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;

Expand Down
7 changes: 7 additions & 0 deletions misc/extension_api_validation/4.3-stable.expected
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@ GH-97257
Validate extension JSON: Error: Field 'classes/EditorFeatureProfile/enums/Feature/values/FEATURE_MAX': value changed value in new API, from 8.0 to 9.

New entry to the `EditorFeatureProfile.Feature` enum added. Those need to go before `FEATURE_MAX`, which will always cause a compatibility break.


GH-91201
--------
Validate extension JSON: JSON file: Field was added in a way that breaks compatibility 'classes/OS/methods/read_string_from_stdin': arguments

Added optional argument. Compatibility method registered.
Loading
Loading