diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7140fbd5ba..f743443e6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -274,7 +274,7 @@ jobs: macos-universal: name: macOS-universal - runs-on: macos-12 + runs-on: macos-latest permissions: security-events: write contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 5391125bc1..c27b86326c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +# 2.31.0 + +Bugfixes: +* Improve performance of media detection; fix musikcube detection (Media, Linux) + * After the change, `general.processingTimeout` will also control the timeout of dbus remote calls +* Fix invalid variable names (#1408, Users) +* Change physical size detection to use basic display parameters (#1406) +* Fix possible sigfaults when detecting displays (#1393) +* Fix Nvidia card type detection +* Fix wl-restart parsing (#1422, WM, Linux) +* Fix syntax error in completion file (#1421) +* Fix hunging when using `ssh-agent` as command text (#1418, Command, macOS) + +Features: +* Remove support of xcb & xlib and xrandr extension is always required (Display) +* Support preferred resolution & refresh rate detection + * On macOS there is no preferred resolution reported and maximum available resolution is reported instead. + * `--display-format {preferred-width}x{preferred-height}@{preferred-refresh-rate}` +* Report scale factor in custom format (Display) + * `--display-format {scale-factor}` +* Detect current Wi-Fi channel and maximum frequency (Wifi) +* Report processor package count (#1413, CPU) +* Remove duplicate whitespaces in CPU name +* Support sakura terminal version & font detection (Terminal / TerminalFont, Linux) + +Logo: +* Fix LMDE +* Update MidOS +* Add Windows Server 2025 + # 2.30.1 Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a0cca18a6..12041866ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12.0) # target_link_libraries with OBJECT libs & project homepage url project(fastfetch - VERSION 2.30.1 + VERSION 2.31.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -57,9 +57,7 @@ include(CMakeDependentOption) cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR FreeBSD OR OpenBSD OR NetBSD OR WIN32 OR ANDROID OR SunOS" OFF) cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD" OFF) cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_XCB "Enable xcb" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) -cmake_dependent_option(ENABLE_X11 "Enable x11" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) cmake_dependent_option(ENABLE_DRM_AMDGPU "Enable libdrm_amdgpu" ON "LINUX" OFF) cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS" OFF) @@ -1222,6 +1220,9 @@ endif() if(NOT BINARY_LINK_TYPE STREQUAL "dlopen") message(STATUS "Enabling custom link type: ${BINARY_LINK_TYPE}") target_compile_definitions(libfastfetch PRIVATE FF_DISABLE_DLOPEN=1) + if(NOT WIN32) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--copy-dt-needed-entries") + endif() endif() function(ff_lib_enable VARNAME PKGCONFIG_NAMES CMAKE_NAME) @@ -1277,18 +1278,10 @@ ff_lib_enable(XCB_RANDR "xcb-randr" "XcbRandr" ) -ff_lib_enable(XCB - "xcb" - "Xcb" -) ff_lib_enable(XRANDR "xrandr" "XRandr" ) -ff_lib_enable(X11 - "x11" - "X11" -) ff_lib_enable(DRM "libdrm" "Libdrm" diff --git a/completions/fastfetch.zsh b/completions/fastfetch.zsh index 5688c17458..08b92c90f2 100644 --- a/completions/fastfetch.zsh +++ b/completions/fastfetch.zsh @@ -26,7 +26,7 @@ def main(): for flag in data[key]: if flag["long"] == "logo-color-[1-9]": for i in range(1, 10): - command_prefix = f"--logo-color-{i}[{flag["desc"]} ({i})]" + command_prefix = f"--logo-color-{i}[{flag['desc']} ({i})]" print_command(command_prefix, flag) continue @@ -34,11 +34,11 @@ def main(): continue if "short" in flag: - command_prefix = f"-{flag["short"]}[{flag["desc"]}]" + command_prefix = f"-{flag['short']}[{flag['desc']}]" print_command(command_prefix, flag) if "long" in flag: - command_prefix = f"--{flag["long"]}[{flag["desc"]}]" + command_prefix = f"--{flag['long']}[{flag['desc']}]" print_command(command_prefix, flag) diff --git a/debian/changelog b/debian/changelog index 6ef906c917..bf1ef555e6 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +fastfetch (2.30.1) jammy; urgency=medium + + * Update to 2.30.1 + + -- Carter Li Mon, 18 Nov 2024 15:40:48 +0800 + fastfetch (2.30.0) jammy; urgency=medium * Update to 2.30.0 diff --git a/debian/files b/debian/files index a935a0912b..6cd2ef9b59 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -fastfetch_2.30.0_source.buildinfo universe/utils optional +fastfetch_2.30.1_source.buildinfo universe/utils optional diff --git a/presets/ci.jsonc b/presets/ci.jsonc index 4380541358..eb3a0c2195 100644 --- a/presets/ci.jsonc +++ b/presets/ci.jsonc @@ -87,8 +87,8 @@ "opengl", "opencl", "users", - "bluetooth", - "bluetoothradio", + // "bluetooth", // doesn't work on macOS because it requires bluetooth permissions + // "bluetoothradio", "sound", "camera", "gamepad", diff --git a/scripts/gen-man.py b/scripts/gen-man.py index def48b072d..2bb589add1 100755 --- a/scripts/gen-man.py +++ b/scripts/gen-man.py @@ -9,7 +9,7 @@ """ from json import load -from datetime import date +from datetime import datetime, timezone from time import time from re import search from os import environ, path @@ -36,8 +36,10 @@ titlePage = "Fastfetch man page" # date (center footer) # format : "Month (abbreviation) Day Year" -todayDate = date.fromtimestamp( - int(environ.get("SOURCE_DATE_EPOCH", time()))).strftime("%b %d %Y") +todayDate = datetime.fromtimestamp( + int(environ.get("SOURCE_DATE_EPOCH", time())), + tz=timezone.utc, +).strftime("%b %d %Y") # file to fastfetch version (left footer) pathToVersionFile = path.join(pathToCurrentDir, "../CMakeLists.txt") diff --git a/src/common/dbus.c b/src/common/dbus.c index 636cce7a6d..ea438335cb 100644 --- a/src/common/dbus.c +++ b/src/common/dbus.c @@ -171,7 +171,7 @@ DBusMessage* ffDBusGetMethodReply(FFDBusData* dbus, const char* busName, const c if (arg) dbus->lib->ffdbus_message_append_args(message, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); - DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, FF_DBUS_TIMEOUT_MILLISECONDS, NULL); + DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL); dbus->lib->ffdbus_message_unref(message); @@ -189,7 +189,7 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID); - DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, FF_DBUS_TIMEOUT_MILLISECONDS, NULL); + DBusMessage* reply = dbus->lib->ffdbus_connection_send_with_reply_and_block(dbus->connection, message, instance.config.general.processingTimeout, NULL); dbus->lib->ffdbus_message_unref(message); diff --git a/src/common/dbus.h b/src/common/dbus.h index 282f008d92..35a1672f8e 100644 --- a/src/common/dbus.h +++ b/src/common/dbus.h @@ -6,8 +6,6 @@ #include "util/FFstrbuf.h" #include "common/library.h" -#define FF_DBUS_TIMEOUT_MILLISECONDS 100 - typedef struct FFDBusLibrary { FF_LIBRARY_SYMBOL(dbus_bus_get) @@ -38,4 +36,9 @@ DBusMessage* ffDBusGetProperty(FFDBusData* dbus, const char* busName, const char bool ffDBusGetPropertyString(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, FFstrbuf* result); bool ffDBusGetPropertyUint(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface, const char* property, uint32_t* result); +static inline DBusMessage* ffDBusGetAllProperties(FFDBusData* dbus, const char* busName, const char* objectPath, const char* interface) +{ + return ffDBusGetMethodReply(dbus, busName, objectPath, "org.freedesktop.DBus.Properties", "GetAll", interface); +} + #endif // FF_HAVE_DBUS diff --git a/src/common/init.c b/src/common/init.c index 8f8dfea246..d6adc13428 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -76,19 +76,21 @@ static void resetConsole(void) } #ifdef _WIN32 -BOOL WINAPI consoleHandler(DWORD signal) +BOOL WINAPI consoleHandler(FF_MAYBE_UNUSED DWORD signal) { - FF_UNUSED(signal); resetConsole(); exit(0); } #else -static void exitSignalHandler(int signal) +static void exitSignalHandler(FF_MAYBE_UNUSED int signal) { - FF_UNUSED(signal); resetConsole(); exit(0); } +static void chldSignalHandler(FF_MAYBE_UNUSED int signal) +{ + // empty; used to interrupt the poll and read syscalls +} #endif void ffStart(void) @@ -118,6 +120,7 @@ void ffStart(void) sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGQUIT, &action, NULL); + sigaction(SIGCHLD, &(struct sigaction) { .sa_handler = chldSignalHandler }, NULL); #endif //reset everything to default before we start printing @@ -179,15 +182,9 @@ void ffListFeatures(void) #if FF_HAVE_XCB_RANDR "xcb-randr\n" #endif - #if FF_HAVE_XCB - "xcb\n" - #endif #if FF_HAVE_XRANDR "xrandr\n" #endif - #if FF_HAVE_X11 - "x11\n" - #endif #if FF_HAVE_DRM "drm\n" #endif diff --git a/src/common/processing_linux.c b/src/common/processing_linux.c index 3b290bc3d4..63d3951147 100644 --- a/src/common/processing_linux.c +++ b/src/common/processing_linux.c @@ -1,7 +1,6 @@ #include "fastfetch.h" #include "common/processing.h" #include "common/io/io.h" -#include "common/time.h" #include "util/stringUtils.h" #include "util/mallocHelper.h" @@ -90,6 +89,16 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use waitpid(childPid, NULL, 0); return "poll(&pollfd, 1, timeout) timeout (try increasing --processing-timeout)"; } + else if (errno == EINTR) + { + // The child process has been terminated. See `chldSignalHandler` in `common/init.c` + if (waitpid(childPid, NULL, WNOHANG) == childPid) + { + // Read remaining data from the pipe + fcntl(childPipeFd, F_SETFL, O_CLOEXEC | O_NONBLOCK); + childPid = -1; + } + } else if (pollfd.revents & POLLERR) { kill(childPid, SIGTERM); @@ -104,7 +113,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use else if (nRead == 0) { int stat_loc = 0; - if (waitpid(childPid, &stat_loc, 0) == childPid) + if (childPid > 0 && waitpid(childPid, &stat_loc, 0) == childPid) { if (!WIFEXITED(stat_loc)) return "child process exited abnormally"; @@ -113,10 +122,15 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use // We only handle 127 as an error. See `getTerminalVersionUrxvt` in `terminalshell.c` return NULL; } - return "waitpid() failed"; + return NULL; } else if (nRead < 0) - break; + { + if (errno == EAGAIN) + return NULL; + else + break; + } }; return "read(childPipeFd, str, FF_PIPE_BUFSIZ) failed"; diff --git a/src/detection/brightness/brightness_linux.c b/src/detection/brightness/brightness_linux.c index 9682a75986..645fb58c67 100644 --- a/src/detection/brightness/brightness_linux.c +++ b/src/detection/brightness/brightness_linux.c @@ -99,7 +99,7 @@ double ddca_set_default_sleep_multiplier(double multiplier); // ddcutil 1.4 DDCA_Status ddca_init(const char *libopts, int syslog_level, int opts); #endif -static const char* detectWithDdcci(FFBrightnessOptions* options, FFlist* result) +static const char* detectWithDdcci(FF_MAYBE_UNUSED FFBrightnessOptions* options, FFlist* result) { FF_LIBRARY_LOAD(libddcutil, "dlopen ddcutil failed", "libddcutil" FF_LIBRARY_EXTENSION, 5); FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libddcutil, ddca_get_display_info_list2) diff --git a/src/detection/cpu/cpu.c b/src/detection/cpu/cpu.c index a3b47416c1..2b9d41dced 100644 --- a/src/detection/cpu/cpu.c +++ b/src/detection/cpu/cpu.c @@ -16,6 +16,7 @@ const char* ffDetectCPU(const FFCPUOptions* options, FFCPUResult* cpu) ffStrbufRemoveStrings(&cpu->name, ARRAY_SIZE(removeStrings), removeStrings); ffStrbufSubstrBeforeFirstC(&cpu->name, '@'); //Cut the speed output in the name as we append our own ffStrbufTrimRight(&cpu->name, ' '); //If we removed the @ in previous step there was most likely a space before it + ffStrbufRemoveDupWhitespaces(&cpu->name); return NULL; } diff --git a/src/detection/cpu/cpu.h b/src/detection/cpu/cpu.h index eb5bd90f44..5af7a8275b 100644 --- a/src/detection/cpu/cpu.h +++ b/src/detection/cpu/cpu.h @@ -15,6 +15,7 @@ typedef struct FFCPUResult FFstrbuf name; FFstrbuf vendor; + uint16_t packages; uint16_t coresPhysical; uint16_t coresLogical; uint16_t coresOnline; diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index 1d94b005ed..8d5cbf0a4f 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -109,6 +109,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) return "sysctlbyname(machdep.cpu.brand_string) failed"; ffSysctlGetString("machdep.cpu.vendor", &cpu->vendor); + cpu->packages = (uint16_t) ffSysctlGetInt("hw.packages", 1); if (cpu->vendor.length == 0 && ffStrbufStartsWithS(&cpu->name, "Apple ")) ffStrbufAppendS(&cpu->vendor, "Apple"); diff --git a/src/detection/cpu/cpu_linux.c b/src/detection/cpu/cpu_linux.c index 91c8ba46f6..326d28e6f1 100644 --- a/src/detection/cpu/cpu_linux.c +++ b/src/detection/cpu/cpu_linux.c @@ -159,14 +159,13 @@ static void detectAndroid(FFCPUResult* cpu) #if __arm__ || __aarch64__ #include "cpu_arm.h" -static void detectArmName(FILE* cpuinfo, FFCPUResult* cpu, uint32_t implId) +static void detectArmName(FFstrbuf* cpuinfo, FFCPUResult* cpu, uint32_t implId) { - FF_AUTO_FREE char* line = NULL; - rewind(cpuinfo); + char* line = NULL; size_t len = 0; uint32_t lastPartId = UINT32_MAX; uint32_t num = 0; - while(getline(&line, &len, cpuinfo) != -1) + while(ffStrbufGetline(&line, &len, cpuinfo)) { if (!ffStrStartsWith(line, "CPU part\t: ")) continue; uint32_t partId = (uint32_t) strtoul(line + strlen("CPU part\t: "), NULL, 16); @@ -220,18 +219,18 @@ static void detectArmName(FILE* cpuinfo, FFCPUResult* cpu, uint32_t implId) #endif static const char* parseCpuInfo( - FF_MAYBE_UNUSED FILE* cpuinfo, - FF_MAYBE_UNUSED FFCPUResult* cpu, + FFstrbuf* cpuinfo, + FFCPUResult* cpu, FF_MAYBE_UNUSED FFstrbuf* physicalCoresBuffer, FF_MAYBE_UNUSED FFstrbuf* cpuMHz, FF_MAYBE_UNUSED FFstrbuf* cpuIsa, FF_MAYBE_UNUSED FFstrbuf* cpuUarch, FF_MAYBE_UNUSED FFstrbuf* cpuImplementer) { - FF_AUTO_FREE char* line = NULL; + char* line = NULL; size_t len = 0; - while(getline(&line, &len, cpuinfo) != -1) + while(ffStrbufGetline(&line, &len, cpuinfo)) { //Stop after reasonable information is acquired if((*line == '\0' || *line == '\n') @@ -465,11 +464,32 @@ FF_MAYBE_UNUSED static void detectArmSoc(FFCPUResult* cpu) } } +FF_MAYBE_UNUSED static uint16_t getPackageCount(FFstrbuf* cpuinfo) +{ + const char* p = cpuinfo->chars; + uint64_t low = 0, high = 0; + + while ((p = memmem(p, cpuinfo->length - (uint32_t) (p - cpuinfo->chars), "\nphysical id\t:", strlen("\nphysical id\t:")))) + { + if (!p) break; + p += strlen("\nphysical id\t:"); + char* pend; + unsigned long id = strtoul(p, &pend, 10); + if (__builtin_expect(id > 64, false)) // Do 129-socket boards exist? + high |= 1 << (id - 64); + else + low |= 1 << id; + p = pend; + } + + return (uint16_t) (__builtin_popcountll(low) + __builtin_popcountll(high)); +} + const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { - FF_AUTO_CLOSE_FILE FILE* cpuinfo = fopen("/proc/cpuinfo", "r"); - if(cpuinfo == NULL) - return "fopen(\"/proc/cpuinfo\", \"r\") failed"; + FF_STRBUF_AUTO_DESTROY cpuinfo = ffStrbufCreateA(PROC_FILE_BUFFSIZ); + if (!ffReadFileBuffer("/proc/cpuinfo", &cpuinfo) || cpuinfo.length == 0) + return "ffReadFileBuffer(\"/proc/cpuinfo\") failed"; cpu->temperature = options->temp ? detectCPUTemp() : FF_CPU_TEMP_UNSET; @@ -479,12 +499,15 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) FF_STRBUF_AUTO_DESTROY cpuUarch = ffStrbufCreate(); FF_STRBUF_AUTO_DESTROY cpuImplementerStr = ffStrbufCreate(); - const char* error = parseCpuInfo(cpuinfo, cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr); + const char* error = parseCpuInfo(&cpuinfo, cpu, &physicalCoresBuffer, &cpuMHz, &cpuIsa, &cpuUarch, &cpuImplementerStr); if (error) return error; cpu->coresLogical = (uint16_t) get_nprocs_conf(); cpu->coresOnline = (uint16_t) get_nprocs(); cpu->coresPhysical = (uint16_t) ffStrbufToUInt(&physicalCoresBuffer, cpu->coresLogical); + #if __x86_64__ || __i386__ + cpu->packages = getPackageCount(&cpuinfo); + #endif // Ref https://github.com/fastfetch-cli/fastfetch/issues/1194#issuecomment-2295058252 ffCPUDetectSpeedByCpuid(cpu); @@ -519,7 +542,7 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) #endif if (cpu->name.length == 0) - detectArmName(cpuinfo, cpu, cpuImplementer); + detectArmName(&cpuinfo, cpu, cpuImplementer); #endif return NULL; diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 7479ff6d1c..09f422e128 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -100,9 +100,7 @@ static const char* detectNCores(FFCPUResult* cpu) ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((uint8_t*)ptr) + ptr->Size) ) { - if (ptr->Relationship == RelationProcessorCore) - ++cpu->coresPhysical; - else if (ptr->Relationship == RelationGroup) + if (ptr->Relationship == RelationGroup) { for (uint32_t index = 0; index < ptr->Group.ActiveGroupCount; ++index) { @@ -110,6 +108,10 @@ static const char* detectNCores(FFCPUResult* cpu) cpu->coresLogical += ptr->Group.GroupInfo[index].MaximumProcessorCount; } } + else if (ptr->Relationship == RelationProcessorCore) + ++cpu->coresPhysical; + else if (ptr->Relationship == RelationProcessorPackage) + cpu->packages++; } return NULL; diff --git a/src/detection/displayserver/displayserver.c b/src/detection/displayserver/displayserver.c index c2ba3e593a..6ae9efbc48 100644 --- a/src/detection/displayserver/displayserver.c +++ b/src/detection/displayserver/displayserver.c @@ -7,6 +7,9 @@ FFDisplayResult* ffdsAppendDisplay( double refreshRate, uint32_t scaledWidth, uint32_t scaledHeight, + uint32_t preferredWidth, + uint32_t preferredHeight, + double preferredRefreshRate, uint32_t rotation, FFstrbuf* name, FFDisplayType type, @@ -25,6 +28,9 @@ FFDisplayResult* ffdsAppendDisplay( display->refreshRate = refreshRate; display->scaledWidth = scaledWidth; display->scaledHeight = scaledHeight; + display->preferredWidth = preferredWidth; + display->preferredHeight = preferredHeight; + display->preferredRefreshRate = preferredRefreshRate; display->rotation = rotation; ffStrbufInitMove(&display->name, name); display->type = type; diff --git a/src/detection/displayserver/displayserver.h b/src/detection/displayserver/displayserver.h index 4de3bdd4d4..dd4fd96513 100644 --- a/src/detection/displayserver/displayserver.h +++ b/src/detection/displayserver/displayserver.h @@ -58,17 +58,20 @@ typedef enum __attribute__((__packed__)) FFDisplayHdrStatus typedef struct FFDisplayResult { - uint32_t width; - uint32_t height; - double refreshRate; - uint32_t scaledWidth; - uint32_t scaledHeight; + uint32_t width; // in px + uint32_t height; // in px + double refreshRate; // in Hz + uint32_t scaledWidth; // in px + uint32_t scaledHeight; // in px + uint32_t preferredWidth; // in px + uint32_t preferredHeight; // in px + double preferredRefreshRate; // in Hz FFstrbuf name; FFDisplayType type; uint32_t rotation; uint64_t id; // platform dependent - uint32_t physicalWidth; - uint32_t physicalHeight; + uint32_t physicalWidth; // in mm + uint32_t physicalHeight; // in mm bool primary; const char* platformApi; uint8_t bitDepth; @@ -97,6 +100,9 @@ FFDisplayResult* ffdsAppendDisplay( double refreshRate, uint32_t scaledWidth, uint32_t scaledHeight, + uint32_t preferredWidth, + uint32_t preferredHeight, + double preferredRefreshRate, uint32_t rotation, FFstrbuf* name, FFDisplayType type, diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_android.c index 88e5b23d33..d10f394e03 100644 --- a/src/detection/displayserver/displayserver_android.c +++ b/src/detection/displayserver/displayserver_android.c @@ -61,6 +61,9 @@ static void detectWithDumpsys(FFDisplayServerResult* ds) 0, 0, 0, + 0, + 0, + 0, &name, FF_DISPLAY_TYPE_UNKNOWN, false, @@ -97,6 +100,9 @@ static bool detectWithGetprop(FFDisplayServerResult* ds) (uint32_t) (height / scaleFactor + .5), 0, 0, + 0, + 0, + NULL, FF_DISPLAY_TYPE_BUILTIN, false, 0, diff --git a/src/detection/displayserver/displayserver_apple.c b/src/detection/displayserver/displayserver_apple.c index 520750110f..e8eeb9fbf0 100644 --- a/src/detection/displayserver/displayserver_apple.c +++ b/src/detection/displayserver/displayserver_apple.c @@ -60,7 +60,11 @@ static void detectDisplays(FFDisplayServerResult* ds) displayInfo = IODisplayCreateInfoDictionary(servicePort, kIODisplayOnlyPreferredName); } #endif + uint32_t physicalWidth = 0, physicalHeight = 0; + uint32_t preferredWidth = 0, preferredHeight = 0; + double preferredRefreshRate = 0; + if(displayInfo) { CFDictionaryRef productNames; @@ -76,9 +80,35 @@ static void detectDisplays(FFDisplayServerResult* ds) if (edidLength >= 128) ffEdidGetPhysicalSize(edidData, &physicalWidth, &physicalHeight); } + + if (!physicalWidth || !physicalHeight) + { + if (ffCfDictGetInt(displayInfo, CFSTR(kDisplayHorizontalImageSize), (int*) &physicalWidth) == NULL) + ffCfDictGetInt(displayInfo, CFSTR(kDisplayVerticalImageSize), (int*) &physicalHeight); + } + + ffCfDictGetInt(displayInfo, CFSTR("kCGDisplayPixelWidth"), (int*) &preferredWidth); + ffCfDictGetInt(displayInfo, CFSTR("kCGDisplayPixelHeight"), (int*) &preferredHeight); + if (preferredWidth && preferredHeight) + { + FF_CFTYPE_AUTO_RELEASE CFArrayRef allModes = CGDisplayCopyAllDisplayModes(screen, NULL); + if (allModes) + { + for (CFIndex i = 0, count = CFArrayGetCount(allModes); i < count; i++) + { + CGDisplayModeRef modeInfo = (CGDisplayModeRef) CFArrayGetValueAtIndex(allModes, i); + if (CGDisplayModeGetPixelWidth(modeInfo) == preferredWidth && CGDisplayModeGetPixelHeight(modeInfo) == preferredHeight) + { + double rr = CGDisplayModeGetRefreshRate(modeInfo); + if (rr > preferredRefreshRate) preferredRefreshRate = rr; + break; + } + } + } + } } - if (!physicalWidth || !physicalHeight) + if ((!physicalWidth || !physicalHeight) && CGDisplayPrimaryDisplay(screen) == screen) // #1406 { CGSize size = CGDisplayScreenSize(screen); physicalWidth = (uint32_t) (size.width + 0.5); @@ -91,6 +121,9 @@ static void detectDisplays(FFDisplayServerResult* ds) refreshRate, (uint32_t)CGDisplayModeGetWidth(mode), (uint32_t)CGDisplayModeGetHeight(mode), + preferredWidth, + preferredHeight, + preferredRefreshRate, (uint32_t)CGDisplayRotation(screen), &buffer, CGDisplayIsBuiltin(screen) ? FF_DISPLAY_TYPE_BUILTIN : FF_DISPLAY_TYPE_EXTERNAL, @@ -117,7 +150,7 @@ static void detectDisplays(FFDisplayServerResult* ds) } #endif - if (display->type == FF_DISPLAY_TYPE_BUILTIN) + if (display->type == FF_DISPLAY_TYPE_BUILTIN && displayInfo) display->hdrStatus = CFDictionaryContainsKey(displayInfo, CFSTR("ReferencePeakHDRLuminance")) ? FF_DISPLAY_HDR_STATUS_SUPPORTED : FF_DISPLAY_HDR_STATUS_UNSUPPORTED; #ifdef MAC_OS_X_VERSION_10_15 @@ -135,11 +168,15 @@ static void detectDisplays(FFDisplayServerResult* ds) #endif display->serial = CGDisplaySerialNumber(screen); - int value; - if (ffCfDictGetInt(displayInfo, CFSTR(kDisplayYearOfManufacture), &value) == NULL) - display->manufactureYear = (uint16_t) value; - if (ffCfDictGetInt(displayInfo, CFSTR(kDisplayWeekOfManufacture), &value) == NULL) - display->manufactureWeek = (uint16_t) value; + + if (displayInfo) + { + int value; + if (ffCfDictGetInt(displayInfo, CFSTR(kDisplayYearOfManufacture), &value) == NULL) + display->manufactureYear = (uint16_t) value; + if (ffCfDictGetInt(displayInfo, CFSTR(kDisplayWeekOfManufacture), &value) == NULL) + display->manufactureWeek = (uint16_t) value; + } } CGDisplayModeRelease(mode); } diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c index d0825dbc68..95f435cc94 100644 --- a/src/detection/displayserver/displayserver_windows.c +++ b/src/detection/displayserver/displayserver_windows.c @@ -149,12 +149,30 @@ static void detectDisplays(FFDisplayServerResult* ds) default: rotation = 0; break; } + DISPLAYCONFIG_TARGET_PREFERRED_MODE preferredMode = { + .header = { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE, + .size = sizeof(preferredMode), + .adapterId = path->targetInfo.adapterId, + .id = path->targetInfo.id, + } + }; + double preferredRefreshRate = 0; + if (DisplayConfigGetDeviceInfo(&preferredMode.header) == ERROR_SUCCESS) + { + DISPLAYCONFIG_RATIONAL freq = preferredMode.targetMode.targetVideoSignalInfo.vSyncFreq; + preferredRefreshRate = freq.Numerator / (double) freq.Denominator; + } + FFDisplayResult* display = ffdsAppendDisplay(ds, width, height, path->targetInfo.refreshRate.Numerator / (double) path->targetInfo.refreshRate.Denominator, (uint32_t) (monitorInfo->info.rcMonitor.right - monitorInfo->info.rcMonitor.left), (uint32_t) (monitorInfo->info.rcMonitor.bottom - monitorInfo->info.rcMonitor.top), + preferredMode.width, + preferredMode.height, + preferredRefreshRate, rotation, &name, path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER ? FF_DISPLAY_TYPE_UNKNOWN : @@ -218,19 +236,24 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) //https://github.com/hykilpikonna/hyfetch/blob/master/neofetch#L2067 const FFOSResult* os = ffDetectOS(); - if( - ffStrbufEqualS(&os->version, "11") || - ffStrbufEqualS(&os->version, "10") || - ffStrbufEqualS(&os->version, "2022") || - ffStrbufEqualS(&os->version, "2019") || - ffStrbufEqualS(&os->version, "2016") - ) ffStrbufSetStatic(&ds->dePrettyName, "Fluent"); - else if( - ffStrbufEqualS(&os->version, "8") || - ffStrbufEqualS(&os->version, "8.1") || - ffStrbufEqualS(&os->version, "2012 R2") || - ffStrbufEqualS(&os->version, "2012") - ) ffStrbufSetStatic(&ds->dePrettyName, "Metro"); + uint32_t ver = (uint32_t) ffStrbufToUInt(&os->version, 0); + if (ver > 1000) + { + // Windows Server + if (ver >= 2016) + ffStrbufSetStatic(&ds->dePrettyName, "Fluent"); + else if (ver >= 2012) + ffStrbufSetStatic(&ds->dePrettyName, "Metro"); + else + ffStrbufSetStatic(&ds->dePrettyName, "Aero"); + } else - ffStrbufSetStatic(&ds->dePrettyName, "Aero"); + { + if (ver >= 10) + ffStrbufSetStatic(&ds->dePrettyName, "Fluent"); + else if (ver >= 8) + ffStrbufSetStatic(&ds->dePrettyName, "Metro"); + else + ffStrbufSetStatic(&ds->dePrettyName, "Aero"); + } } diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index cc4e07b21b..63b2c83152 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -54,18 +54,11 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) //Try the x11 libs, from most feature rich to least. //We use the display list to detect if a connection is needed. //They respect wmProtocolName, and only detect display if it is set. - if(ds->displays.length == 0) ffdsConnectXcbRandr(ds); if(ds->displays.length == 0) ffdsConnectXrandr(ds); - - if(ds->displays.length == 0) - ffdsConnectXcb(ds); - - if(ds->displays.length == 0) - ffdsConnectXlib(ds); } //This display detection method is display server independent. @@ -86,7 +79,7 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) if (ffSettingsGetFreeBSDKenv("screen.height", &buf)) { uint32_t height = (uint32_t) ffStrbufToUInt(&buf, 0); - ffdsAppendDisplay(ds, width, height, 0, 0, 0, 0, NULL, FF_DISPLAY_TYPE_UNKNOWN, false, 0, 0, 0, "kenv"); + ffdsAppendDisplay(ds, width, height, 0, 0, 0, 0, 0, 0, 0, NULL, FF_DISPLAY_TYPE_UNKNOWN, false, 0, 0, 0, "kenv"); } } } diff --git a/src/detection/displayserver/linux/displayserver_linux.h b/src/detection/displayserver/linux/displayserver_linux.h index 5452711e7b..19ae4e71f3 100644 --- a/src/detection/displayserver/linux/displayserver_linux.h +++ b/src/detection/displayserver/linux/displayserver_linux.h @@ -5,11 +5,7 @@ const char* ffdsConnectWayland(FFDisplayServerResult* result); const char* ffdsConnectXcbRandr(FFDisplayServerResult* result); -const char* ffdsConnectXcb(FFDisplayServerResult* result); - const char* ffdsConnectXrandr(FFDisplayServerResult* result); -const char* ffdsConnectXlib(FFDisplayServerResult* result); - const char* ffdsConnectDrm(FFDisplayServerResult* result); void ffdsDetectWMDE(FFDisplayServerResult* result); diff --git a/src/detection/displayserver/linux/drm.c b/src/detection/displayserver/linux/drm.c index fb837e4a89..1fe274b479 100644 --- a/src/detection/displayserver/linux/drm.c +++ b/src/detection/displayserver/linux/drm.c @@ -84,6 +84,8 @@ static const char* drmParseSysfs(FFDisplayServerResult* result) width, height, refreshRate, 0, 0, + 0, 0, + 0, 0, &name, ffdsGetDisplayType(plainName), @@ -218,12 +220,14 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetConnectorCurrent) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetCrtc) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetEncoder) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetFB) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetProperty) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeGetPropertyBlob) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeResources) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeCrtc) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeEncoder) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeConnector) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeEncoder) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeFB) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreeProperty) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmModeFreePropertyBlob) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmFreeDevices) @@ -252,28 +256,29 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) continue; #endif - FF_AUTO_CLOSE_FD int fd = open(path, O_RDONLY | O_CLOEXEC); - if (fd < 0) + FF_AUTO_CLOSE_FD int primaryFd = open(path, O_RDWR | O_CLOEXEC); + if (primaryFd < 0) continue; - drmModeRes* res = ffdrmModeGetResources(fd); + drmModeRes* res = ffdrmModeGetResources(primaryFd); if (!res) continue; for (int iConn = 0; iConn < res->count_connectors; ++iConn) { - drmModeConnector* conn = ffdrmModeGetConnectorCurrent(fd, res->connectors[iConn]); + drmModeConnector* conn = ffdrmModeGetConnectorCurrent(primaryFd, res->connectors[iConn]); if (!conn) continue; if (conn->connection != DRM_MODE_DISCONNECTED) { - drmModeEncoder* encoder = ffdrmModeGetEncoder(fd, conn->encoder_id); + drmModeEncoder* encoder = ffdrmModeGetEncoder(primaryFd, conn->encoder_id); uint32_t width = 0, height = 0, refreshRate = 0; + uint8_t bitDepth = 0; if (encoder) { - drmModeCrtc* crtc = ffdrmModeGetCrtc(fd, encoder->crtc_id); + drmModeCrtc* crtc = ffdrmModeGetCrtc(primaryFd, encoder->crtc_id); if (crtc) { width = crtc->mode.hdisplay; @@ -292,30 +297,44 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) } } } + + drmModeFBPtr fb = ffdrmModeGetFB(primaryFd, crtc->buffer_id); + if (fb) + { + bitDepth = (uint8_t) (fb->depth / 3); + ffdrmModeFreeFB(fb); + } + ffdrmModeFreeCrtc(crtc); } ffdrmModeFreeEncoder(encoder); } - if (width == 0 || height == 0) + uint32_t preferredWidth = 0, preferredHeight = 0, preferredRefreshRate = 0; + + for (int iMode = 0; iMode < conn->count_modes; ++iMode) { - // NVIDIA DRM driver seems incomplete and conn->encoder_id == 0 - // Assume preferred resolution is used as what we do in drmParseSys - for (int iMode = 0; iMode < conn->count_modes; ++iMode) - { - drmModeModeInfo* mode = &conn->modes[iMode]; + drmModeModeInfo* mode = &conn->modes[iMode]; - if (mode->type & DRM_MODE_TYPE_PREFERRED) - { - width = mode->hdisplay; - height = mode->vdisplay; - refreshRate = mode->vrefresh; - break; - } + if (mode->type & DRM_MODE_TYPE_PREFERRED) + { + preferredWidth = mode->hdisplay; + preferredHeight = mode->vdisplay; + preferredRefreshRate = mode->vrefresh; + break; } } + // NVIDIA DRM driver seems incomplete and conn->encoder_id == 0 + // Assume preferred resolution is used as what we do in drmParseSys + if (width == 0 || height == 0) + { + width = preferredWidth; + height = preferredHeight; + refreshRate = preferredRefreshRate; + } + ffStrbufClear(&name); uint16_t myear = 0, mweak = 0; @@ -324,7 +343,7 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) for (int iProp = 0; iProp < conn->count_props; ++iProp) { - drmModePropertyRes *prop = ffdrmModeGetProperty(fd, conn->props[iProp]); + drmModePropertyRes *prop = ffdrmModeGetProperty(primaryFd, conn->props[iProp]); if (!prop) continue; @@ -334,9 +353,9 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) drmModePropertyBlobPtr blob = NULL; if (prop->count_blobs > 0 && prop->blob_ids != NULL) - blob = ffdrmModeGetPropertyBlob(fd, prop->blob_ids[0]); + blob = ffdrmModeGetPropertyBlob(primaryFd, prop->blob_ids[0]); else - blob = ffdrmModeGetPropertyBlob(fd, (uint32_t) conn->prop_values[iProp]); + blob = ffdrmModeGetPropertyBlob(primaryFd, (uint32_t) conn->prop_values[iProp]); if (blob) { @@ -382,6 +401,9 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) refreshRate, 0, 0, + preferredWidth, + preferredHeight, + preferredRefreshRate, 0, &name, conn->connector_type == DRM_MODE_CONNECTOR_eDP || conn->connector_type == DRM_MODE_CONNECTOR_LVDS @@ -401,6 +423,7 @@ static const char* drmConnectLibdrm(FFDisplayServerResult* result) item->serial = serial; item->manufactureYear = myear; item->manufactureWeek = mweak; + item->bitDepth = bitDepth; } } diff --git a/src/detection/displayserver/linux/wayland/global-output.c b/src/detection/displayserver/linux/wayland/global-output.c index ab9fc5c2fd..8b3671d166 100644 --- a/src/detection/displayserver/linux/wayland/global-output.c +++ b/src/detection/displayserver/linux/wayland/global-output.c @@ -6,13 +6,20 @@ static void waylandOutputModeListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refreshRate) { - if(!(flags & WL_OUTPUT_MODE_CURRENT)) - return; - WaylandDisplay* display = data; - display->width = width; - display->height = height; - display->refreshRate = refreshRate; + + if (flags & WL_OUTPUT_MODE_CURRENT) + { + display->width = width; + display->height = height; + display->refreshRate = refreshRate; + } + if (flags & WL_OUTPUT_MODE_PREFERRED) + { + display->preferredWidth = width; + display->preferredHeight = height; + display->preferredRefreshRate = refreshRate; + } } static void waylandOutputScaleListener(void* data, FF_MAYBE_UNUSED struct wl_output* output, int32_t scale) @@ -79,9 +86,6 @@ void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* regist WaylandDisplay display = { .parent = wldata, - .width = 0, - .height = 0, - .refreshRate = 0, .scale = 1, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .type = FF_DISPLAY_TYPE_UNKNOWN, @@ -118,6 +122,9 @@ void ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* regist display.refreshRate / 1000.0, (uint32_t) (display.width / display.scale), (uint32_t) (display.height / display.scale), + (uint32_t) display.preferredWidth, + (uint32_t) display.preferredHeight, + display.preferredRefreshRate / 1000.0, rotation, display.edidName.length ? &display.edidName diff --git a/src/detection/displayserver/linux/wayland/kde-output.c b/src/detection/displayserver/linux/wayland/kde-output.c index beac7b6355..6644880e09 100644 --- a/src/detection/displayserver/linux/wayland/kde-output.c +++ b/src/detection/displayserver/linux/wayland/kde-output.c @@ -11,26 +11,33 @@ typedef struct WaylandKdeMode int32_t width; int32_t height; int32_t refreshRate; + bool preferred; struct kde_output_device_mode_v2* pMode; } WaylandKdeMode; -static void waylandKdeSizeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_, int32_t width, int32_t height) +static void waylandKdeModeSizeListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_, int32_t width, int32_t height) { WaylandKdeMode* mode = (WaylandKdeMode*) data; mode->width = width; mode->height = height; } -static void waylandKdeRefreshListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_, int32_t rate) +static void waylandKdeModeRefreshListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_, int32_t rate) { WaylandKdeMode* mode = (WaylandKdeMode*) data; mode->refreshRate = rate; } +static void waylandKdeModePreferredListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_mode_v2 *_) +{ + WaylandKdeMode* mode = (WaylandKdeMode*) data; + mode->preferred = true; +} + static const struct kde_output_device_mode_v2_listener modeListener = { - .size = waylandKdeSizeListener, - .refresh = waylandKdeRefreshListener, - .preferred = (void*) stubListener, + .size = waylandKdeModeSizeListener, + .refresh = waylandKdeModeRefreshListener, + .preferred = waylandKdeModePreferredListener, .removed = (void*) stubListener, }; @@ -40,7 +47,7 @@ static void waylandKdeModeListener(void* data, FF_MAYBE_UNUSED struct kde_output if (!wldata->internal) return; WaylandKdeMode* newMode = ffListAdd((FFlist*) wldata->internal); - newMode->pMode = mode; + *newMode = (WaylandKdeMode) { .pMode = mode }; // Strangely, the listener is called only in this function, but not in `waylandKdeCurrentModeListener` wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &modeListener, newMode); @@ -52,18 +59,24 @@ static void waylandKdeCurrentModeListener(void* data, FF_MAYBE_UNUSED struct kde WaylandDisplay* wldata = (WaylandDisplay*) data; if (!wldata->internal) return; - WaylandKdeMode* current = NULL; + int set = 0; FF_LIST_FOR_EACH(WaylandKdeMode, m, *(FFlist*) wldata->internal) { if (m->pMode == mode) { - current = m; - break; + wldata->width = m->width; + wldata->height = m->height; + wldata->refreshRate = m->refreshRate; + if (++set == 2) break; + } + if (m->preferred) + { + wldata->preferredWidth = m->width; + wldata->preferredHeight = m->height; + wldata->preferredRefreshRate = m->refreshRate; + if (++set == 2) break; } } - wldata->width = current->width; - wldata->height = current->height; - wldata->refreshRate = current->refreshRate; } static void waylandKdeScaleListener(void* data, FF_MAYBE_UNUSED struct kde_output_device_v2* _, wl_fixed_t scale) @@ -164,9 +177,6 @@ void ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandKdeMode)); WaylandDisplay display = { .parent = wldata, - .width = 0, - .height = 0, - .refreshRate = 0, .scale = 1, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .type = FF_DISPLAY_TYPE_UNKNOWN, @@ -191,6 +201,9 @@ void ffWaylandHandleKdeOutput(WaylandData* wldata, struct wl_registry* registry, display.refreshRate / 1000.0, (uint32_t) (display.width / display.scale), (uint32_t) (display.height / display.scale), + (uint32_t) display.preferredWidth, + (uint32_t) display.preferredHeight, + display.preferredRefreshRate / 1000.0, rotation, display.edidName.length ? &display.edidName diff --git a/src/detection/displayserver/linux/wayland/wayland.c b/src/detection/displayserver/linux/wayland/wayland.c index e4b387a517..d09eb06f98 100644 --- a/src/detection/displayserver/linux/wayland/wayland.c +++ b/src/detection/displayserver/linux/wayland/wayland.c @@ -39,7 +39,7 @@ static bool waylandDetectWM(int fd, FFDisplayServerResult* result) filename = result->wmProcessName.chars; if (ffStrEquals(filename, "wl-restart")) - ffStrbufSubstrAfterFirstC(&result->wmProcessName, '\0'); + ffStrbufSubstrAfterLastC(&result->wmProcessName, '\0'); ffStrbufSubstrBeforeFirstC(&result->wmProcessName, '\0'); //Trim the arguments ffStrbufSubstrAfterLastC(&result->wmProcessName, '/'); //Trim the path diff --git a/src/detection/displayserver/linux/wayland/wayland.h b/src/detection/displayserver/linux/wayland/wayland.h index c19d881991..ab11d05388 100644 --- a/src/detection/displayserver/linux/wayland/wayland.h +++ b/src/detection/displayserver/linux/wayland/wayland.h @@ -36,9 +36,12 @@ typedef struct WaylandDisplay WaylandData* parent; int32_t width; int32_t height; + int32_t refreshRate; + int32_t preferredWidth; + int32_t preferredHeight; + int32_t preferredRefreshRate; int32_t physicalWidth; int32_t physicalHeight; - int32_t refreshRate; double scale; enum wl_output_transform transform; FFDisplayType type; diff --git a/src/detection/displayserver/linux/wayland/zwlr-output.c b/src/detection/displayserver/linux/wayland/zwlr-output.c index c54818a64a..a19ec9fb6d 100644 --- a/src/detection/displayserver/linux/wayland/zwlr-output.c +++ b/src/detection/displayserver/linux/wayland/zwlr-output.c @@ -20,34 +20,43 @@ typedef struct WaylandZwlrMode int32_t width; int32_t height; int32_t refreshRate; + bool preferred; struct zwlr_output_mode_v1* pMode; } WaylandZwlrMode; -static void waylandZwlrSizeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t width, int32_t height) +static void waylandZwlrModeSizeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t width, int32_t height) { WaylandZwlrMode* mode = (WaylandZwlrMode*) data; mode->width = width; mode->height = height; } -static void waylandZwlrRefreshListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t rate) +static void waylandZwlrModeRefreshListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1, int32_t rate) { WaylandZwlrMode* mode = (WaylandZwlrMode*) data; mode->refreshRate = rate; } +static void waylandZwlrModePreferredListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_mode_v1 *zwlr_output_mode_v1) +{ + WaylandZwlrMode* mode = (WaylandZwlrMode*) data; + mode->preferred = true; +} + static const struct zwlr_output_mode_v1_listener modeListener = { - .size = waylandZwlrSizeListener, - .refresh = waylandZwlrRefreshListener, - .preferred = (void*) stubListener, + .size = waylandZwlrModeSizeListener, + .refresh = waylandZwlrModeRefreshListener, + .preferred = waylandZwlrModePreferredListener, .finished = (void*) stubListener, }; static void waylandZwlrModeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, struct zwlr_output_mode_v1 *mode) { WaylandDisplay* wldata = (WaylandDisplay*) data; + if (!wldata->internal) return; + WaylandZwlrMode* newMode = ffListAdd((FFlist*) wldata->internal); - newMode->pMode = mode; + *newMode = (WaylandZwlrMode) { .pMode = mode }; // Strangely, the listener is called only in this function, but not in `waylandZwlrCurrentModeListener` wldata->parent->ffwl_proxy_add_listener((struct wl_proxy *) mode, (void (**)(void)) &modeListener, newMode); @@ -57,18 +66,26 @@ static void waylandZwlrCurrentModeListener(void* data, FF_MAYBE_UNUSED struct zw { // waylandZwlrModeListener is always run before this WaylandDisplay* wldata = (WaylandDisplay*) data; - WaylandZwlrMode* current = NULL; + if (!wldata->internal) return; + + int set = 0; FF_LIST_FOR_EACH(WaylandZwlrMode, m, *(FFlist*) wldata->internal) { if (m->pMode == mode) { - current = m; - break; + wldata->width = m->width; + wldata->height = m->height; + wldata->refreshRate = m->refreshRate; + if (++set == 2) break; + } + if (m->preferred) + { + wldata->preferredWidth = m->width; + wldata->preferredHeight = m->height; + wldata->preferredRefreshRate = m->refreshRate; + if (++set == 2) break; } } - wldata->width = current->width; - wldata->height = current->height; - wldata->refreshRate = current->refreshRate; } static void waylandZwlrPhysicalSizeListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, int32_t width, int32_t height) @@ -78,12 +95,18 @@ static void waylandZwlrPhysicalSizeListener(void* data, FF_MAYBE_UNUSED struct z wldata->physicalHeight = height; } +static void waylandZwlrEnabledListener(void* data, FF_MAYBE_UNUSED struct zwlr_output_head_v1 *zwlr_output_head_v1, bool enabled) +{ + WaylandDisplay* wldata = (WaylandDisplay*) data; + if (!enabled) wldata->internal = NULL; +} + static const struct zwlr_output_head_v1_listener headListener = { .name = (void*) ffWaylandOutputNameListener, .description = (void*) ffWaylandOutputDescriptionListener, .physical_size = waylandZwlrPhysicalSizeListener, .mode = waylandZwlrModeListener, - .enabled = (void*) stubListener, + .enabled = (void*) waylandZwlrEnabledListener, .current_mode = waylandZwlrCurrentModeListener, .position = (void*) stubListener, .transform = waylandZwlrTransformListener, @@ -102,9 +125,6 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output FF_LIST_AUTO_DESTROY modes = ffListCreate(sizeof(WaylandZwlrMode)); WaylandDisplay display = { .parent = wldata, - .width = 0, - .height = 0, - .refreshRate = 0, .scale = 1, .transform = WL_OUTPUT_TRANSFORM_NORMAL, .type = FF_DISPLAY_TYPE_UNKNOWN, @@ -117,7 +137,7 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output wldata->ffwl_proxy_add_listener((struct wl_proxy*) head, (void(**)(void)) &headListener, &display); wldata->ffwl_display_roundtrip(wldata->display); - if(display.width <= 0 || display.height <= 0) + if(display.width <= 0 || display.height <= 0 || !display.internal) return; uint32_t rotation = ffWaylandHandleRotation(&display); @@ -128,6 +148,9 @@ static void waylandHandleZwlrHead(void *data, FF_MAYBE_UNUSED struct zwlr_output display.refreshRate / 1000.0, (uint32_t) (display.width / display.scale + 0.5), (uint32_t) (display.height / display.scale + 0.5), + (uint32_t) display.preferredWidth, + (uint32_t) display.preferredHeight, + display.preferredRefreshRate / 1000.0, rotation, display.edidName.length ? &display.edidName diff --git a/src/detection/displayserver/linux/xcb.c b/src/detection/displayserver/linux/xcb.c index a9e4b8d795..b3eab2dece 100644 --- a/src/detection/displayserver/linux/xcb.c +++ b/src/detection/displayserver/linux/xcb.c @@ -1,11 +1,15 @@ #include "displayserver_linux.h" -#include "util/mallocHelper.h" -#include "common/time.h" -#ifdef FF_HAVE_XCB +#ifdef FF_HAVE_XCB_RANDR + #include "common/library.h" +#include "common/time.h" +#include "util/edidHelper.h" +#include "util/mallocHelper.h" + #include #include +#include #include typedef struct XcbPropertyData @@ -82,72 +86,6 @@ static void xcbDetectWMfromEWMH(XcbPropertyData* data, xcb_connection_t* connect ffStrbufSetS(&result->wmProcessName, wmName); } -const char* ffdsConnectXcb(FFDisplayServerResult* result) -{ - FF_LIBRARY_LOAD(xcb, "dlopen lbxcb failed", "libxcb" FF_LIBRARY_EXTENSION, 2) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xcb, xcb_connect) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xcb, xcb_get_setup) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xcb, xcb_setup_roots_iterator) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xcb, xcb_screen_next) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xcb, xcb_disconnect) - - XcbPropertyData propertyData; - bool propertyDataInitialized = xcbInitPropertyData(xcb, &propertyData); - - xcb_connection_t* connection = ffxcb_connect(NULL, NULL); - if(connection == NULL) - return "xcb_connect failed"; - - xcb_screen_iterator_t iterator = ffxcb_setup_roots_iterator(ffxcb_get_setup(connection)); - - if(iterator.rem > 0 && propertyDataInitialized) - xcbDetectWMfromEWMH(&propertyData, connection, iterator.data->root, result); - - while(iterator.rem > 0) - { - xcb_screen_t* screen = iterator.data; - ffdsAppendDisplay(result, - (uint32_t) screen->width_in_pixels, - (uint32_t) screen->height_in_pixels, - 0, - (uint32_t) screen->width_in_pixels, - (uint32_t) screen->height_in_pixels, - 0, - NULL, - FF_DISPLAY_TYPE_UNKNOWN, - false, - 0, - (uint32_t) screen->width_in_millimeters, - (uint32_t) screen->height_in_millimeters, - "xcb" - ); - ffxcb_screen_next(&iterator); - } - - ffxcb_disconnect(connection); - - //If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland. - if(result->wmProtocolName.length == 0) - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11); - - return NULL; -} - -#else - -const char* ffdsConnectXcb(FFDisplayServerResult* result) -{ - //Do nothing. There are other implementations coming - FF_UNUSED(result) - return "Fastfetch was compiled without XCB support"; -} - -#endif - -#ifdef FF_HAVE_XCB_RANDR -#include "util/edidHelper.h" -#include - typedef struct XcbRandrData { FF_LIBRARY_SYMBOL(xcb_randr_get_screen_resources_current) @@ -181,28 +119,6 @@ typedef struct XcbRandrData xcb_randr_get_screen_resources_current_reply_t* screenResources; } XcbRandrData; -static double xcbRandrHandleMode(XcbRandrData* data, xcb_randr_mode_t mode) -{ - //We do the check here, because we want the best fallback display if this call failed - if(data->screenResources == NULL) - return 0; - - xcb_randr_mode_info_iterator_t modesIterator = data->ffxcb_randr_get_screen_resources_current_modes_iterator(data->screenResources); - - while(modesIterator.rem > 0) - { - if(modesIterator.data->id == mode) - { - xcb_randr_mode_info_t* modeInfo = modesIterator.data; - return (double) modeInfo->dot_clock / (double) (modeInfo->htotal * modeInfo->vtotal); - } - - data->ffxcb_randr_mode_info_next(&modesIterator); - } - - return 0; -} - static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc, FFstrbuf* name, bool primary, xcb_randr_get_output_info_reply_t* output, FFDisplayType displayType, uint8_t* edidData, uint32_t edidLength) { xcb_randr_get_crtc_info_cookie_t crtcInfoCookie = data->ffxcb_randr_get_crtc_info(data->connection, crtc, XCB_CURRENT_TIME); @@ -227,13 +143,38 @@ static bool xcbRandrHandleCrtc(XcbRandrData* data, xcb_randr_crtc_t crtc, FFstrb break; } + xcb_randr_mode_info_t* currentMode = NULL; + xcb_randr_mode_info_t* preferredMode = NULL; + + if(data->screenResources) + { + xcb_randr_mode_info_iterator_t modesIterator = data->ffxcb_randr_get_screen_resources_current_modes_iterator(data->screenResources); + + if (output->num_preferred > 0) + preferredMode = modesIterator.data; + + while (modesIterator.rem > 0) + { + if (modesIterator.data->id == crtcInfoReply->mode) + { + currentMode = modesIterator.data; + break; + } + + data->ffxcb_randr_mode_info_next(&modesIterator); + } + } + FFDisplayResult* item = ffdsAppendDisplay( data->result, (uint32_t) crtcInfoReply->width, (uint32_t) crtcInfoReply->height, - xcbRandrHandleMode(data, crtcInfoReply->mode), + currentMode ? (double) currentMode->dot_clock / (double) ((uint32_t) currentMode->htotal * currentMode->vtotal) : 0, (uint32_t) crtcInfoReply->width, (uint32_t) crtcInfoReply->height, + preferredMode ? (uint32_t) preferredMode->width : 0, + preferredMode ? (uint32_t) preferredMode->height : 0, + preferredMode ? (double) preferredMode->dot_clock / (double) ((uint32_t) preferredMode->htotal * preferredMode->vtotal) : 0, rotation, name, displayType, @@ -323,6 +264,7 @@ static bool xcbRandrHandleMonitor(XcbRandrData* data, xcb_randr_monitor_info_t* 0, (uint32_t) monitor->width, (uint32_t) monitor->height, + 0, 0, 0, 0, &name, displayType, @@ -378,6 +320,7 @@ static void xcbRandrHandleScreen(XcbRandrData* data, xcb_screen_t* screen) 0, (uint32_t) screen->width_in_pixels, (uint32_t) screen->height_in_pixels, + 0, 0, 0, 0, NULL, FF_DISPLAY_TYPE_UNKNOWN, diff --git a/src/detection/displayserver/linux/xlib.c b/src/detection/displayserver/linux/xlib.c index 00ef3ce7ce..d2a1d1f245 100644 --- a/src/detection/displayserver/linux/xlib.c +++ b/src/detection/displayserver/linux/xlib.c @@ -1,9 +1,13 @@ #include "displayserver_linux.h" -#ifdef FF_HAVE_X11 +#ifdef FF_HAVE_XRANDR + #include "common/library.h" #include "common/parsing.h" +#include "util/edidHelper.h" #include "util/stringUtils.h" + +#include #include typedef struct X11PropertyData @@ -58,66 +62,6 @@ static void x11DetectWMFromEWMH(X11PropertyData* data, Display* display, FFDispl data->ffXFree(wmWindow); } -const char* ffdsConnectXlib(FFDisplayServerResult* result) -{ - FF_LIBRARY_LOAD(x11, "dlopen libX11 failed", "libX11" FF_LIBRARY_EXTENSION, 7, "libX11-xcb" FF_LIBRARY_EXTENSION, 2) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(x11, XOpenDisplay) - FF_LIBRARY_LOAD_SYMBOL_MESSAGE(x11, XCloseDisplay) - - X11PropertyData propertyData; - bool propertyDataInitialized = x11InitPropertyData(x11, &propertyData); - - Display* display = ffXOpenDisplay(x11); - if(display == NULL) - return "XOpenDisplay failed"; - - if(propertyDataInitialized && ScreenCount(display) > 0) - x11DetectWMFromEWMH(&propertyData, display, result); - - for(int i = 0; i < ScreenCount(display); i++) - { - Screen* screen = ScreenOfDisplay(display, i); - ffdsAppendDisplay(result, - (uint32_t) WidthOfScreen(screen), - (uint32_t) HeightOfScreen(screen), - 0, - (uint32_t) WidthOfScreen(screen), - (uint32_t) HeightOfScreen(screen), - 0, - NULL, - FF_DISPLAY_TYPE_UNKNOWN, - false, - 0, - (uint32_t) WidthMMOfScreen(screen), - (uint32_t) HeightMMOfScreen(screen), - "xlib" - ); - } - - ffXCloseDisplay(display); - - //If wayland hasn't set this, connection failed for it. So we are running only a X Server, not XWayland. - if(result->wmProtocolName.length == 0) - ffStrbufSetS(&result->wmProtocolName, FF_WM_PROTOCOL_X11); - - return NULL; -} - -#else - -const char* ffdsConnectXlib(FFDisplayServerResult* result) -{ - //Do nothing. WM / DE detection will use environment vars to detect as much as possible. - FF_UNUSED(result); - return "Fastfetch was compiled without libX11 support"; -} - -#endif //FF_HAVE_X11 - -#ifdef FF_HAVE_XRANDR -#include "util/edidHelper.h" -#include - typedef struct XrandrData { FF_LIBRARY_SYMBOL(XInternAtom) @@ -141,19 +85,6 @@ typedef struct XrandrData XRRScreenResources* screenResources; } XrandrData; -static double xrandrHandleMode(XrandrData* data, RRMode mode) -{ - for(int i = 0; i < data->screenResources->nmode; i++) - { - if(data->screenResources->modes[i].id == mode) - { - XRRModeInfo* modeInfo = &data->screenResources->modes[i]; - return (double) modeInfo->dotClock / (double) (modeInfo->hTotal * modeInfo->vTotal); - } - } - return 0; -} - static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf* name, bool primary, FFDisplayType displayType, uint8_t* edidData, uint32_t edidLength) { //We do the check here, because we want the best fallback display if this call failed @@ -181,13 +112,31 @@ static bool xrandrHandleCrtc(XrandrData* data, XRROutputInfo* output, FFstrbuf* break; } + XRRModeInfo* currentMode = NULL; + if (data->screenResources) + { + for(int i = 0; i < data->screenResources->nmode; i++) + { + if(data->screenResources->modes[i].id == crtcInfo->mode) + { + currentMode = &data->screenResources->modes[i]; + break; + } + } + } + + XRRModeInfo* preferredMode = data->screenResources && output->npreferred > 0 ? &data->screenResources->modes[0] : NULL; + FFDisplayResult* item = ffdsAppendDisplay( data->result, (uint32_t) crtcInfo->width, (uint32_t) crtcInfo->height, - xrandrHandleMode(data, crtcInfo->mode), + currentMode ? (double) currentMode->dotClock / (double) ((uint32_t) currentMode->hTotal * currentMode->vTotal) : 0, (uint32_t) crtcInfo->width, (uint32_t) crtcInfo->height, + preferredMode ? (uint32_t) preferredMode->width : 0, + preferredMode ? (uint32_t) preferredMode->height : 0, + preferredMode ? (double) preferredMode->dotClock / (double) ((uint32_t) preferredMode->hTotal * preferredMode->vTotal) : 0, rotation, name, displayType, @@ -264,6 +213,7 @@ static bool xrandrHandleMonitor(XrandrData* data, XRRMonitorInfo* monitorInfo) 0, (uint32_t) monitorInfo->width, (uint32_t) monitorInfo->height, + 0, 0, 0, 0, &name, displayType, @@ -315,6 +265,7 @@ static void xrandrHandleScreen(XrandrData* data, Screen* screen) 0, (uint32_t) WidthOfScreen(screen), (uint32_t) HeightOfScreen(screen), + 0, 0, 0, 0, NULL, FF_DISPLAY_TYPE_UNKNOWN, diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index bf9cc1add4..f67a9cf60e 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -536,27 +536,27 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf .name = &gpu->name, }, soName); } - - if (gpu->type == FF_GPU_TYPE_UNKNOWN) - { - if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) - { - if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") || - ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") || - ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) - gpu->type = FF_GPU_TYPE_DISCRETE; - } - else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_MTHREADS) - { - if (ffStrbufStartsWithIgnCaseS(&gpu->name, "MTT ")) - gpu->type = FF_GPU_TYPE_DISCRETE; - } - } } if (gpu->name.length == 0) ffGPUFillVendorAndName(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); + if (gpu->type == FF_GPU_TYPE_UNKNOWN) + { + if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "GeForce") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Quadro") || + ffStrbufStartsWithIgnCaseS(&gpu->name, "Tesla")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } + else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_MTHREADS) + { + if (ffStrbufStartsWithIgnCaseS(&gpu->name, "MTT ")) + gpu->type = FF_GPU_TYPE_DISCRETE; + } + } + return NULL; } diff --git a/src/detection/gpu/mtml.h b/src/detection/gpu/mtml.h index a97cfeb37c..008f83ae24 100644 --- a/src/detection/gpu/mtml.h +++ b/src/detection/gpu/mtml.h @@ -4,10 +4,8 @@ // THIS FILE IS CREATED FROM SCRATCH, BY READING THE OFFICIAL MTML API // DOCUMENTATION REFERENCED BELOW, IN ORDER TO MAKE FASTFETCH MIT COMPLIANT. -#define MTML_API __attribute__((visibility("default"))) #define MTML_DEVICE_PCI_SBDF_BUFFER_SIZE 32 #define MTML_DEVICE_NAME_BUFFER_SIZE 32 -#define MTML_DEVICE_UUID_BUFFER_SIZE 48 /** * Return values for MTML API calls. @@ -23,10 +21,6 @@ typedef enum typedef enum { MTML_BRAND_MTT = 0, //!< MTT series. - MTML_BRAND_UNKNOWN, //!< An unknown brand. - - // Keep this on the last line. - MTML_BRAND_COUNT //!< The number of brands. } MtmlBrandType; typedef struct MtmlLibrary MtmlLibrary; @@ -57,62 +51,62 @@ typedef struct } MtmlPciInfo; // Retrieves the number of cores of a device. -MtmlReturn MTML_API mtmlDeviceCountGpuCores(const MtmlDevice* device, unsigned int* numCores); +MtmlReturn mtmlDeviceCountGpuCores(const MtmlDevice* device, unsigned int* numCores); // Retrieves the brand of a device. -MtmlReturn MTML_API mtmlDeviceGetBrand(const MtmlDevice *dev, MtmlBrandType *type); +MtmlReturn mtmlDeviceGetBrand(const MtmlDevice *dev, MtmlBrandType *type); // Retrieves the index associated with the specified device. -MtmlReturn MTML_API mtmlDeviceGetIndex(const MtmlDevice *dev, unsigned int *index); +MtmlReturn mtmlDeviceGetIndex(const MtmlDevice *dev, unsigned int *index); // Retrieves the name of a device. -MtmlReturn MTML_API mtmlDeviceGetName(const MtmlDevice *dev, char *name, unsigned int length); +MtmlReturn mtmlDeviceGetName(const MtmlDevice *dev, char *name, unsigned int length); // Retrieves the PCI attributes of a device. -MtmlReturn MTML_API mtmlDeviceGetPciInfo(const MtmlDevice *dev, MtmlPciInfo *pci); +MtmlReturn mtmlDeviceGetPciInfo(const MtmlDevice *dev, MtmlPciInfo *pci); /** * Retrieves the UUID of a specified device. The UUID is a hexadecimal string in the * form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, where each 'x' is an ASCII character that represents a hexadecimal * digit. The UUID is globally unique for every single device thus can be used to identify different devices * physically. */ -MtmlReturn MTML_API mtmlDeviceGetUUID(const MtmlDevice *dev, char *uuid, unsigned int length); +MtmlReturn mtmlDeviceGetUUID(const MtmlDevice *dev, char *uuid, unsigned int length); // Initializes a GPU opaque object to represent a specific graphic core on the target device that is designated by its index. -MtmlReturn MTML_API mtmlDeviceInitGpu(const MtmlDevice *dev, MtmlGpu **gpu); +MtmlReturn mtmlDeviceInitGpu(const MtmlDevice *dev, MtmlGpu **gpu); // Initializes a memory opaque object to represent the memory on the target device. -MtmlReturn MTML_API mtmlDeviceInitMemory(const MtmlDevice *dev, MtmlMemory **mem); +MtmlReturn mtmlDeviceInitMemory(const MtmlDevice *dev, MtmlMemory **mem); // Retrieves the maximum supported clock speed for the device's graphic core. -MtmlReturn MTML_API mtmlGpuGetMaxClock(const MtmlGpu *gpu, unsigned int *clockMhz); +MtmlReturn mtmlGpuGetMaxClock(const MtmlGpu *gpu, unsigned int *clockMhz); // Retrieves the current temperature readings for the device's graphic core, in degrees Celsius. -MtmlReturn MTML_API mtmlGpuGetTemperature(const MtmlGpu *gpu, unsigned int *temp); +MtmlReturn mtmlGpuGetTemperature(const MtmlGpu *gpu, unsigned int *temp); // Retrieves the current utilization rate for the device's graphic core. -MtmlReturn MTML_API mtmlGpuGetUtilization(const MtmlGpu *gpu, unsigned int *utilization); +MtmlReturn mtmlGpuGetUtilization(const MtmlGpu *gpu, unsigned int *utilization); // Retrieves the number of devices that can be accessed by the library opaque object. -MtmlReturn MTML_API mtmlLibraryCountDevice(const MtmlLibrary *lib, unsigned int *count); +MtmlReturn mtmlLibraryCountDevice(const MtmlLibrary *lib, unsigned int *count); /** * Initializes a device opaque object to represent a device that is designated by its index. * The index ranges from (0) to (deviceCount - 1), where deviceCount is retrieved from \ref mtmlLibraryCountDevice(). */ -MtmlReturn MTML_API mtmlLibraryInit(MtmlLibrary **lib); +MtmlReturn mtmlLibraryInit(MtmlLibrary **lib); /** * Initializes a device opaque object to represent a device that is designated by its index. * The index ranges from (0) to (deviceCount - 1), where deviceCount is retrieved from \ref mtmlLibraryCountDevice(). */ -MtmlReturn MTML_API mtmlLibraryInitDeviceByIndex(const MtmlLibrary *lib, unsigned int index, MtmlDevice **dev); +MtmlReturn mtmlLibraryInitDeviceByIndex(const MtmlLibrary *lib, unsigned int index, MtmlDevice **dev); /** * Initializes a device opaque object to represent a device that is designated by its PCI Sbdf. * The PCI Sbdf format like 00000000:3a:00.0 refer to \ref MtmlPciInfo::sbdf. */ -MtmlReturn MTML_API mtmlLibraryInitDeviceByPciSbdf(const MtmlLibrary *lib, const char *pciSbdf, MtmlDevice **dev); +MtmlReturn mtmlLibraryInitDeviceByPciSbdf(const MtmlLibrary *lib, const char *pciSbdf, MtmlDevice **dev); // Initializes a MtmlSystem opaque pointer that is bound to a library opaque object. -MtmlReturn MTML_API mtmlLibraryInitSystem(const MtmlLibrary *lib, MtmlSystem **sys); +MtmlReturn mtmlLibraryInitSystem(const MtmlLibrary *lib, MtmlSystem **sys); /** * Shuts down the library opaque object that is previously initialized by \ref mtmlLibraryInit() and releases its resources. * The \a lib pointer cannot be used anymore after this function returns. */ -MtmlReturn MTML_API mtmlLibraryShutDown(MtmlLibrary *lib); +MtmlReturn mtmlLibraryShutDown(MtmlLibrary *lib); // Retrieves the amount of total memory available on the device, in bytes. -MtmlReturn MTML_API mtmlMemoryGetTotal(const MtmlMemory *mem, unsigned long long *total); +MtmlReturn mtmlMemoryGetTotal(const MtmlMemory *mem, unsigned long long *total); // Retrieves the amount of used memory on the device, in bytes. -MtmlReturn MTML_API mtmlMemoryGetUsed(const MtmlMemory *mem, unsigned long long *used); +MtmlReturn mtmlMemoryGetUsed(const MtmlMemory *mem, unsigned long long *used); // Retrieves the current memory utilization rate for the device. -MtmlReturn MTML_API mtmlMemoryGetUtilization(const MtmlMemory *mem, unsigned int *utilization); +MtmlReturn mtmlMemoryGetUtilization(const MtmlMemory *mem, unsigned int *utilization); diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c index 43e3f21157..a4953de500 100644 --- a/src/detection/media/media_linux.c +++ b/src/detection/media/media_linux.c @@ -17,36 +17,23 @@ continue; \ } -static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResult* result) +static bool parseMprisMetadata(FFDBusData* data, DBusMessageIter* rootIterator, FFMediaResult* result) { - DBusMessage* reply = ffDBusGetProperty(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "Metadata"); - if(reply == NULL) - return false; - - DBusMessageIter rootIterator; - if(!data->lib->ffdbus_message_iter_init(reply, &rootIterator)) - { - data->lib->ffdbus_message_unref(reply); - return false; - } + DBusMessageIter arrayIterator; - if(data->lib->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_VARIANT) + if (data->lib->ffdbus_message_iter_get_arg_type(rootIterator) == DBUS_TYPE_VARIANT) { - data->lib->ffdbus_message_unref(reply); - return false; + DBusMessageIter variantIterator; + data->lib->ffdbus_message_iter_recurse(rootIterator, &variantIterator); + if(data->lib->ffdbus_message_iter_get_arg_type(&variantIterator) != DBUS_TYPE_ARRAY) + return false; + data->lib->ffdbus_message_iter_recurse(&variantIterator, &arrayIterator); } - - DBusMessageIter variantIterator; - data->lib->ffdbus_message_iter_recurse(&rootIterator, &variantIterator); - if(data->lib->ffdbus_message_iter_get_arg_type(&variantIterator) != DBUS_TYPE_ARRAY) + else { - data->lib->ffdbus_message_unref(reply); - return false; + data->lib->ffdbus_message_iter_recurse(rootIterator, &arrayIterator); } - DBusMessageIter arrayIterator; - data->lib->ffdbus_message_iter_recurse(&variantIterator, &arrayIterator); - while(true) { if(data->lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY) @@ -66,13 +53,17 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul data->lib->ffdbus_message_iter_next(&dictIterator); - if(ffStrEquals(key, "xesam:title")) + if(!ffStrStartsWith(key, "xesam:")) + FF_DBUS_ITER_CONTINUE(data, &arrayIterator) + + key += strlen("xesam:"); + if(ffStrEquals(key, "title")) ffDBusGetString(data, &dictIterator, &result->song); - else if(ffStrEquals(key, "xesam:album")) + else if(ffStrEquals(key, "album")) ffDBusGetString(data, &dictIterator, &result->album); - else if(ffStrEquals(key, "xesam:artist")) + else if(ffStrEquals(key, "artist")) ffDBusGetString(data, &dictIterator, &result->artist); - else if(ffStrEquals(key, "xesam:url")) + else if(ffStrEquals(key, "url")) ffDBusGetString(data, &dictIterator, &result->url); if(result->song.length > 0 && result->artist.length > 0 && result->album.length > 0 && result->url.length > 0) @@ -81,7 +72,53 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul FF_DBUS_ITER_CONTINUE(data, &arrayIterator) } - data->lib->ffdbus_message_unref(reply); + return true; +} + +static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResult* result) +{ + // Get all properties at once to reduce the number of IPCs + DBusMessage* reply = ffDBusGetAllProperties(data, busName, "/org/mpris/MediaPlayer2", ""); + if(reply == NULL) + return false; + + DBusMessageIter rootIterator; + if(!data->lib->ffdbus_message_iter_init(reply, &rootIterator) && + data->lib->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY) + { + data->lib->ffdbus_message_unref(reply); + return false; + } + + DBusMessageIter arrayIterator; + data->lib->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator); + + FF_STRBUF_AUTO_DESTROY desktopIdentity = ffStrbufCreate(); + + while(true) + { + if(data->lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY) + FF_DBUS_ITER_CONTINUE(data, &arrayIterator) + + DBusMessageIter dictIterator; + data->lib->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator); + + const char* key; + data->lib->ffdbus_message_iter_get_basic(&dictIterator, &key); + + data->lib->ffdbus_message_iter_next(&dictIterator); + + if(ffStrEquals(key, "Metadata")) + parseMprisMetadata(data, &dictIterator, result); + else if(ffStrEquals(key, "PlaybackStatus")) + ffDBusGetString(data, &dictIterator, &result->status); + else if(ffStrEquals(key, "Identity")) + ffDBusGetString(data, &dictIterator, &result->player); + else if(ffStrEquals(key, "DesktopEntry")) + ffDBusGetString(data, &dictIterator, &desktopIdentity); + + FF_DBUS_ITER_CONTINUE(data, &arrayIterator) + } if(result->song.length == 0) { @@ -91,17 +128,20 @@ static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResul return false; } - ffDBusGetPropertyString(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", "PlaybackStatus", &result->status); - - //Set short bus name - ffStrbufAppendS(&result->playerId, busName + sizeof(FF_DBUS_MPRIS_PREFIX) - 1); + if (result->player.length == 0) + { + if (desktopIdentity.length > 0) + { + ffStrbufDestroy(&result->player); + ffStrbufInitMove(&result->player, &desktopIdentity); + } + else + { + ffStrbufAppend(&result->player, &result->playerId); + } + } - //We found a song, get the player name - ffDBusGetPropertyString(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "Identity", &result->player); - if(result->player.length == 0) - ffDBusGetPropertyString(data, busName, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2", "DesktopEntry", &result->player); - if(result->player.length == 0) - ffStrbufAppend(&result->player, &result->playerId); + data->lib->ffdbus_message_unref(reply); return true; } @@ -122,9 +162,9 @@ static void getCustomBus(FFDBusData* data, const FFstrbuf* playerName, FFMediaRe static void getBestBus(FFDBusData* data, FFMediaResult* result) { if( - getBusProperties(data, FF_DBUS_MPRIS_PREFIX"spotify", result) || - getBusProperties(data, FF_DBUS_MPRIS_PREFIX"vlc", result) || - getBusProperties(data, FF_DBUS_MPRIS_PREFIX"plasma-browser-integration", result) + getBusProperties(data, FF_DBUS_MPRIS_PREFIX "spotify", result) || + getBusProperties(data, FF_DBUS_MPRIS_PREFIX "vlc", result) || + getBusProperties(data, FF_DBUS_MPRIS_PREFIX "plasma-browser-integration", result) ) return; DBusMessage* reply = ffDBusGetMethodReply(data, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames", NULL); diff --git a/src/detection/os/os_linux.c b/src/detection/os/os_linux.c index cab2cef8ae..a00584796b 100644 --- a/src/detection/os/os_linux.c +++ b/src/detection/os/os_linux.c @@ -273,5 +273,13 @@ void ffDetectOSImpl(FFOSResult* os) if (!detectDebianDerived(os)) getDebianVersion(os); } + else if(ffStrbufEqualS(&os->id, "linuxmint")) + { + if (ffStrbufEqualS(&os->name, "LMDE")) + { + ffStrbufSetS(&os->id, "lmde"); + ffStrbufSetS(&os->idLike, "linuxmint"); + } + } #endif } diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index baf383def6..2c0ecd28b0 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -250,7 +250,7 @@ static void detectFootTerminal(FFTerminalFontResult* terminalFont) ffFontInitValues(&terminalFont->font, font.chars, "8"); return; } - uint32_t size = equal + strlen("size="); + uint32_t size = equal + (uint32_t) strlen("size="); uint32_t comma = ffStrbufNextIndexC(&font, size, ','); if (comma < font.length) font.chars[comma] = '\0'; @@ -454,4 +454,6 @@ void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFo detectWestonTerminal(terminalFont); else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "terminator")) detectTerminator(terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "sakura")) + detectFromConfigFile("sakura/sakura.conf", "font=", terminalFont);; } diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 7ce6854a5e..ebacb444f0 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -661,6 +661,19 @@ FF_MAYBE_UNUSED static bool getTerminalVersionTilix(FFstrbuf* exe, FFstrbuf* ver ffStrbufSubstrAfter(version, index); return true; } + +FF_MAYBE_UNUSED static bool getTerminalVersionSakura(FFstrbuf* exe, FFstrbuf* version) +{ + if(ffProcessAppendStdErr(version, (char* const[]) { + exe->chars, + "--version", + NULL + }) != NULL) // sakura version is 3.8.8 + return false; + + ffStrbufSubstrAfterLastC(version, ' '); + return true; +} #endif #ifdef _WIN32 @@ -763,6 +776,9 @@ bool fftsGetTerminalVersion(FFstrbuf* processName, FF_MAYBE_UNUSED FFstrbuf* exe if(ffStrbufIgnCaseEqualS(processName, "tilix")) return getTerminalVersionTilix(exe, version); + if(ffStrbufIgnCaseEqualS(processName, "sakura")) + return getTerminalVersionSakura(exe, version); + #endif #ifdef _WIN32 diff --git a/src/detection/wifi/wifi.h b/src/detection/wifi/wifi.h index 10b4ffdbc9..f941869a05 100644 --- a/src/detection/wifi/wifi.h +++ b/src/detection/wifi/wifi.h @@ -18,6 +18,8 @@ struct FFWifiConnection double signalQuality; // Percentage double rxRate; double txRate; + uint16_t channel; + uint16_t frequency; // MHz }; typedef struct FFWifiResult @@ -27,3 +29,15 @@ typedef struct FFWifiResult } FFWifiResult; const char* ffDetectWifi(FFlist* result /*list of FFWifiItem*/); + +static inline uint16_t ffWifiFreqToChannel(uint16_t frequency) +{ + // https://github.com/opetryna/win32wifi/blob/master/win32wifi/Win32Wifi.py#L140 + // FIXME: Does it work for 6 GHz? + if (frequency == 2484) + return 14; + else if (frequency < 2484) + return (uint16_t) ((frequency - 2407) / 5); + else + return (uint16_t) ((frequency / 5) - 1000); +} diff --git a/src/detection/wifi/wifi_android.c b/src/detection/wifi/wifi_android.c index a149892422..2c0360cbae 100644 --- a/src/detection/wifi/wifi_android.c +++ b/src/detection/wifi/wifi_android.c @@ -43,6 +43,8 @@ const char* ffDetectWifi(FFlist* result) item->conn.signalQuality = 0.0/0.0; item->conn.rxRate = 0.0/0.0; item->conn.txRate = 0.0/0.0; + item->conn.channel = 0; + item->conn.frequency = 0; ffStrbufAppendS(&item->inf.status, yyjson_get_str(yyjson_obj_get(root, "supplicant_state"))); if(!item->inf.status.length) @@ -60,6 +62,9 @@ const char* ffDetectWifi(FFlist* result) ffStrbufAppendS(&item->inf.description, yyjson_get_str(yyjson_obj_get(root, "ip"))); ffStrbufAppendS(&item->conn.bssid, yyjson_get_str(yyjson_obj_get(root, "bssid"))); ffStrbufAppendS(&item->conn.ssid, yyjson_get_str(yyjson_obj_get(root, "ssid"))); + item->conn.frequency = (uint16_t) yyjson_get_int(yyjson_obj_get(root, "frequency_mhz")); + item->conn.txRate = yyjson_get_num(yyjson_obj_get(root, "link_speed_mbps")); + item->conn.channel = ffWifiFreqToChannel(item->conn.frequency); return NULL; } diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index 17cebcdfb9..aefe49c881 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -80,6 +80,8 @@ @interface CWInterface() item->conn.signalQuality = 0.0/0.0; item->conn.rxRate = 0.0/0.0; item->conn.txRate = 0.0/0.0; + item->conn.channel = 0; + item->conn.frequency = 0; ffStrbufAppendS(&item->inf.description, inf.interfaceName.UTF8String); ffStrbufSetStatic(&item->inf.status, inf.powerOn ? "Power On" : "Power Off"); @@ -289,6 +291,15 @@ @interface CWInterface() ffStrbufAppendF(&item->conn.security, "Unknown (%ld)", inf.security); break; } + + item->conn.channel = (uint16_t) inf.wlanChannel.channelNumber; + switch (inf.wlanChannel.channelBand) + { + case kCWChannelBand2GHz: item->conn.frequency = 2400; break; + case kCWChannelBand5GHz: item->conn.frequency = 5400; break; + case 3 /*kCWChannelBand6GHz*/: item->conn.frequency = 6400; break; + default: item->conn.frequency = 0; break; + } } return NULL; } diff --git a/src/detection/wifi/wifi_bsd.c b/src/detection/wifi/wifi_bsd.c index 1f47cf9ef3..fafc8f1c9f 100644 --- a/src/detection/wifi/wifi_bsd.c +++ b/src/detection/wifi/wifi_bsd.c @@ -34,18 +34,27 @@ const char* ffDetectWifi(FFlist* result) item->conn.signalQuality = 0.0/0.0; item->conn.rxRate = 0.0/0.0; item->conn.txRate = 0.0/0.0; + item->conn.channel = 0; + item->conn.frequency = 0; ffParsePropLines(ifconfig.chars, "ssid ", &item->conn.ssid); if (item->conn.ssid.length) { - uint32_t ibssid = ffStrbufFirstIndexS(&item->conn.ssid, " bssid "); - if (ibssid < item->conn.ssid.length) + uint32_t idx = ffStrbufFirstIndexS(&item->conn.ssid, " bssid "); + if (idx < item->conn.ssid.length) { - ibssid += (uint32_t) strlen(" bssid "); - ffStrbufSetS(&item->conn.bssid, item->conn.ssid.chars + ibssid); + ffStrbufSetS(&item->conn.bssid, item->conn.ssid.chars + idx + (uint32_t) strlen(" bssid ")); + ffStrbufSubstrBefore(&item->conn.ssid, idx); } - ffStrbufSubstrBeforeFirstC(&item->conn.ssid, ' '); + idx = ffStrbufFirstIndexS(&item->conn.ssid, " channel "); + if (idx < item->conn.ssid.length) + { + const char* pchannel = item->conn.ssid.chars + idx + strlen(" channel "); + sscanf(pchannel, "%hu (%hu MHz %*s)", &item->conn.channel, &item->conn.frequency); + } + + ffStrbufSubstrBefore(&item->conn.ssid, idx); } ffParsePropLines(ifconfig.chars, "media: ", &item->conn.protocol); diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c index cd99a271a7..6267db134c 100644 --- a/src/detection/wifi/wifi_linux.c +++ b/src/detection/wifi/wifi_linux.c @@ -36,6 +36,13 @@ typedef enum { NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192 = 0x00002000, } NM80211ApSecurityFlags; +#define FF_DBUS_ITER_CONTINUE(dbus, iterator) \ + { \ + if(!(dbus).lib->ffdbus_message_iter_next(iterator)) \ + break; \ + continue; \ + } + static const char* detectWifiWithNm(FFWifiResult* item, FFstrbuf* buffer) { FFDBusData dbus; @@ -72,24 +79,80 @@ static const char* detectWifiWithNm(FFWifiResult* item, FFstrbuf* buffer) if (!item->conn.status.length) ffStrbufSetStatic(&item->conn.status, "connected"); - if (!item->conn.ssid.length) - ffDBusGetPropertyString(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "Ssid", &item->conn.ssid); - - if (!item->conn.bssid.length) - ffDBusGetPropertyString(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress", &item->conn.bssid); + DBusMessage* reply = ffDBusGetAllProperties(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint"); + if(reply == NULL) + return "Failed to get access point properties"; - if (item->conn.signalQuality != item->conn.signalQuality) + DBusMessageIter rootIterator; + if(!dbus.lib->ffdbus_message_iter_init(reply, &rootIterator) && + dbus.lib->ffdbus_message_iter_get_arg_type(&rootIterator) != DBUS_TYPE_ARRAY) { - uint32_t strengthPercent; - if (ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "Strength", &strengthPercent)) - item->conn.signalQuality = strengthPercent; + dbus.lib->ffdbus_message_unref(reply); + return "Invalid type of access point properties"; } + DBusMessageIter arrayIterator; + dbus.lib->ffdbus_message_iter_recurse(&rootIterator, &arrayIterator); + NM80211ApFlags flags; NM80211ApSecurityFlags wpaFlags, rsnFlags; - if (ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "Flags", &flags) && - ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars, "org.freedesktop.NetworkManager.AccessPoint", "WpaFlags", &wpaFlags) && - ffDBusGetPropertyUint(&dbus, "org.freedesktop.NetworkManager", apPath.chars , "org.freedesktop.NetworkManager.AccessPoint", "RsnFlags", &rsnFlags)) + int flagCount = 0; + + while(true) + { + if(dbus.lib->ffdbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DICT_ENTRY) + FF_DBUS_ITER_CONTINUE(dbus, &arrayIterator) + + DBusMessageIter dictIterator; + dbus.lib->ffdbus_message_iter_recurse(&arrayIterator, &dictIterator); + + const char* key; + dbus.lib->ffdbus_message_iter_get_basic(&dictIterator, &key); + + dbus.lib->ffdbus_message_iter_next(&dictIterator); + + if (ffStrEquals(key, "Ssid")) + { + if (!item->conn.ssid.length) + ffDBusGetString(&dbus, &dictIterator, &item->conn.ssid); + } + else if (ffStrEquals(key, "HwAddress")) + { + if (!item->conn.bssid.length) + ffDBusGetString(&dbus, &dictIterator, &item->conn.bssid); + } + else if (ffStrEquals(key, "Strength")) + { + if (item->conn.signalQuality != item->conn.signalQuality) + { + uint32_t strengthPercent; + if (ffDBusGetUint(&dbus, &dictIterator, &strengthPercent)) + item->conn.signalQuality = strengthPercent; + } + } + else if (ffStrEquals(key, "Frequency")) + { + if (item->conn.frequency == 0) + { + uint32_t frequency; + if (ffDBusGetUint(&dbus, &dictIterator, &frequency)) + { + item->conn.frequency = (uint16_t) frequency; + if (item->conn.channel == 0) + item->conn.channel = ffWifiFreqToChannel(item->conn.frequency); + } + } + } + else if ((ffStrEquals(key, "Flags") && ffDBusGetUint(&dbus, &dictIterator, &flags)) || + (ffStrEquals(key, "WpaFlags") && ffDBusGetUint(&dbus, &dictIterator, &wpaFlags)) || + (ffStrEquals(key, "RsnFlags") && ffDBusGetUint(&dbus, &dictIterator, &rsnFlags)) + ) + ++flagCount; + + FF_DBUS_ITER_CONTINUE(dbus, &arrayIterator) + } + + if (flagCount == 3) { if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags == NM_802_11_AP_SEC_NONE) && (rsnFlags == NM_802_11_AP_SEC_NONE)) @@ -166,7 +229,9 @@ static const char* detectWifiWithIw(FFWifiResult* item, FFstrbuf* buffer) { item->conn.txRate = ffStrbufToDouble(buffer); - if(ffStrbufContainS(buffer, " HE-MCS ")) + if(ffStrbufContainS(buffer, " EHT-MCS ")) + ffStrbufSetStatic(&item->conn.protocol, "802.11be (Wi-Fi 7)"); + else if(ffStrbufContainS(buffer, " HE-MCS ")) ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); else if(ffStrbufContainS(buffer, " VHT-MCS ")) ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); @@ -174,6 +239,13 @@ static const char* detectWifiWithIw(FFWifiResult* item, FFstrbuf* buffer) ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); } + ffStrbufClear(buffer); + if(ffParsePropLines(output.chars, "freq: ", buffer)) + { + item->conn.frequency = (uint16_t) ffStrbufToUInt(buffer, 0); + item->conn.channel = ffWifiFreqToChannel(item->conn.frequency); + } + return NULL; } @@ -219,6 +291,30 @@ static const char* detectWifiWithIoctls(FFWifiResult* item) if(ioctl(sock, SIOCGIWRATE, &iwr) >= 0) item->conn.txRate = iwr.u.bitrate.value / 1000000.; + if(ioctl(sock, SIOCGIWFREQ, &iwr) >= 0) + { + if (iwr.u.freq.e == 0 && iwr.u.freq.m <= 1000) + { + item->conn.channel = (uint16_t) iwr.u.freq.m; + } + else + { + // convert it to MHz + while (iwr.u.freq.e < 6) + { + iwr.u.freq.m /= 10; + iwr.u.freq.e++; + } + while (iwr.u.freq.e > 6) + { + iwr.u.freq.m *= 10; + iwr.u.freq.e--; + } + item->conn.frequency = (uint16_t) iwr.u.freq.m; + item->conn.channel = ffWifiFreqToChannel(item->conn.frequency); + } + } + struct iw_statistics stats; iwr.u.data.pointer = &stats; iwr.u.data.length = sizeof(stats); @@ -289,6 +385,8 @@ const char* ffDetectWifi(FF_MAYBE_UNUSED FFlist* result) item->conn.signalQuality = 0.0/0.0; item->conn.rxRate = 0.0/0.0; item->conn.txRate = 0.0/0.0; + item->conn.channel = 0; + item->conn.frequency = 0; ffStrbufSetF(&buffer, "/sys/class/net/%s/operstate", i->if_name); if (!ffAppendFileBuffer(buffer.chars, &item->inf.status)) diff --git a/src/detection/wifi/wifi_windows.c b/src/detection/wifi/wifi_windows.c index 18262f2810..f3128a9d10 100644 --- a/src/detection/wifi/wifi_windows.c +++ b/src/detection/wifi/wifi_windows.c @@ -49,6 +49,7 @@ const char* ffDetectWifi(FFlist* result) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanQueryInterface) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanFreeMemory) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanCloseHandle) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(wlanapi, WlanGetNetworkBssList) DWORD curVersion; HANDLE hClient = NULL; @@ -82,21 +83,23 @@ const char* ffDetectWifi(FFlist* result) item->conn.signalQuality = 0.0/0.0; item->conn.rxRate = 0.0/0.0; item->conn.txRate = 0.0/0.0; + item->conn.channel = 0; + item->conn.frequency = 0; convertIfStateToString(ifInfo->isState, &item->inf.status); if(ifInfo->isState != wlan_interface_state_connected) continue; - DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES); - WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_invalid; WLAN_CONNECTION_ATTRIBUTES* connInfo = NULL; + DWORD bufSize = sizeof(*connInfo); + WLAN_OPCODE_VALUE_TYPE opCode = wlan_opcode_value_type_query_only; if(ffWlanQueryInterface(hClient, &ifInfo->InterfaceGuid, wlan_intf_opcode_current_connection, NULL, - &connectInfoSize, + &bufSize, (PVOID*)&connInfo, &opCode) != ERROR_SUCCESS ) continue; @@ -107,8 +110,8 @@ const char* ffDetectWifi(FFlist* result) (const char *)connInfo->wlanAssociationAttributes.dot11Ssid.ucSSID); for (size_t i = 0; i < sizeof(connInfo->wlanAssociationAttributes.dot11Bssid); i++) - ffStrbufAppendF(&item->conn.bssid, "%.2X-", connInfo->wlanAssociationAttributes.dot11Bssid[i]); - ffStrbufTrimRight(&item->conn.bssid, '-'); + ffStrbufAppendF(&item->conn.bssid, "%.2X:", connInfo->wlanAssociationAttributes.dot11Bssid[i]); + ffStrbufTrimRight(&item->conn.bssid, ':'); switch (connInfo->wlanAssociationAttributes.dot11PhyType) { @@ -151,8 +154,8 @@ const char* ffDetectWifi(FFlist* result) } item->conn.signalQuality = connInfo->wlanAssociationAttributes.wlanSignalQuality; - item->conn.rxRate = connInfo->wlanAssociationAttributes.ulRxRate; - item->conn.txRate = connInfo->wlanAssociationAttributes.ulTxRate; + item->conn.rxRate = connInfo->wlanAssociationAttributes.ulRxRate / 1000.; + item->conn.txRate = connInfo->wlanAssociationAttributes.ulTxRate / 1000.; if(connInfo->wlanSecurityAttributes.bSecurityEnabled) { @@ -189,7 +192,7 @@ const char* ffDetectWifi(FFlist* result) ffStrbufAppendS(&item->conn.security, "OWE"); break; case 11 /* DOT11_AUTH_ALGO_WPA3_ENT */: - ffStrbufAppendS(&item->conn.security, "OWE-ENT"); + ffStrbufAppendS(&item->conn.security, "WPA3-ENT"); break; default: ffStrbufAppendF(&item->conn.security, "Unknown (%u)", (unsigned)connInfo->wlanSecurityAttributes.dot11AuthAlgorithm); @@ -201,7 +204,34 @@ const char* ffDetectWifi(FFlist* result) else ffStrbufAppendS(&item->conn.security, "Insecure"); + WLAN_BSS_LIST* bssList = NULL; + if (ffWlanGetNetworkBssList(hClient, + &ifInfo->InterfaceGuid, + &connInfo->wlanAssociationAttributes.dot11Ssid, + connInfo->wlanAssociationAttributes.dot11BssType, + connInfo->wlanSecurityAttributes.bSecurityEnabled, + NULL, + &bssList) == ERROR_SUCCESS && bssList->dwNumberOfItems > 0 + ) { + item->conn.frequency = (uint16_t) (bssList->wlanBssEntries[0].ulChCenterFrequency / 1000); + ffWlanFreeMemory(bssList); + } + ffWlanFreeMemory(connInfo); + + ULONG* channelNumber = 0; + bufSize = sizeof(*channelNumber); + if(ffWlanQueryInterface(hClient, + &ifInfo->InterfaceGuid, + wlan_intf_opcode_channel_number, + NULL, + &bufSize, + (PVOID*)&channelNumber, + &opCode) == ERROR_SUCCESS + ) { + item->conn.channel = (uint16_t) *channelNumber; + ffWlanFreeMemory(channelNumber); + } } exit: diff --git a/src/logo/ascii/midos.txt b/src/logo/ascii/midos.txt index 4790708285..9c9b80ed0e 100644 --- a/src/logo/ascii/midos.txt +++ b/src/logo/ascii/midos.txt @@ -1,20 +1,20 @@ - .:=+*#%%@@@@@@%%#*+=:. - .=*%@@@@@@@@@@@@@@@@@@@@@@%*=: - .=%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+. - :*@@@@@@@@@@@@@@@@@$2++$1%@@@@@@@@@@@@@@@@#- - .*@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@#. - -%@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@- - -@@@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@@= -:@@@@@@@@@@@@@@@@@%%@@@%$2..$1#@@@@#@@@@@@@@@@@@@@@@@: -*@@@@@@@@@@@@#$2*=:..$1*@@@%$2..$1#@@@#$2..:=+$1#@@@@@@@@@@@@* -%@@@@@@@#+$2=:...-+$1#@@@@@%$2..$1#@@@@@#$2+=:..:-$1+#@@@@@@@% -%@@@@@$2:..:=+-..-+$1#@@@@@%$2..$1#@@@@@%$2+-..-+=:..:$1@@@@@@ -*@@@@@$2*%$1@@@@@@%$2+-..$1#@@@%$2..$1#@@@%$2..-+$1#@@@@@@$2%*$1@@@@@* -:@@@@@@@@@@@@@@@@@$2#%$1@@@%$2..$1#@@@$2%#$1@@@@@@@@@@@@@@@@@: - =@@@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@@= - -@@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@= - .*@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@#: - :*@@@@@@@@@@@@@@@@@$2==$1%@@@@@@@@@@@@@@@@#-. - .+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+: - :=*%@@@@@@@@@@@@@@@@@@@@@@%*=:. - .-=+*#%%@@@@@@%%#*+=-. \ No newline at end of file + ▁▂▃▄▅▆▇▇████▇▇▆▅▄▃▂▁ + ▃▅▇████████████████████▇▅▃ + ▂▅████████████████████████████▅▁ + ▗▟██████████████▛▀▀▜██████████████▙▖ + ▗▟████████████████▌ ▐████████████████▙▖ + ▗██████████████████▌ ▐██████████████████▖ + ▗███████████████████▌ ▐███████████████████▖ +▕███████████████▀▘ ██▌ ▐██ ▝▀███████████████▏ +▐██████████▛▀▀ ▂██▌ ▐██▂ ▀▀▜██████████▌ +███████▀▀ ▁▄▆████▌ ▐████▆▄▁ ▀▀███████ +█████ ▂▄▂ ▔▀▜████▌ ▐████▛▀▔ ▂▄▂ █████ +▐████▄▄▆█████▄▂ ▔▀██▌ ▐██▀▔ ▂▄█████▆▄▄████▌ +▕██████████████▇▄▂ ██▌ ▐██ ▂▄▇██████████████▏ + ▝███████████████████▌ ▐███████████████████▘ + ▝██████████████████▌ ▐██████████████████▘ + ▝▜████████████████▌ ▐████████████████▛▘ + ▝▜██████████████▙▄▄▟██████████████▛▘ + ▔▀▜██████████████████████████▛▀▔ + ▔▀▜████████████████████▛▀▔ + ▔▔▀▀▀▜██████▛▀▀▀▔▔ \ No newline at end of file diff --git a/src/logo/ascii/midos_old.txt b/src/logo/ascii/midos_old.txt new file mode 100644 index 0000000000..4790708285 --- /dev/null +++ b/src/logo/ascii/midos_old.txt @@ -0,0 +1,20 @@ + .:=+*#%%@@@@@@%%#*+=:. + .=*%@@@@@@@@@@@@@@@@@@@@@@%*=: + .=%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+. + :*@@@@@@@@@@@@@@@@@$2++$1%@@@@@@@@@@@@@@@@#- + .*@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@#. + -%@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@- + -@@@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@@= +:@@@@@@@@@@@@@@@@@%%@@@%$2..$1#@@@@#@@@@@@@@@@@@@@@@@: +*@@@@@@@@@@@@#$2*=:..$1*@@@%$2..$1#@@@#$2..:=+$1#@@@@@@@@@@@@* +%@@@@@@@#+$2=:...-+$1#@@@@@%$2..$1#@@@@@#$2+=:..:-$1+#@@@@@@@% +%@@@@@$2:..:=+-..-+$1#@@@@@%$2..$1#@@@@@%$2+-..-+=:..:$1@@@@@@ +*@@@@@$2*%$1@@@@@@%$2+-..$1#@@@%$2..$1#@@@%$2..-+$1#@@@@@@$2%*$1@@@@@* +:@@@@@@@@@@@@@@@@@$2#%$1@@@%$2..$1#@@@$2%#$1@@@@@@@@@@@@@@@@@: + =@@@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@@= + -@@@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@@@= + .*@@@@@@@@@@@@@@@@@@%$2..$1#@@@@@@@@@@@@@@@@@@#: + :*@@@@@@@@@@@@@@@@@$2==$1%@@@@@@@@@@@@@@@@#-. + .+%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%+: + :=*%@@@@@@@@@@@@@@@@@@@@@@%*=:. + .-=+*#%%@@@@@@%%#*+=-. \ No newline at end of file diff --git a/src/logo/ascii/windows_2025.txt b/src/logo/ascii/windows_2025.txt new file mode 100644 index 0000000000..1c02084e78 --- /dev/null +++ b/src/logo/ascii/windows_2025.txt @@ -0,0 +1,17 @@ +$1 ##%%%%%%%%% $2%%%%%%%%%## +$1 ###%%%%%%%%%% $2%%%%%%%%%%### +$1 ####%%%%%%%%%%% $2%%%%%%%%%%%#### +$1 ##%%%%%%%%%%%%%% $2%%%%%%%%%%%%%%## +$1#%%%%%%%%%%%%%%%% $2%%%%%%%%%%%%%%%%# +$1%%%%%%%%%%%%%%%%% $2%%%%%%%%%%%%%%%%% +$1%%%%%%%%%%%%%%%%% $2%%%%%%%%%%%%%%%%% +$1%%%%%%%%%%%%%%%%% $2#%%%%%%%%%%%%%%%% + +$3%%%%%%%%%%%%%%%%% $4#%%%%%%%%%%%%%%%% +$3%%%%%%%%%%%%%%%%% $4%%%%%%%%%%%%%%%%% +$3%%%%%%%%%%%%%%%%% $4%%%%%%%%%%%%%%%%% +$3%%%%%%%%%%%%%%%%% $4%%%%%%%%%%%%%%%%# +$3 ###%%%%%%%%%%%%% $4%%%%%%%%%%%%%%%## +$3 ####%%%%%%%%%%% $4%%%%%%%%%%%#%#### +$3 ##%#%%%%%%%%% $4%%%%%%%%%%%###### +$3 ##%%%%%%%%% $4%%%%%%%%%######## \ No newline at end of file diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 77d2e05c05..c0e8670095 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -2837,6 +2837,17 @@ static const FFlogo M[] = { { .names = {"MidOS"}, .lines = FASTFETCH_DATATEXT_LOGO_MIDOS, + .colors = { + FF_COLOR_FG_LIGHT_BLACK, + }, + .colorKeys = FF_COLOR_FG_LIGHT_BLACK, + .colorTitle = FF_COLOR_FG_WHITE, + }, + // MidOSOld + { + .names = {"MidOS_old"}, + .lines = FASTFETCH_DATATEXT_LOGO_MIDOS_OLD, + .type = FF_LOGO_LINE_TYPE_ALTER_BIT, .colors = { FF_COLOR_FG_LIGHT_BLACK, FF_COLOR_FG_WHITE, @@ -4809,6 +4820,19 @@ static const FFlogo W[] = { FF_COLOR_FG_WHITE, }, }, + // Windows2025 + { + .names = {"Windows Server 2025"}, + .lines = FASTFETCH_DATATEXT_LOGO_WINDOWS_2025, + .colors = { + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + FF_COLOR_FG_BLUE, + }, + .colorKeys = FF_COLOR_FG_YELLOW, + .colorTitle = FF_COLOR_FG_CYAN, + }, // Windows11 { .names = {"Windows 11", "Windows Server 2022"}, diff --git a/src/modules/cpu/cpu.c b/src/modules/cpu/cpu.c index 614b0bf26a..1a924ca5db 100644 --- a/src/modules/cpu/cpu.c +++ b/src/modules/cpu/cpu.c @@ -6,7 +6,7 @@ #include "modules/cpu/cpu.h" #include "util/stringUtils.h" -#define FF_CPU_NUM_FORMAT_ARGS 9 +#define FF_CPU_NUM_FORMAT_ARGS 10 static int sortCores(const FFCPUCore* a, const FFCPUCore* b) { @@ -55,6 +55,9 @@ void ffPrintCPU(FFCPUOptions* options) FF_STRBUF_AUTO_DESTROY str = ffStrbufCreate(); + if(cpu.packages > 1) + ffStrbufAppendF(&str, "%u x ", cpu.packages); + if(cpu.name.length > 0) ffStrbufAppend(&str, &cpu.name); else if(cpu.vendor.length > 0) @@ -68,7 +71,12 @@ void ffPrintCPU(FFCPUOptions* options) if(coreTypes.length > 0) ffStrbufAppendF(&str, " (%s)", coreTypes.chars); else if(cpu.coresOnline > 1) - ffStrbufAppendF(&str, " (%u)", cpu.coresOnline); + { + if(cpu.packages > 1) + ffStrbufAppendF(&str, " (%u)", cpu.coresOnline / 2); + else + ffStrbufAppendF(&str, " (%u)", cpu.coresOnline); + } uint32_t freq = cpu.frequencyMax; if(freq == 0) @@ -106,6 +114,7 @@ void ffPrintCPU(FFCPUOptions* options) FF_FORMAT_ARG(freqMax, "freq-max"), FF_FORMAT_ARG(tempStr, "temperature"), FF_FORMAT_ARG(coreTypes, "core-types"), + FF_FORMAT_ARG(cpu.packages, "packages"), })); } } @@ -203,6 +212,10 @@ void ffGenerateCPUJsonResult(FFCPUOptions* options, yyjson_mut_doc* doc, yyjson_ yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); yyjson_mut_obj_add_strbuf(doc, obj, "cpu", &cpu.name); yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &cpu.vendor); + if (cpu.packages == 0) + yyjson_mut_obj_add_null(doc, obj, "packages"); + else + yyjson_mut_obj_add_uint(doc, obj, "packages", cpu.packages); yyjson_mut_val* cores = yyjson_mut_obj_add_obj(doc, obj, "cores"); yyjson_mut_obj_add_uint(doc, cores, "physical", cpu.coresPhysical); @@ -240,6 +253,7 @@ void ffPrintCPUHelpFormat(void) "Max frequency (formatted) - freq-max", "Temperature (formatted) - temperature", "Logical core count grouped by frequency - core-types", + "Processor package count - packages", })); } diff --git a/src/modules/display/display.c b/src/modules/display/display.c index 6c720f31bd..07884d5011 100644 --- a/src/modules/display/display.c +++ b/src/modules/display/display.c @@ -6,7 +6,7 @@ #include -#define FF_DISPLAY_NUM_FORMAT_ARGS 20 +#define FF_DISPLAY_NUM_FORMAT_ARGS 24 static int sortByNameAsc(FFDisplayResult* a, FFDisplayResult* b) { @@ -148,7 +148,7 @@ void ffPrintDisplay(FFDisplayOptions* options) } else { - double ppi = sqrt(result->width * result->width + result->height * result->height) / inch; + double ppi = inch == 0 ? 0 : sqrt(result->width * result->width + result->height * result->height) / inch; bool hdrEnabled = result->hdrStatus == FF_DISPLAY_HDR_STATUS_ENABLED; bool hdrCompatible = result->hdrStatus == FF_DISPLAY_HDR_STATUS_SUPPORTED || result->hdrStatus == FF_DISPLAY_HDR_STATUS_ENABLED; uint32_t iInch = (uint32_t) (inch + 0.5), iPpi = (uint32_t) (ppi + 0.5); @@ -164,6 +164,17 @@ void ffPrintDisplay(FFDisplayOptions* options) else refreshRate[0] = 0; + char preferredRefreshRate[16]; + if(result->preferredRefreshRate > 0) + { + if(options->preciseRefreshRate) + snprintf(preferredRefreshRate, ARRAY_SIZE(preferredRefreshRate), "%g", ((int) (result->preferredRefreshRate * 1000 + 0.5)) / 1000.0); + else + snprintf(preferredRefreshRate, ARRAY_SIZE(preferredRefreshRate), "%i", (uint32_t) (result->preferredRefreshRate + 0.5)); + } + else + preferredRefreshRate[0] = 0; + char buf[32]; if (result->serial) { @@ -173,6 +184,8 @@ void ffPrintDisplay(FFDisplayOptions* options) else buf[0] = '\0'; + double scaleFactor = (double) result->width / (double) result->scaledWidth; + FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_DISPLAY_NUM_FORMAT_ARGS, ((FFformatarg[]) { FF_FORMAT_ARG(result->width, "width"), FF_FORMAT_ARG(result->height, "height"), @@ -194,6 +207,10 @@ void ffPrintDisplay(FFDisplayOptions* options) FF_FORMAT_ARG(buf, "serial"), FF_FORMAT_ARG(result->platformApi, "platform-api"), FF_FORMAT_ARG(hdrCompatible, "hdr-compatible"), + FF_FORMAT_ARG(scaleFactor, "scale-factor"), + FF_FORMAT_ARG(result->preferredWidth, "preferred-width"), + FF_FORMAT_ARG(result->preferredHeight, "preferred-height"), + FF_FORMAT_ARG(preferredRefreshRate, "preferred-refresh-rate"), })); } } @@ -350,16 +367,21 @@ void ffGenerateDisplayJsonResult(FF_MAYBE_UNUSED FFDisplayOptions* options, yyjs yyjson_mut_val* output = yyjson_mut_obj_add_obj(doc, obj, "output"); yyjson_mut_obj_add_uint(doc, output, "width", item->width); yyjson_mut_obj_add_uint(doc, output, "height", item->height); + yyjson_mut_obj_add_real(doc, output, "refreshRate", item->refreshRate); yyjson_mut_val* scaled = yyjson_mut_obj_add_obj(doc, obj, "scaled"); yyjson_mut_obj_add_uint(doc, scaled, "width", item->scaledWidth); yyjson_mut_obj_add_uint(doc, scaled, "height", item->scaledHeight); + yyjson_mut_val* preferred = yyjson_mut_obj_add_obj(doc, obj, "preferred"); + yyjson_mut_obj_add_uint(doc, preferred, "width", item->preferredWidth); + yyjson_mut_obj_add_uint(doc, preferred, "height", item->preferredHeight); + yyjson_mut_obj_add_real(doc, preferred, "refreshRate", item->preferredRefreshRate); + yyjson_mut_val* physical = yyjson_mut_obj_add_obj(doc, obj, "physical"); yyjson_mut_obj_add_uint(doc, physical, "width", item->physicalWidth); yyjson_mut_obj_add_uint(doc, physical, "height", item->physicalHeight); - yyjson_mut_obj_add_real(doc, obj, "refreshRate", item->refreshRate); yyjson_mut_obj_add_uint(doc, obj, "rotation", item->rotation); yyjson_mut_obj_add_uint(doc, obj, "bitDepth", item->bitDepth); if (item->hdrStatus == FF_DISPLAY_HDR_STATUS_UNKNOWN) @@ -416,9 +438,9 @@ void ffGenerateDisplayJsonResult(FF_MAYBE_UNUSED FFDisplayOptions* options, yyjs void ffPrintDisplayHelpFormat(void) { FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_DISPLAY_MODULE_NAME, "{1}x{2} @ {3}Hz (as {4}x{5}) [{7}]", FF_DISPLAY_NUM_FORMAT_ARGS, ((const char* []) { - "Screen width (in pixels) - width", - "Screen height (in pixels) - height", - "Screen refresh rate (in Hz) - refresh-rate", + "Screen configured width (in pixels) - width", + "Screen configured height (in pixels) - height", + "Screen configured refresh rate (in Hz) - refresh-rate", "Screen scaled width (in pixels) - scaled-width", "Screen scaled height (in pixels) - scaled-height", "Screen name - name", @@ -436,6 +458,10 @@ void ffPrintDisplayHelpFormat(void) "Serial number - serial", "The platform API used when detecting the display - platform-api", "True if the display is HDR compatible - hdr-compatible", + "HiDPI scale factor - scale-factor", + "Screen preferred width (in pixels) - preferred-width", + "Screen preferred height (in pixels) - preferred-height", + "Screen preferred refresh rate (in Hz) - preferred-refresh-rate", })); } diff --git a/src/modules/users/users.c b/src/modules/users/users.c index 7be21109bc..650db448db 100644 --- a/src/modules/users/users.c +++ b/src/modules/users/users.c @@ -191,7 +191,7 @@ void ffGenerateUsersJsonResult(FFUsersOptions* options, yyjson_mut_doc* doc, yyj void ffPrintUsersHelpFormat(void) { FF_PRINT_MODULE_FORMAT_HELP_CHECKED(FF_USERS_MODULE_NAME, "{1}@{2} - login time {5}", FF_USERS_NUM_FORMAT_ARGS, ((const char* []) { - "User name - user-name", + "User name - name", "Host name - host-name", "Session name - session", "Client IP - client-ip", diff --git a/src/modules/wifi/wifi.c b/src/modules/wifi/wifi.c index b2d829d42f..f52382d0c3 100644 --- a/src/modules/wifi/wifi.c +++ b/src/modules/wifi/wifi.c @@ -4,7 +4,7 @@ #include "modules/wifi/wifi.h" #include "util/stringUtils.h" -#define FF_WIFI_NUM_FORMAT_ARGS 11 +#define FF_WIFI_NUM_FORMAT_ARGS 13 void ffPrintWifi(FFWifiOptions* options) { @@ -29,6 +29,27 @@ void ffPrintWifi(FFWifiOptions* options) FFWifiResult* item = FF_LIST_GET(FFWifiResult, result, index); uint8_t moduleIndex = result.length == 1 ? 0 : (uint8_t)(index + 1); + // https://en.wikipedia.org/wiki/List_of_WLAN_channels + char bandStr[8]; + if (item->conn.frequency > 58000) + strcpy(bandStr, "60"); + if (item->conn.frequency > 40000) + strcpy(bandStr, "45"); + else if (item->conn.frequency > 5900) + strcpy(bandStr, "6"); + else if (item->conn.frequency > 5100) + strcpy(bandStr, "5"); + else if (item->conn.frequency > 4900) + strcpy(bandStr, "4.9"); + else if (item->conn.frequency > 3600) + strcpy(bandStr, "3.65"); + else if (item->conn.frequency > 2000) + strcpy(bandStr, "2.4"); + else if (item->conn.frequency > 800) + strcpy(bandStr, "0.9"); + else + bandStr[0] = '\0'; + if(options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_WIFI_MODULE_NAME, moduleIndex, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); @@ -54,6 +75,8 @@ void ffPrintWifi(FFWifiOptions* options) ffStrbufAppendS(&buffer, " - "); ffStrbufAppend(&buffer, &item->conn.protocol); } + if (bandStr[0]) + ffStrbufAppendF(&buffer, " - %s GHz", bandStr); if(item->conn.security.length) { ffStrbufAppendS(&buffer, " - "); @@ -97,6 +120,8 @@ void ffPrintWifi(FFWifiOptions* options) FF_FORMAT_ARG(item->conn.txRate, "tx-rate"), FF_FORMAT_ARG(item->conn.security, "security"), FF_FORMAT_ARG(percentBar, "signal-quality-bar"), + FF_FORMAT_ARG(item->conn.channel, "channel"), + FF_FORMAT_ARG(bandStr, "band"), })); } @@ -181,6 +206,8 @@ void ffGenerateWifiJsonResult(FF_MAYBE_UNUSED FFWifiOptions* options, yyjson_mut yyjson_mut_obj_add_real(doc, conn, "signalQuality", wifi->conn.signalQuality); yyjson_mut_obj_add_real(doc, conn, "rxRate", wifi->conn.rxRate); yyjson_mut_obj_add_real(doc, conn, "txRate", wifi->conn.txRate); + yyjson_mut_obj_add_uint(doc, conn, "channel", wifi->conn.channel); + yyjson_mut_obj_add_uint(doc, conn, "frequency", wifi->conn.frequency); } FF_LIST_FOR_EACH(FFWifiResult, item, result) @@ -209,6 +236,8 @@ void ffPrintWifiHelpFormat(void) "Connection TX rate - tx-rate", "Connection Security algorithm - security", "Connection signal quality (percentage bar) - signal-quality-bar", + "Connection channel number - channel", + "Connection channel band in GHz - band", })); } diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 68240f00ec..31ec637d62 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -531,3 +531,69 @@ void ffStrbufInsertNC(FFstrbuf* strbuf, uint32_t index, uint32_t num, char c) memset(&strbuf->chars[index], c, num); strbuf->length += num; } + +/** + * @brief Read a line from a FFstrbuf. + * + * @details Behaves like getline(3) but reads from a FFstrbuf. + * + * @param[in,out] lineptr The pointer to a pointer that will be set to the start of the line. + * Can be NULL for the first call. + * @param[in,out] n The pointer to the size of the buffer of lineptr. + * @param[in] buffer The buffer to read from. The buffer must not be a string literal. + * + * @return true if a line has been read, false if the end of the buffer has been reached. + */ +bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer) +{ + assert(lineptr && n && buffer); + assert(buffer->allocated > 0 || (buffer->allocated == 0 && buffer->length == 0)); + assert(!*lineptr || (*lineptr >= buffer->chars && *lineptr <= buffer->chars + buffer->length)); + + const char* pBufferEnd = buffer->chars + buffer->length; + if (!*lineptr) + *lineptr = buffer->chars; + else + { + *lineptr += *n; + if (*lineptr >= pBufferEnd) // non-empty last line + return false; + **lineptr = '\n'; + ++*lineptr; + } + if (*lineptr >= pBufferEnd) // empty last line + return false; + + size_t remaining = (size_t) (pBufferEnd - *lineptr); + char* ending = memchr(*lineptr, '\n', remaining); + if (ending) + { + *n = (size_t) (ending - *lineptr); + *ending = '\0'; + } + else + *n = remaining; + return true; +} + +bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf) +{ + if (strbuf->allocated == 0) return false; // Doesn't work with static strings + + bool changed = false; + for (uint32_t i = 0; i < strbuf->length; i++) + { + if (strbuf->chars[i] != ' ') continue; + + i++; + uint32_t j = i; + for (; j < strbuf->length && strbuf->chars[j] == ' '; j++); + + if (j == i) continue; + memmove(&strbuf->chars[i], &strbuf->chars[j], strbuf->length - j + 1); + strbuf->length -= j - i; + changed = true; + } + + return changed; +} diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index 2ac123352c..1c754b10a6 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -90,6 +90,9 @@ FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultV void ffStrbufUpperCase(FFstrbuf* strbuf); void ffStrbufLowerCase(FFstrbuf* strbuf); +bool ffStrbufGetline(char** lineptr, size_t* n, FFstrbuf* buffer); +bool ffStrbufRemoveDupWhitespaces(FFstrbuf* strbuf); + FF_C_NODISCARD static inline FFstrbuf ffStrbufCreateA(uint32_t allocate) { FFstrbuf strbuf; diff --git a/src/util/edidHelper.c b/src/util/edidHelper.c index adf621197d..8c920bd30d 100644 --- a/src/util/edidHelper.c +++ b/src/util/edidHelper.c @@ -65,15 +65,15 @@ bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name) void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height) { - *width = (((uint32_t) edid[68] & 0xF0) << 4) + edid[66]; - *height = (((uint32_t) edid[68] & 0x0F) << 8) + edid[67]; + *width = edid[21] * 10; + *height = edid[22] * 10; } void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week) { if (edid[17] > 0 && edid[17] < 0xFF) { - *year = (uint16_t) edid[17] + 1990; + *year = (uint16_t) (edid[17] + 1990); *week = (uint16_t) edid[16]; if (*week == 0xFF) *week = 0; } diff --git a/tests/strbuf.c b/tests/strbuf.c index 92141d4b91..005a12fa5b 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -458,6 +458,141 @@ int main(void) VERIFY(strbuf2.allocated == 32); } + { + int i = 0; + char* lineptr = NULL; + size_t n = 0; + const char* text = "Processor\t: ARMv7\nprocessor\t: 0\nBogoMIPS\t: 38.00\n\nprocessor\t: 1\nBogoMIPS\t: 38.00"; + ffStrbufSetS(&strbuf, text); + + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + switch (i) + { + case 1: + VERIFY(strcmp(lineptr, "Processor\t: ARMv7") == 0); + VERIFY(n == strlen("Processor\t: ARMv7")); + break; + case 2: + VERIFY(strcmp(lineptr, "processor\t: 0") == 0); + VERIFY(n == strlen("processor\t: 0")); + break; + case 3: + VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0); + VERIFY(n == strlen("BogoMIPS\t: 38.00")); + break; + case 4: + VERIFY(strcmp(lineptr, "") == 0); + VERIFY(n == 0); + break; + case 5: + VERIFY(strcmp(lineptr, "processor\t: 1") == 0); + VERIFY(n == strlen("processor\t: 1")); + break; + case 6: + VERIFY(strcmp(lineptr, "BogoMIPS\t: 38.00") == 0); + VERIFY(n == strlen("BogoMIPS\t: 38.00")); + break; + default: + VERIFY(false); + break; + } + } + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 6); + + lineptr = NULL; + n = 0; + i = 0; + text = "\n"; + ffStrbufSetS(&strbuf, text); + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + switch (i) + { + case 1: + VERIFY(strcmp(lineptr, "") == 0); + VERIFY(n == 0); + break; + default: + VERIFY(false); + break; + } + } + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 1); + + lineptr = NULL; + n = 0; + i = 0; + text = "abcd"; + ffStrbufSetS(&strbuf, text); + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + switch (i) + { + case 1: + VERIFY(strcmp(lineptr, "abcd") == 0); + VERIFY(n == strlen("abcd")); + break; + default: + VERIFY(false); + break; + } + } + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 1); + + lineptr = NULL; + n = 0; + i = 0; + text = ""; + ffStrbufSetS(&strbuf, text); + while (ffStrbufGetline(&lineptr, &n, &strbuf)) + { + ++i; + VERIFY(false); + } + + VERIFY(ffStrbufEqualS(&strbuf, text)); + VERIFY(*lineptr == '\0'); + VERIFY(i == 0); + } + + ffStrbufSetS(&strbuf, "Hello World"); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false); + VERIFY(strcmp(strbuf.chars, "Hello World") == 0); + + ffStrbufSetS(&strbuf, "Hello World"); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true); + VERIFY(strcmp(strbuf.chars, "Hello World") == 0); + + ffStrbufSetS(&strbuf, " Hello World "); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true); + VERIFY(strcmp(strbuf.chars, " Hello World ") == 0); + + ffStrbufSetS(&strbuf, " Hello World "); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true); + VERIFY(strcmp(strbuf.chars, " Hello World ") == 0); + + ffStrbufSetS(&strbuf, " "); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == true); + VERIFY(strcmp(strbuf.chars, " ") == 0); + + ffStrbufClear(&strbuf); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false); + VERIFY(strcmp(strbuf.chars, "") == 0); + + ffStrbufSetStatic(&strbuf, " "); + VERIFY(ffStrbufRemoveDupWhitespaces(&strbuf) == false); + VERIFY(strcmp(strbuf.chars, " ") == 0); + //Success puts("\e[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); }