mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-27 23:30:47 +01:00
@@ -39,6 +39,11 @@ Features:
|
||||
* Added CPU name and frequency detection support on SPARC. (CPU, Linux)
|
||||
* Added package detection support for CRUX. (Packages, Linux)
|
||||
* Exposed in custom format as `{crux}`.
|
||||
* Improved Android ROM detection (DE, Android)
|
||||
* Added support for HarmonyOS, HarmonyOS NEXT, Flyme, JOYUI, SmartisanOS, realme UI, HydrogenOS, ZUI, ZUXOS, MyOS, NebulaAIOS, ObricUI, MiFavor, LineageOS, PixelExperience, EUI and 360 UI.
|
||||
* Added support for MagicUI 3.x, which stores a bare version number instead of a `MagicUI_x.y.z` string.
|
||||
* Added Samsung OneUI support (#2541)
|
||||
* This is mostly untested due to lack of available devices running these ROMs. Please report any issues you encounter.
|
||||
* Improved COSMIC detection (DE / WM, Linux)
|
||||
* The version is now read from the `COSMIC_VERSION` environment variable when it is set.
|
||||
* Improved accuracy and performance of process name detection in the Top module. (Top, macOS)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "displayserver.h"
|
||||
#include "common/arrutil.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/processing.h"
|
||||
#include "linux/displayserver_linux.h"
|
||||
@@ -135,56 +136,316 @@ static bool detectWithGetprop(FFDisplayServerResult* ds) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool detectDE(FFDisplayServerResult* ds) {
|
||||
if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) // OriginOS 6
|
||||
{
|
||||
ffStrbufAppendC(&ds->dePrettyName, ' ');
|
||||
ffSettingsGetAndroidProperty("ro.vivo.product.version", &ds->dePrettyName); // PD2505D_xxx
|
||||
// Several vendors embed the UI name and its version in `ro.build.display.id` without any
|
||||
// separator, e.g. `MyOS12.0.14_A2121` or `RedMagicOS10.0.24_NX779J`.
|
||||
static bool detectDEFromDisplayId(FFDisplayServerResult* ds, const char* const* names, uint32_t count) {
|
||||
FF_STRBUF_AUTO_DESTROY displayId = ffStrbufCreate();
|
||||
if (!ffSettingsGetAndroidProperty("ro.build.display.id", &displayId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint32_t length = (uint32_t) strlen(names[i]);
|
||||
if (!ffStrbufStartsWithS(&displayId, names[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ffStrbufSubstrBeforeFirstC(&displayId, '_'); // Drop the model suffix
|
||||
if (displayId.length > length) {
|
||||
ffStrbufInsertNC(&displayId, length, 1, ' ');
|
||||
}
|
||||
ffStrbufSet(&ds->dePrettyName, &displayId);
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.magic", &ds->dePrettyName) ||
|
||||
ffSettingsGetAndroidProperty("ro.build.version.emui", &ds->dePrettyName)) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool detectDE(FFDisplayServerResult* ds) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY brand = ffStrbufCreate();
|
||||
|
||||
// `ffSettingsGetAndroidProperty` appends to the given buffer, so the brand has to be read
|
||||
// into its own buffer exactly once: reading it into a shared buffer repeatedly would
|
||||
// concatenate the values and break every comparison against it.
|
||||
ffSettingsGetAndroidProperty("ro.product.brand", &brand);
|
||||
|
||||
// vivo reports the marketing name and version in `ro.vivo.os.build.display.id`,
|
||||
// separated by an underscore (`Funtouch OS_10`, `OriginOS 5`), and the build number
|
||||
// in `ro.vivo.product.version` (`PD2505D_A_9.16.42`).
|
||||
if (ffSettingsGetAndroidProperty("ro.vivo.os.build.display.id", &ds->dePrettyName)) {
|
||||
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
|
||||
if (ffSettingsGetAndroidProperty("ro.vivo.product.version", &buffer)) {
|
||||
ffStrbufAppendC(&ds->dePrettyName, ' ');
|
||||
ffStrbufAppend(&ds->dePrettyName, &buffer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// HarmonyOS 2.0 - 4.x is built on top of the Android framework and keeps reporting an
|
||||
// EMUI version, but it is HarmonyOS. `ro.build.ohos.devicetype` is set only by those
|
||||
// builds, and `hw_sc.build.platform.version` is the HarmonyOS version, while
|
||||
// `ro.build.version.emui` only carries the EMUI compatibility version
|
||||
// (HarmonyOS 2.0 == EMUI 12, 3.0 == 13, 4.0 == 14, 4.2 == 14.2, 4.3 == 15).
|
||||
if (ffSettingsGetAndroidProperty("ro.build.ohos.devicetype", &buffer)) {
|
||||
ffStrbufClear(&buffer);
|
||||
if (!ffSettingsGetAndroidProperty("hw_sc.build.platform.version", &buffer) &&
|
||||
ffSettingsGetAndroidProperty("ro.huawei.build.display.id", &buffer)) {
|
||||
// A few builds leave `hw_sc.build.platform.version` unset and only expose the
|
||||
// version through `ro.huawei.build.display.id`, e.g. `JKM-AL00 2.0.0.263(C00E260R4P3)`
|
||||
ffStrbufSubstrAfterFirstC(&buffer, ' ');
|
||||
ffStrbufSubstrBeforeFirstC(&buffer, '(');
|
||||
ffStrbufSubstrBeforeLastC(&buffer, '.');
|
||||
}
|
||||
if (buffer.length > 0) {
|
||||
ffStrbufSetF(&ds->dePrettyName, "HarmonyOS %s", buffer.chars);
|
||||
} else {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "HarmonyOS");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// HarmonyOS NEXT (5.0 and newer) drops the Android framework. It is the only Huawei
|
||||
// family reporting `ro.build.display.id` as `System 104.5.0.001(60J9)`, and none of its
|
||||
// properties carries the marketing version, so it can only be named without one.
|
||||
if (ffStrbufIgnCaseEqualS(&brand, "HUAWEI") &&
|
||||
ffSettingsGetAndroidProperty("ro.build.display.id", &buffer) &&
|
||||
ffStrbufStartsWithS(&buffer, "System ")) {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "HarmonyOS NEXT");
|
||||
return true;
|
||||
}
|
||||
|
||||
// HONOR reports MagicOS / MagicUI in `ro.build.version.magic`. MagicUI 3.x stores a
|
||||
// bare version number (`3.0.1`) instead of a `MagicUI_x.y.z` string.
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.magic", &ds->dePrettyName)) {
|
||||
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
|
||||
if (!ffStrbufStartsWithS(&ds->dePrettyName, "Magic")) {
|
||||
ffStrbufPrependS(&ds->dePrettyName, "MagicUI ");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.emui", &ds->dePrettyName)) {
|
||||
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Xiaomi HyperOS. `ro.mi.os.version.incremental` is `OS1.0.10.0.TLDCNXM`, while
|
||||
// `ro.build.version.incremental` still starts with `V816` on HyperOS 1.0, so it can
|
||||
// not be used to tell HyperOS and MIUI apart.
|
||||
if (ffSettingsGetAndroidProperty("ro.mi.os.version.name", &ds->dePrettyName)) {
|
||||
// MiUI like
|
||||
ffStrbufClear(&ds->dePrettyName);
|
||||
ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName); // Detail version number
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "OS")) {
|
||||
ds->dePrettyName.chars[0] = 'S';
|
||||
ds->dePrettyName.chars[1] = ' ';
|
||||
ffStrbufPrependS(&ds->dePrettyName, "HyperO");
|
||||
} else if (ffStrbufStartsWithS(&ds->dePrettyName, "V")) {
|
||||
ds->dePrettyName.chars[0] = ' ';
|
||||
ffStrbufPrependS(&ds->dePrettyName, "MiUI");
|
||||
if (ffSettingsGetAndroidProperty("ro.mi.os.version.incremental", &ds->dePrettyName) &&
|
||||
ffStrbufStartsWithS(&ds->dePrettyName, "OS")) {
|
||||
ffStrbufSubstrAfter(&ds->dePrettyName, 1); // Drop the leading "OS"
|
||||
ffStrbufPrependS(&ds->dePrettyName, "HyperOS ");
|
||||
} else {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "HyperOS");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Black Shark runs JOYUI, a MIUI fork, and therefore also sets
|
||||
// `ro.miui.ui.version.name`. `ro.build.version.incremental` is `V11.0.4.0.JOYUI`.
|
||||
if (ffStrbufIgnCaseEqualS(&brand, "blackshark")) {
|
||||
ffStrbufClear(&ds->dePrettyName);
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName)) {
|
||||
ffStrbufSubstrBeforeLastC(&ds->dePrettyName, '.'); // Drop the trailing "JOYUI"
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "V")) {
|
||||
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
|
||||
}
|
||||
ffStrbufPrependS(&ds->dePrettyName, "JOYUI ");
|
||||
} else {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "JOYUI");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// MIUI. `ro.build.version.incremental` is `V14.0.1.0.TJJCNXM` on stable builds and a
|
||||
// bare release date (`21.11.17`) on beta builds.
|
||||
if (ffSettingsGetAndroidProperty("ro.miui.ui.version.name", &buffer)) {
|
||||
ffStrbufClear(&ds->dePrettyName);
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName)) {
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "V")) {
|
||||
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
|
||||
}
|
||||
ffStrbufPrependS(&ds->dePrettyName, "MiUI ");
|
||||
} else {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "MiUI");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.oplusrom", &ds->dePrettyName)) {
|
||||
|
||||
// realme UI is a ColorOS fork and reports both `ro.build.version.realmeui` and
|
||||
// `ro.build.version.oplusrom`; the former is the realme UI version.
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.realmeui", &ds->dePrettyName)) {
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "V")) {
|
||||
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
|
||||
}
|
||||
ffStrbufPrependS(&ds->dePrettyName, "ColorOS");
|
||||
ffStrbufPrependS(&ds->dePrettyName, "realme UI ");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ColorOS 12 and newer report `ro.build.version.oplusrom`, ColorOS 11 and older
|
||||
// report `ro.build.version.opporom`.
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.oplusrom", &ds->dePrettyName) ||
|
||||
ffSettingsGetAndroidProperty("ro.build.version.opporom", &ds->dePrettyName)) {
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "V")) {
|
||||
ffStrbufSubstrAfter(&ds->dePrettyName, 0);
|
||||
}
|
||||
ffStrbufPrependS(&ds->dePrettyName, "ColorOS ");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffSettingsGetAndroidProperty("ro.oxygen.version", &ds->dePrettyName)) {
|
||||
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS");
|
||||
ffStrbufPrependS(&ds->dePrettyName, "OxygenOS ");
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.product.brand", &ds->dePrettyName) && ffStrbufEqualS(&ds->dePrettyName, "asus")) {
|
||||
ffStrbufClear(&ds->dePrettyName);
|
||||
ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName);
|
||||
|
||||
// HydrogenOS is the Chinese counterpart of OxygenOS and reports `ro.rom.version`.
|
||||
if (ffStrbufEqualS(&brand, "OnePlus") &&
|
||||
ffSettingsGetAndroidProperty("ro.rom.version", &ds->dePrettyName)) {
|
||||
ffStrbufPrependS(&ds->dePrettyName, "H2OS ");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffSettingsGetAndroidProperty("ro.build.version.oneui", &ds->dePrettyName)) {
|
||||
// [ro.build.version.oneui]: [50101] => One UI 5.1.1
|
||||
// Samsung encodes the version with a base-100 carry: `major * 10000 + minor * 100 + patch`
|
||||
uint32_t version = (uint32_t) ffStrbufToUInt(&ds->dePrettyName, 0);
|
||||
uint32_t major = version / 10000;
|
||||
uint32_t minor = version / 100 % 100;
|
||||
uint32_t patch = version % 100;
|
||||
if (major == 0) {
|
||||
// Unexpected format, show the raw value
|
||||
ffStrbufPrependS(&ds->dePrettyName, "OneUI ");
|
||||
} else if (patch > 0) {
|
||||
ffStrbufSetF(&ds->dePrettyName, "OneUI %u.%u.%u", major, minor, patch);
|
||||
} else {
|
||||
ffStrbufSetF(&ds->dePrettyName, "OneUI %u.%u", major, minor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Flyme. `ro.build.display.id` is `Flyme 10.5.0.1A`, where the trailing `A` marks a
|
||||
// stable release. `ro.flyme.version.id` holds the same string, except on Flyme 12,
|
||||
// where it is an Android build id instead.
|
||||
if (ffStrbufIgnCaseEqualS(&brand, "meizu")) {
|
||||
if (!ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName) ||
|
||||
!ffStrbufStartsWithS(&ds->dePrettyName, "Flyme")) {
|
||||
ffStrbufSetStatic(&ds->dePrettyName, "Flyme");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// SmartisanOS. `ro.smartisan.version` is `4.2.6-201808311713-user-511` or
|
||||
// `6.6.6.2_TNT-201904101033-user-oce`.
|
||||
if (ffSettingsGetAndroidProperty("ro.smartisan.version", &ds->dePrettyName)) {
|
||||
ffStrbufSubstrBeforeFirstC(&ds->dePrettyName, '_');
|
||||
ffStrbufSubstrBeforeFirstC(&ds->dePrettyName, '-');
|
||||
ffStrbufPrependS(&ds->dePrettyName, "SmartisanOS ");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ZUI / ZUXOS. `ro.com.zui.version` is the internal ZUI version (`17.0` for
|
||||
// ZUXOS 1.1.10.138), while `ro.build.display.id` embeds the marketing name and
|
||||
// version: `TB321FU_CN_OPEN_USER_Q00011.0_V_ZUXOS_1.1.10.138_ST_250626`.
|
||||
if (ffSettingsGetAndroidProperty("ro.com.zui.version", &ds->dePrettyName)) {
|
||||
FF_STRBUF_AUTO_DESTROY displayId = ffStrbufCreate();
|
||||
const char* name = nullptr;
|
||||
if (ffSettingsGetAndroidProperty("ro.build.display.id", &displayId)) {
|
||||
if (ffStrbufSubstrAfterFirstS(&displayId, "_ZUXOS_")) {
|
||||
name = "ZUXOS";
|
||||
} else if (ffStrbufSubstrAfterFirstS(&displayId, "_ZUI_")) {
|
||||
name = "ZUI";
|
||||
}
|
||||
if (name) {
|
||||
ffStrbufSubstrBeforeFirstC(&displayId, '_'); // Drop the trailing "_ST_250626"
|
||||
ffStrbufSetF(&ds->dePrettyName, "%s %s", name, displayId.chars);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ffStrbufPrependS(&ds->dePrettyName, "ZUI "); // Moto builds only expose `ro.com.zui.version`
|
||||
return true;
|
||||
}
|
||||
|
||||
// 360 OS (QiKU) reports `ro.build.uiversion` as `360UI:V3.0`.
|
||||
if (ffSettingsGetAndroidProperty("ro.build.uiversion", &ds->dePrettyName)) {
|
||||
uint32_t index = ffStrbufFirstIndexC(&ds->dePrettyName, ':');
|
||||
if (index < ds->dePrettyName.length) {
|
||||
// `360UI:V3.0` -> `360UI 3.0`
|
||||
ffStrbufRemoveSubstr(&ds->dePrettyName, index, index + 1);
|
||||
if (ds->dePrettyName.chars[index] == 'V') {
|
||||
ffStrbufRemoveSubstr(&ds->dePrettyName, index, index + 1);
|
||||
}
|
||||
ffStrbufInsertNC(&ds->dePrettyName, index, 1, ' ');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// LeEco EUI reports `ro.letv.release.version` as `6.0.030S`, where the trailing `S`
|
||||
// marks a stable release.
|
||||
if (ffSettingsGetAndroidProperty("ro.letv.release.version", &ds->dePrettyName)) {
|
||||
ffStrbufPrependS(&ds->dePrettyName, "EUI ");
|
||||
return true;
|
||||
}
|
||||
|
||||
// nubia / ZTE
|
||||
if (ffStrbufIgnCaseEqualS(&brand, "nubia") || ffStrbufIgnCaseEqualS(&brand, "zte")) {
|
||||
// ObricUI reports `ro.os.ota.version`, e.g.
|
||||
// `1.8.0.2-20260204-125753-RELEASE-user-pacific-b911`
|
||||
if (ffSettingsGetAndroidProperty("ro.os.ota.version", &ds->dePrettyName)) {
|
||||
ffStrbufSubstrBeforeFirstC(&ds->dePrettyName, '-');
|
||||
ffStrbufPrependS(&ds->dePrettyName, "ObricUI ");
|
||||
return true;
|
||||
}
|
||||
|
||||
// MyOS, NebulaAIOS and RedMagicOS embed the name and version in `ro.build.display.id`
|
||||
static const char* const displayIdNames[] = { "RedMagicOS", "NebulaAIOS", "MyOS" };
|
||||
if (detectDEFromDisplayId(ds, displayIdNames, ARRAY_SIZE(displayIdNames))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The older nubiaUI stores them in `ro.build.nubia.rom.name` and `.code`
|
||||
if (ffSettingsGetAndroidProperty("ro.build.nubia.rom.name", &ds->dePrettyName)) {
|
||||
if (ffSettingsGetAndroidProperty("ro.build.nubia.rom.code", &buffer)) {
|
||||
if (ffStrbufStartsWithS(&buffer, "V")) {
|
||||
ffStrbufSubstrAfter(&buffer, 0); // `V1.0` -> `1.0`
|
||||
}
|
||||
ffStrbufAppendC(&ds->dePrettyName, ' ');
|
||||
ffStrbufAppend(&ds->dePrettyName, &buffer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// MiFavor is the UI of the pre-MyOS ZTE phones; `ro.build.MiFavor_version` is a
|
||||
// bare version number (`4.0`). Note that MyOS, NebulaAIOS and RedMagicOS reuse
|
||||
// this property for their own version, so they are checked first.
|
||||
if (ffSettingsGetAndroidProperty("ro.build.MiFavor_version", &ds->dePrettyName)) {
|
||||
ffStrbufPrependS(&ds->dePrettyName, "MiFavor ");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// LineageOS and other AOSP-based distributions
|
||||
if (ffSettingsGetAndroidProperty("ro.lineage.build.version", &ds->dePrettyName)) {
|
||||
ffStrbufPrependS(&ds->dePrettyName, "LineageOS ");
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("org.pixelexperience.version.display", &ds->dePrettyName)) {
|
||||
// PixelExperience_Plus_whyred-13.0-20230325-0421-OFFICIAL
|
||||
ffStrbufSubstrBeforeFirstC(&ds->dePrettyName, '-');
|
||||
ffStrbufSubstrBeforeLastC(&ds->dePrettyName, '_');
|
||||
ffStrbufReplaceAllC(&ds->dePrettyName, '_', ' ');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ffStrbufEqualS(&brand, "asus") &&
|
||||
ffSettingsGetAndroidProperty("ro.build.version.incremental", &ds->dePrettyName)) {
|
||||
return true;
|
||||
}
|
||||
if (ffSettingsGetAndroidProperty("ro.build.display.id", &ds->dePrettyName)) {
|
||||
if (ffStrbufStartsWithS(&ds->dePrettyName, "RedMagicOS")) {
|
||||
ffStrbufInsertNC(&ds->dePrettyName, strlen("RedMagicOS"), 1, ' ');
|
||||
}
|
||||
|
||||
// Google Pixel uses native Android
|
||||
// Google Pixel and other devices running native Android
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user