diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 9daf58cb713..ba48adff6ab 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -322,6 +322,14 @@ String OS::get_name() const { return ::OS::get_singleton()->get_name(); } +String OS::get_distribution_name() const { + return ::OS::get_singleton()->get_distribution_name(); +} + +String OS::get_version() const { + return ::OS::get_singleton()->get_version(); +} + Vector OS::get_cmdline_args() { List cmdline = ::OS::get_singleton()->get_cmdline_args(); Vector cmdlinev; @@ -535,6 +543,8 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment); ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name); + ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_name); + ClassDB::bind_method(D_METHOD("get_version"), &OS::get_version); ClassDB::bind_method(D_METHOD("get_cmdline_args"), &OS::get_cmdline_args); ClassDB::bind_method(D_METHOD("get_cmdline_user_args"), &OS::get_cmdline_user_args); diff --git a/core/core_bind.h b/core/core_bind.h index cd382d29156..0f85e473a51 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -193,6 +193,8 @@ public: bool set_environment(const String &p_var, const String &p_value) const; String get_name() const; + String get_distribution_name() const; + String get_version() const; Vector get_cmdline_args(); Vector get_cmdline_user_args(); diff --git a/core/os/os.h b/core/os/os.h index 363697ea30b..6944d29eebc 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -161,6 +161,8 @@ public: virtual bool set_environment(const String &p_var, const String &p_value) const = 0; virtual String get_name() const = 0; + virtual String get_distribution_name() const = 0; + virtual String get_version() const = 0; virtual List get_cmdline_args() const { return _cmdline; } virtual List get_cmdline_user_args() const { return _user_args; } virtual List get_cmdline_platform_args() const { return List(); } diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 059766656fb..54f31d79187 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -216,6 +216,15 @@ Not to be confused with [method get_user_data_dir], which returns the [i]project-specific[/i] user data path. + + + + Returns the name of the distribution for Linux and BSD platforms (e.g. Ubuntu, Manjaro, OpenBSD, etc.). + Returns the same value as [method get_name] for stock Android ROMs, but attempts to return the custom ROM name for popular Android derivatives such as LineageOS. + Returns the same value as [method get_name] for other platforms. + [b]Note:[/b] This method is not supported on the web platform. It returns an empty string. + + @@ -430,6 +439,18 @@ Not to be confused with [method get_data_dir], which returns the [i]global[/i] (non-project-specific) user home directory. + + + + Returns the exact production and build version of the operating system. This is different from the branded version used in marketing. This helps to distinguish between different releases of operating systems, including minor versions, and insider and custom builds. + For Windows, the major and minor version are returned, as well as the build number. For example, the returned string can look like [code]10.0.9926[/code] for a build of Windows 10, and it can look like [code]6.1.7601[/code] for a build of Windows 7 SP1. + For rolling distributions, such as Arch Linux, an empty string is returned. + For macOS and iOS, the major and minor version are returned, as well as the patch number. + For UWP, the device family version is returned. + For Android, the SDK version and the incremental build number are returned. If it's a custom ROM, it attempts to return its version instead. + [b]Note:[/b] This method is not supported on the web platform. It returns an empty string. + + diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index beb28129996..ab298a0e49f 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -194,6 +194,14 @@ String OS_Unix::get_name() const { return "Unix"; } +String OS_Unix::get_distribution_name() const { + return ""; +} + +String OS_Unix::get_version() const { + return ""; +} + double OS_Unix::get_unix_time() const { struct timeval tv_now; gettimeofday(&tv_now, nullptr); diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index b4c844bfef6..8ef650f28b8 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -62,6 +62,8 @@ public: virtual Error set_cwd(const String &p_cwd) override; virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; virtual DateTime get_datetime(bool p_utc) const override; virtual TimeZoneInfo get_time_zone_info() const override; diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 142dc54c451..4469c7a0f7c 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -44,6 +44,7 @@ #include "net_socket_android.h" #include +#include #include "java_godot_io_wrapper.h" #include "java_godot_wrapper.h" @@ -175,6 +176,79 @@ String OS_Android::get_name() const { return "Android"; } +String OS_Android::get_system_property(const char *key) const { + static String value; + char value_str[PROP_VALUE_MAX]; + if (__system_property_get(key, value_str)) { + value = String(value_str); + } + return value; +} + +String OS_Android::get_distribution_name() const { + if (!get_system_property("ro.havoc.version").is_empty()) { + return "Havoc OS"; + } else if (!get_system_property("org.pex.version").is_empty()) { // Putting before "Pixel Experience", because it's derivating from it. + return "Pixel Extended"; + } else if (!get_system_property("org.pixelexperience.version").is_empty()) { + return "Pixel Experience"; + } else if (!get_system_property("ro.potato.version").is_empty()) { + return "POSP"; + } else if (!get_system_property("ro.xtended.version").is_empty()) { + return "Project-Xtended"; + } else if (!get_system_property("org.evolution.version").is_empty()) { + return "Evolution X"; + } else if (!get_system_property("ro.corvus.version").is_empty()) { + return "Corvus-Q"; + } else if (!get_system_property("ro.pa.version").is_empty()) { + return "Paranoid Android"; + } else if (!get_system_property("ro.crdroid.version").is_empty()) { + return "crDroid Android"; + } else if (!get_system_property("ro.syberia.version").is_empty()) { + return "Syberia Project"; + } else if (!get_system_property("ro.arrow.version").is_empty()) { + return "ArrowOS"; + } else if (!get_system_property("ro.lineage.version").is_empty()) { // Putting LineageOS last, just in case any derivative writes to "ro.lineage.version". + return "LineageOS"; + } + + if (!get_system_property("ro.modversion").is_empty()) { // Handles other Android custom ROMs. + return vformat("%s %s", get_name(), "Custom ROM"); + } + + // Handles stock Android. + return get_name(); +} + +String OS_Android::get_version() const { + const Vector roms = { "ro.havoc.version", "org.pex.version", "org.pixelexperience.version", + "ro.potato.version", "ro.xtended.version", "org.evolution.version", "ro.corvus.version", "ro.pa.version", + "ro.crdroid.version", "ro.syberia.version", "ro.arrow.version", "ro.lineage.version" }; + for (int i = 0; i < roms.size(); i++) { + static String rom_version = get_system_property(roms[i]); + if (!rom_version.is_empty()) { + return rom_version; + } + } + + static String mod_version = get_system_property("ro.modversion"); // Handles other Android custom ROMs. + if (!mod_version.is_empty()) { + return mod_version; + } + + // Handles stock Android. + static String sdk_version = get_system_property("ro.build.version.sdk_int"); + static String build = get_system_property("ro.build.version.incremental"); + if (!sdk_version.is_empty()) { + if (!build.is_empty()) { + return vformat("%s.%s", sdk_version, build); + } + return sdk_version; + } + + return ""; +} + MainLoop *OS_Android::get_main_loop() const { return main_loop; } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 96c06d715c9..d6546a3507a 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -65,6 +65,8 @@ private: GodotJavaWrapper *godot_java = nullptr; GodotIOJavaWrapper *godot_io_java = nullptr; + String get_system_property(const char *key) const; + public: static const char *ANDROID_EXEC_PATH; @@ -93,6 +95,8 @@ public: 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 String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; virtual MainLoop *get_main_loop() const override; void main_loop_begin(); diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h index 3b88f53b6a1..00d91da7716 100644 --- a/platform/ios/os_ios.h +++ b/platform/ios/os_ios.h @@ -100,6 +100,8 @@ public: virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override; virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; virtual String get_model_name() const override; virtual Error shell_open(String p_uri) override; diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index b9d186f3552..a674498620d 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -240,6 +240,15 @@ String OS_IOS::get_name() const { return "iOS"; } +String OS_IOS::get_distribution_name() const { + return get_name(); +} + +String OS_IOS::get_version() const { + NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion; + return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion); +} + String OS_IOS::get_model_name() const { String model = ios->get_model(); if (model != "") { diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index f0d7b6ede56..f4e94f1a919 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #ifdef FONTCONFIG_ENABLED @@ -205,6 +206,42 @@ String OS_LinuxBSD::get_name() const { #endif } +String OS_LinuxBSD::get_systemd_os_release_info_value(const String &key) const { + static String info; + if (info.is_empty()) { + Ref f = FileAccess::open("/etc/os-release", FileAccess::READ); + if (f.is_valid()) { + while (!f->eof_reached()) { + const String line = f->get_line(); + if (line.find(key) != -1) { + return line.split("=")[1].strip_edges(); + } + } + } + } + return info; +} + +String OS_LinuxBSD::get_distribution_name() const { + static String systemd_name = get_systemd_os_release_info_value("NAME"); // returns a value for systemd users, otherwise an empty string. + if (!systemd_name.is_empty()) { + return systemd_name; + } + struct utsname uts; // returns a decent value for BSD family. + uname(&uts); + return uts.sysname; +} + +String OS_LinuxBSD::get_version() const { + static String systemd_version = get_systemd_os_release_info_value("VERSION"); // returns a value for systemd users, otherwise an empty string. + if (!systemd_version.is_empty()) { + return systemd_version; + } + struct utsname uts; // returns a decent value for BSD family. + uname(&uts); + return uts.version; +} + Error OS_LinuxBSD::shell_open(String p_uri) { Error ok; int err_code; diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h index d5b23213164..722d83ba19d 100644 --- a/platform/linuxbsd/os_linuxbsd.h +++ b/platform/linuxbsd/os_linuxbsd.h @@ -67,6 +67,8 @@ class OS_LinuxBSD : public OS_Unix { MainLoop *main_loop = nullptr; + String get_systemd_os_release_info_value(const String &key) const; + protected: virtual void initialize() override; virtual void finalize() override; @@ -77,6 +79,8 @@ protected: public: virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; virtual MainLoop *get_main_loop() const override; diff --git a/platform/macos/os_macos.h b/platform/macos/os_macos.h index 61db99689cc..46e7c17ebe0 100644 --- a/platform/macos/os_macos.h +++ b/platform/macos/os_macos.h @@ -75,6 +75,8 @@ public: virtual List get_cmdline_platform_args() const override; virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index c250a9d71af..ae8534f6ab9 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -134,6 +134,15 @@ String OS_MacOS::get_name() const { return "macOS"; } +String OS_MacOS::get_distribution_name() const { + return get_name(); +} + +String OS_MacOS::get_version() const { + NSOperatingSystemVersion ver = [NSProcessInfo processInfo].operatingSystemVersion; + return vformat("%d.%d.%d", (int64_t)ver.majorVersion, (int64_t)ver.minorVersion, (int64_t)ver.patchVersion); +} + void OS_MacOS::alert(const String &p_alert, const String &p_title) { NSAlert *window = [[NSAlert alloc] init]; NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()]; diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 791328964bd..8050d299f05 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -444,6 +444,16 @@ String OS_UWP::get_name() const { return "UWP"; } +String OS_UWP::get_distribution_name() const { + return get_name(); +} + +String OS_UWP::get_version() const { + winrt::hstring df_version = VersionInfo().DeviceFamilyVersion(); + static String version = String(winrt::to_string(df_version).c_str()); + return version; +} + OS::DateTime OS_UWP::get_datetime(bool p_utc) const { SYSTEMTIME systemtime; if (p_utc) { diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 7d4224cf74c..0c1c4a793a1 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -183,6 +183,8 @@ public: virtual MainLoop *get_main_loop() const; virtual String get_name() const; + virtual String get_distribution_name() const; + virtual String get_version() const; virtual DateTime get_datetime(bool p_utc) const; virtual TimeZoneInfo get_time_zone_info() const; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 2c268ff3d5f..1978ec5ab65 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -290,6 +290,24 @@ String OS_Windows::get_name() const { return "Windows"; } +String OS_Windows::get_distribution_name() const { + return get_name(); +} + +String OS_Windows::get_version() const { + typedef LONG NTSTATUS, *PNTSTATUS; + typedef NTSTATUS(WINAPI * RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + RtlGetVersionPtr version_ptr = (RtlGetVersionPtr)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlGetVersion"); + if (version_ptr != nullptr) { + RTL_OSVERSIONINFOW fow = { 0 }; + fow.dwOSVersionInfoSize = sizeof(fow); + if (version_ptr(&fow) == 0x00000000) { + return vformat("%d.%d.%d", (int64_t)fow.dwMajorVersion, (int64_t)fow.dwMinorVersion, (int64_t)fow.dwBuildNumber); + } + } + return ""; +} + OS::DateTime OS_Windows::get_datetime(bool p_utc) const { SYSTEMTIME systemtime; if (p_utc) { diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 53451b780e0..491de2266f8 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -143,6 +143,8 @@ public: virtual MainLoop *get_main_loop() const override; virtual String get_name() const override; + virtual String get_distribution_name() const override; + virtual String get_version() const override; virtual void initialize_joypads() override {}