diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa519107..0683c6727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 2.6.0 + +Changes: +* Remove support of option `--battery-dir`. We detect a lot of things in `/sys/class/*` and only module `Battery` supports specifying a custom directory for some reason, which is weird. +* Remove `--chassis-use-wmi` which is no longer used. + +Features: +* Add `ENABLE_PROPRIETARY_GPU_DRIVER_API` cmake option to disable using of proprietary GPU driver APIs (GPU) +* Support wallpaper detection for macOS Sonoma (Wallpaper, macOS) +* Support power adapter detection for Asahi Linux (PowerAdapter, Linux) +* Support battery serial number and manufacturer date detection (Battery) +* Support host serial number and UUID detection (Host) +* Support battery level detection for gamepads where possible (Gamepad) +* Support maximum CPU clock detection. Previously base clock was printed (CPU, Windows) +* Support manufacture date and serial number detection for physical monitors (Monitor) +* Support ash (default shell of BusyBox) version detection (Shell, Linux) +* Sound module in FreeBSD now uses native `ioctl`s. Pulseaudio dependency is no longer used. +* Locale module in Windows now prints the same format as in Linux and other posix systems. + +Bugfixes: +* Fix overall memory leaks (macOS) +* Remove trailing `\0` in JSON results (FreeBSD) +* Fix physical monitor detection with Nvidia drivers (Monitor, Linux) +* Don't print llvmpipe in vulkan module (Vulkan) +* Fix system yyjson usage in `fastfetch.c`. Previously embedded `3rdparty/yyjson/yyjson.h` was used in `fastfetch.c` even if `ENABLE_SYSTEM_YYJSON` was set (CMake) +* Fix locale module printing unexpected results in specific environments (Locale) +* Fix battery temperature detection in Windows. Note only smart batteries report temperatures but few laptops uses smart battery (Battery, Windows) +* Print device name if no backlight name is available, so we don't print empty parentheses (Brightness, FreeBSD) + # 2.5.0 Changes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 21f0051a8..b1247d7f9 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.5.0 + VERSION 2.6.0 LANGUAGES C DESCRIPTION "Fast neofetch-like system information tool" HOMEPAGE_URL "https://github.com/fastfetch-cli/fastfetch" @@ -66,7 +66,7 @@ cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD OR WIN32" OFF) cmake_dependent_option(ENABLE_LIBNM "Enable libnm" ON "LINUX" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) -cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX OR BSD" OFF) +cmake_dependent_option(ENABLE_PULSE "Enable pulse" ON "LINUX" OFF) cmake_dependent_option(ENABLE_DDCUTIL "Enable ddcutil" ON "LINUX" OFF) cmake_dependent_option(ENABLE_DIRECTX_HEADERS "Enable DirectX headers for WSL" ON "LINUX" OFF) cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF) @@ -74,6 +74,7 @@ cmake_dependent_option(ENABLE_PCI_MEMORY "Enable detecting GPU memory size with option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF) option(ENABLE_ASAN "Build fastfetch with ASAN (address sanitizer)" OFF) +option(ENABLE_PROPRIETARY_GPU_DRIVER_API "Enable proprietary GPU driver API (NVML, IGCL and AGS)" ON) option(BUILD_TESTS "Build tests" OFF) # Also create test executables option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds @@ -109,12 +110,8 @@ endif() if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--tsaware -Wl,--build-id -Wl,--subsystem,console:6.1,--major-os-version,6,--minor-os-version,1") -endif() - -# Used for dlopen finding dylibs installed by homebrew -# `/opt/homebrew/lib` is not on in dlopen search path by default -if(APPLE AND DEFINED ENV{HOMEBREW_PREFIX}) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,$ENV{HOMEBREW_PREFIX}/lib") +elseif(APPLE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fobjc-arc") endif() set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer") @@ -290,7 +287,6 @@ set(LIBFASTFETCH_SRC src/detection/displayserver/displayserver.c src/detection/font/font.c src/detection/gpu/gpu.c - src/detection/locale/locale.c src/detection/media/media.c src/detection/netio/netio.c src/detection/opencl/opencl.c @@ -374,7 +370,6 @@ set(LIBFASTFETCH_SRC src/util/FFlist.c src/util/FFstrbuf.c src/util/platform/FFPlatform.c - src/util/stringUtils.c src/util/smbiosHelper.c ) @@ -405,12 +400,12 @@ if(LINUX) src/detection/displayserver/linux/xlib.c src/detection/font/font_linux.c src/detection/gpu/gpu_linux.c - src/detection/gpu/gpu_nvidia.c src/detection/gtk_qt/gtk.c src/detection/host/host_linux.c src/detection/icons/icons_linux.c src/detection/libc/libc_linux.c src/detection/lm/lm_linux.c + src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_linux.c src/detection/media/media_linux.c @@ -420,7 +415,7 @@ if(LINUX) src/detection/opengl/opengl_linux.c src/detection/os/os_linux.c src/detection/packages/packages_linux.c - src/detection/poweradapter/poweradapter_nosupport.c + src/detection/poweradapter/poweradapter_linux.c src/detection/processes/processes_linux.c src/detection/gtk_qt/qt.c src/detection/sound/sound_linux.c @@ -464,6 +459,7 @@ elseif(ANDROID) src/detection/icons/icons_nosupport.c src/detection/libc/libc_android.c src/detection/lm/lm_nosupport.c + src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_nosupport.c src/detection/media/media_nosupport.c @@ -519,12 +515,12 @@ elseif(BSD) src/detection/displayserver/linux/xlib.c src/detection/font/font_linux.c src/detection/gpu/gpu_linux.c - src/detection/gpu/gpu_nvidia.c src/detection/gtk_qt/gtk.c src/detection/host/host_bsd.c src/detection/lm/lm_linux.c src/detection/icons/icons_linux.c src/detection/libc/libc_bsd.c + src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_bsd.c src/detection/media/media_linux.c @@ -537,7 +533,7 @@ elseif(BSD) src/detection/poweradapter/poweradapter_nosupport.c src/detection/processes/processes_bsd.c src/detection/gtk_qt/qt.c - src/detection/sound/sound_linux.c + src/detection/sound/sound_bsd.c src/detection/swap/swap_bsd.c src/detection/temps/temps_bsd.c src/detection/terminalfont/terminalfont_linux.c @@ -580,6 +576,7 @@ elseif(APPLE) src/detection/lm/lm_nosupport.c src/detection/icons/icons_nosupport.c src/detection/libc/libc_apple.c + src/detection/locale/locale_linux.c src/detection/localip/localip_linux.c src/detection/gamepad/gamepad_apple.c src/detection/media/media_apple.m @@ -600,7 +597,7 @@ elseif(APPLE) src/detection/theme/theme_nosupport.c src/detection/uptime/uptime_bsd.c src/detection/users/users_linux.c - src/detection/wallpaper/wallpaper_apple.c + src/detection/wallpaper/wallpaper_apple.m src/detection/wifi/wifi_apple.m src/detection/wm/wm_apple.c src/detection/de/de_nosupport.c @@ -620,7 +617,7 @@ elseif(WIN32) src/detection/bluetooth/bluetooth_windows.c src/detection/board/board_windows.c src/detection/brightness/brightness_windows.cpp - src/detection/chassis/chassis_windows.cpp + src/detection/chassis/chassis_windows.c src/detection/cpu/cpu_windows.c src/detection/cpuusage/cpuusage_windows.c src/detection/cursor/cursor_windows.c @@ -630,13 +627,11 @@ elseif(WIN32) src/detection/displayserver/displayserver_windows.c src/detection/font/font_windows.c src/detection/gpu/gpu_windows.c - src/detection/gpu/gpu_nvidia.c - src/detection/gpu/gpu_intel.c - src/detection/gpu/gpu_amd.c src/detection/host/host_windows.c src/detection/icons/icons_windows.c src/detection/libc/libc_windows.cpp src/detection/lm/lm_nosupport.c + src/detection/locale/locale_windows.c src/detection/localip/localip_windows.c src/detection/gamepad/gamepad_windows.c src/detection/media/media_nosupport.c @@ -676,6 +671,17 @@ if(ENABLE_DIRECTX_HEADERS) list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_wsl.cpp) endif() +if(ENABLE_PROPRIETARY_GPU_DRIVER_API AND (LINUX OR BSD OR WIN32)) + message(STATUS "Enabling proprietary GPU driver API") + if(LINUX OR BSD OR WIN32) + list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_nvidia.c) + endif() + if(WIN32) + list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_intel.c) + list(APPEND LIBFASTFETCH_SRC src/detection/gpu/gpu_amd.c) + endif() +endif() + include(CheckFunctionExists) check_function_exists(wcwidth HAVE_WCWIDTH) if(NOT HAVE_WCWIDTH) @@ -699,9 +705,20 @@ add_library(libfastfetch OBJECT ${LIBFASTFETCH_SRC} ) +if(ENABLE_PROPRIETARY_GPU_DRIVER_API AND (LINUX OR BSD OR WIN32)) + target_compile_definitions(libfastfetch PRIVATE FF_USE_PROPRIETARY_GPU_DRIVER_API) +endif() + if(yyjson_FOUND) target_compile_definitions(libfastfetch PRIVATE FF_USE_SYSTEM_YYJSON) - target_link_libraries(libfastfetch PRIVATE yyjson) + target_link_libraries(libfastfetch PRIVATE yyjson::yyjson) + # `target_link_libraries(yyjson::yyjson)` sets rpath implicitly +else() + # Used for dlopen finding dylibs installed by homebrew + # `/opt/homebrew/lib` is not on in dlopen search path by default + if(APPLE AND DEFINED ENV{HOMEBREW_PREFIX}) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,$ENV{HOMEBREW_PREFIX}/lib") + endif() endif() if(LINUX AND EXISTS "/lib/ld-musl-${CMAKE_HOST_SYSTEM_PROCESSOR}.so.1") @@ -993,6 +1010,13 @@ set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") set_target_properties(fastfetch PROPERTIES LINKER_LANGUAGE C) set_target_properties(flashfetch PROPERTIES LINKER_LANGUAGE C) +if(yyjson_FOUND) + target_compile_definitions(fastfetch PRIVATE FF_USE_SYSTEM_YYJSON) + target_link_libraries(fastfetch PRIVATE yyjson::yyjson) + target_compile_definitions(flashfetch PRIVATE FF_USE_SYSTEM_YYJSON) + target_link_libraries(flashfetch PRIVATE yyjson::yyjson) +endif() + if(WIN32) set(TARGET_NAME fastfetch) target_sources(fastfetch diff --git a/completions/bash b/completions/bash index c6e7b26dc..71f21649d 100644 --- a/completions/bash +++ b/completions/bash @@ -384,7 +384,6 @@ __fastfetch_completion() "--lib-pulse" "--lib-ddcutil" "--lib-nm" - "--battery-dir" ) local FF_OPTIONS_LOGO=( diff --git a/doc/json_schema.json b/doc/json_schema.json index dd097dbe6..ce00756da 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -804,10 +804,6 @@ "const": "battery", "description": "Print battery capacity, status, etc" }, - "dir": { - "description": "The directory where the battery folders are. Standard: `/sys/class/power_supply/`. Linux only", - "type": "string" - }, "useSetupApi": { "description": "Set if `SetupAPI` should be used on Windows to detect battery info, which supports multi batteries, but slower. Windows only", "type": "boolean", @@ -883,11 +879,6 @@ "const": "chassis", "description": "Print chassis type (desktop, laptop, etc)" }, - "useWmi": { - "description": "Set if WMI query should be used on Windows, which detects more information but slower", - "type": "boolean", - "default": false - }, "key": { "$ref": "#/$defs/key" }, diff --git a/src/common/init.c b/src/common/init.c index 12724aa2d..358cc113d 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -244,6 +244,15 @@ void ffListFeatures(void) #ifdef FF_HAVE_DIRECTX_HEADERS "Directx Headers\n" #endif + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + "Proprietary GPU driver API\n" + #endif + #ifdef FF_USE_SYSTEM_YYJSON + "System yyjson\n" + #endif + #ifdef FF_USE_PCI_MEMORY + "PCI memory\n" + #endif "" , stdout); } diff --git a/src/common/io/io.h b/src/common/io/io.h index ec3375bd0..d188942ca 100644 --- a/src/common/io/io.h +++ b/src/common/io/io.h @@ -105,7 +105,7 @@ static inline bool ffPathExists(const char* path, FFPathType pathType) unsigned int mode = fileStat.st_mode & S_IFMT; - if(pathType & FF_PATHTYPE_FILE && mode == S_IFREG) + if(pathType & FF_PATHTYPE_FILE && mode != S_IFDIR) return true; if(pathType & FF_PATHTYPE_DIRECTORY && mode == S_IFDIR) diff --git a/src/common/processing_windows.c b/src/common/processing_windows.c index 398194b34..c93e5a9e4 100644 --- a/src/common/processing_windows.c +++ b/src/common/processing_windows.c @@ -83,8 +83,7 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use char str[FF_PIPE_BUFSIZ]; DWORD nRead = 0; - FF_AUTO_CLOSE_FD HANDLE hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); - OVERLAPPED overlapped = { .hEvent = hEvent }; + OVERLAPPED overlapped = { }; // ReadFile always completes synchronously if the pipe is not created with FILE_FLAG_OVERLAPPED do { @@ -93,11 +92,11 @@ const char* ffProcessAppendOutput(FFstrbuf* buffer, char* const argv[], bool use switch (GetLastError()) { case ERROR_IO_PENDING: - if (!timeout || WaitForSingleObject(hEvent, (DWORD) timeout) != WAIT_OBJECT_0) + if (!timeout || WaitForSingleObject(hChildPipeRead, (DWORD) timeout) != WAIT_OBJECT_0) { CancelIo(hChildPipeRead); TerminateProcess(hProcess, 1); - return "WaitForSingleObject(hEvent) failed or timeout"; + return "WaitForSingleObject(hChildPipeRead) failed or timeout"; } if (!GetOverlappedResult(hChildPipeRead, &overlapped, &nRead, FALSE)) diff --git a/src/common/settings.c b/src/common/settings.c index ae057a292..a01d4e706 100644 --- a/src/common/settings.c +++ b/src/common/settings.c @@ -411,9 +411,8 @@ bool ffSettingsGetFreeBSDKenv(const char* propName, FFstrbuf* result) //https://wiki.ghostbsd.org/index.php/Kenv ffStrbufEnsureFree(result, KENV_MVALLEN); int len = kenv(KENV_GET, propName, result->chars + result->length, KENV_MVALLEN); - if (len <= 0) return false; - result->length += (uint32_t) len; - result->chars[result->length] = '\0'; + if (len <= 1) return false; // number of bytes copied, including NUL terminator + result->length += (uint32_t) len - 1; return true; } #endif diff --git a/src/data/help.json b/src/data/help.json index 44f264caf..d3da78a82 100644 --- a/src/data/help.json +++ b/src/data/help.json @@ -842,16 +842,6 @@ "default": "default" } }, - { - "long": "chassis-use-wmi", - "desc": "Set if WMI query should be used on Windows", - "remark": "WMI query detects more information but is slower. Windows only", - "arg": { - "type": "bool", - "optional": true, - "default": false - } - }, { "long": "separator-string", "desc": "Set the string printed by the separator module", @@ -1026,15 +1016,6 @@ "default": "main" } }, - { - "long": "battery-dir", - "desc": "The directory where the battery folders are", - "remark": "Linux only", - "arg": { - "type": "path", - "default": "/sys/class/power_supply/" - } - }, { "long": "battery-use-setup-api", "desc": "Set if \"SetupAPI\" should be used on Windows to detect battery info", diff --git a/src/detection/battery/battery.h b/src/detection/battery/battery.h index 6dfe6466b..2f4c8e3f1 100644 --- a/src/detection/battery/battery.h +++ b/src/detection/battery/battery.h @@ -7,10 +7,12 @@ typedef struct FFBatteryResult { FFstrbuf manufacturer; + FFstrbuf manufactureDate; FFstrbuf modelName; FFstrbuf technology; - double capacity; FFstrbuf status; + FFstrbuf serial; + double capacity; double temperature; uint32_t cycleCount; } FFBatteryResult; diff --git a/src/detection/battery/battery_android.c b/src/detection/battery/battery_android.c index 6ddf3bbfe..8939625cf 100644 --- a/src/detection/battery/battery_android.c +++ b/src/detection/battery/battery_android.c @@ -40,6 +40,7 @@ static const char* parseTermuxApi(FFBatteryOptions* options, FFlist* results) ffStrbufInit(&battery->modelName); ffStrbufInit(&battery->status); ffStrbufInit(&battery->technology); + ffStrbufInit(&battery->manufactureDate); battery->capacity = yyjson_get_num(yyjson_obj_get(root, "percentage")); ffStrbufAppendS(&battery->status, yyjson_get_str(yyjson_obj_get(root, "status"))); diff --git a/src/detection/battery/battery_apple.c b/src/detection/battery/battery_apple.c index f301297f1..8aafed206 100644 --- a/src/detection/battery/battery_apple.c +++ b/src/detection/battery/battery_apple.c @@ -4,49 +4,46 @@ #include "detection/temps/temps_apple.h" #include +#include const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) { - io_iterator_t iterator; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("AppleSmartBattery"), &iterator) != kIOReturnSuccess) + FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL; + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("AppleSmartBattery"), &iterator) != kIOReturnSuccess) return "IOServiceGetMatchingServices() failed"; io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryBattery = registryEntry; FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef properties = NULL; - if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) - { - IOObjectRelease(registryEntry); + if (IORegistryEntryCreateCFProperties(entryBattery, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) continue; - } bool boolValue; - const char* error; FFBatteryResult* battery = ffListAdd(results); battery->temperature = FF_BATTERY_TEMP_UNSET; ffStrbufInit(&battery->manufacturer); ffStrbufInit(&battery->modelName); + ffStrbufInit(&battery->serial); ffStrbufInit(&battery->technology); ffStrbufInit(&battery->status); + ffStrbufInit(&battery->manufactureDate); battery->capacity = 0.0/0.0; int currentCapacity, maxCapacity; - if ((error = ffCfDictGetInt(properties, CFSTR("MaxCapacity"), &maxCapacity))) - return error; - if (maxCapacity <= 0) - return "Querying MaxCapacity failed"; + if (ffCfDictGetInt(properties, CFSTR(kIOPMPSMaxCapacityKey), &maxCapacity) != NULL || maxCapacity <= 0) + continue; - if ((error = ffCfDictGetInt(properties, CFSTR("CurrentCapacity"), ¤tCapacity))) - return error; - if(currentCapacity <= 0) - return "Querying CurrentCapacity failed"; + if (ffCfDictGetInt(properties, CFSTR(kIOPMPSCurrentCapacityKey), ¤tCapacity) != NULL || currentCapacity <= 0) + continue; battery->capacity = currentCapacity * 100.0 / maxCapacity; - ffCfDictGetString(properties, CFSTR("DeviceName"), &battery->modelName); + ffCfDictGetString(properties, CFSTR(kIOPMDeviceNameKey), &battery->modelName); + ffCfDictGetString(properties, CFSTR(kIOPMPSSerialKey), &battery->serial); if (!ffCfDictGetBool(properties, CFSTR("built-in"), &boolValue) && boolValue) { @@ -57,33 +54,43 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) } int32_t cycleCount = 0; - ffCfDictGetInt(properties, CFSTR("CycleCount"), &cycleCount); + ffCfDictGetInt(properties, CFSTR(kIOPMPSCycleCountKey), &cycleCount); battery->cycleCount = cycleCount < 0 ? 0 : (uint32_t) cycleCount; - if (!ffCfDictGetBool(properties, CFSTR("ExternalConnected"), &boolValue) && boolValue) + if (!ffCfDictGetBool(properties, CFSTR(kIOPMPSExternalConnectedKey), &boolValue) && boolValue) ffStrbufAppendS(&battery->status, "AC connected, "); else ffStrbufAppendS(&battery->status, "Discharging, "); - if (!ffCfDictGetBool(properties, CFSTR("IsCharging"), &boolValue) && boolValue) + if (!ffCfDictGetBool(properties, CFSTR(kIOPMPSIsChargingKey), &boolValue) && boolValue) ffStrbufAppendS(&battery->status, "Charging, "); - if (!ffCfDictGetBool(properties, CFSTR("AtCriticalLevel"), &boolValue) && boolValue) + if (!ffCfDictGetBool(properties, CFSTR(kIOPMPSAtCriticalLevelKey), &boolValue) && boolValue) ffStrbufAppendS(&battery->status, "Critical, "); ffStrbufTrimRight(&battery->status, ' '); ffStrbufTrimRight(&battery->status, ','); + CFDictionaryRef batteryData; + if (ffCfDictGetDict(properties, CFSTR("BatteryData"), &batteryData) == NULL) + { + char manufactureDate[sizeof(uint64_t)]; + if (ffCfDictGetInt64(batteryData, CFSTR(kIOPMPSManufactureDateKey), (int64_t*) manufactureDate) == NULL) + { + // https://github.com/AsahiLinux/linux/blob/b5c05cbffb0488c7618106926d522cc3b43d93d5/drivers/power/supply/macsmc_power.c#L410-L419 + int year = (manufactureDate[0] - '0') * 10 + (manufactureDate[1] - '0') + 2000 - 8; + int month = (manufactureDate[2] - '0') * 10 + (manufactureDate[3] - '0'); + int day = (manufactureDate[4] - '0') * 10 + (manufactureDate[3] - '5'); + ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", year, month, day); + } + } + if (options->temp) { int64_t temp; - if (!ffCfDictGetInt64(properties, CFSTR("Temperature"), &temp)) + if (!ffCfDictGetInt64(properties, CFSTR(kIOPMPSBatteryTemperatureKey), &temp)) battery->temperature = (double) temp / 10 - 273.15; else ffDetectSmcTemps(FF_TEMP_BATTERY, &battery->temperature); } - - IOObjectRelease(registryEntry); } - IOObjectRelease(iterator); - return NULL; } diff --git a/src/detection/battery/battery_bsd.c b/src/detection/battery/battery_bsd.c index 3edba2a62..4376e6e75 100644 --- a/src/detection/battery/battery_bsd.c +++ b/src/detection/battery/battery_bsd.c @@ -39,6 +39,8 @@ const char* ffDetectBattery(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* r ffStrbufInit(&battery->modelName); ffStrbufInit(&battery->status); ffStrbufInit(&battery->technology); + ffStrbufInit(&battery->serial); + ffStrbufInit(&battery->manufactureDate); battery->capacity = battio.battinfo.cap; if(battio.battinfo.state == ACPI_BATT_STAT_INVALID) diff --git a/src/detection/battery/battery_linux.c b/src/detection/battery/battery_linux.c index 04b8a9a0a..085e4a892 100644 --- a/src/detection/battery/battery_linux.c +++ b/src/detection/battery/battery_linux.c @@ -9,106 +9,133 @@ static void parseBattery(FFstrbuf* dir, const char* id, FFBatteryOptions* option { uint32_t dirLength = dir->length; - FF_STRBUF_AUTO_DESTROY testBatteryBuffer = ffStrbufCreate(); + FF_STRBUF_AUTO_DESTROY tmpBuffer = ffStrbufCreate(); //type must exist and be "Battery" ffStrbufAppendS(dir, "/type"); - ffReadFileBuffer(dir->chars, &testBatteryBuffer); - ffStrbufTrimRightSpace(&testBatteryBuffer); + if (ffReadFileBuffer(dir->chars, &tmpBuffer)) + ffStrbufTrimRightSpace(&tmpBuffer); ffStrbufSubstrBefore(dir, dirLength); - if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Battery") != 0) + if(!ffStrbufIgnCaseEqualS(&tmpBuffer, "Battery")) return; //scope may not exist or must not be "Device" ffStrbufAppendS(dir, "/scope"); - ffReadFileBuffer(dir->chars, &testBatteryBuffer); - ffStrbufTrimRightSpace(&testBatteryBuffer); + if (ffReadFileBuffer(dir->chars, &tmpBuffer)) + ffStrbufTrimRightSpace(&tmpBuffer); ffStrbufSubstrBefore(dir, dirLength); - if(ffStrbufIgnCaseCompS(&testBatteryBuffer, "Device") == 0) + if(ffStrbufIgnCaseEqualS(&tmpBuffer, "Device")) return; - FFBatteryResult* result = ffListAdd(results); - //capacity must exist and be not empty ffStrbufAppendS(dir, "/capacity"); - bool available = ffReadFileBuffer(dir->chars, &testBatteryBuffer); + bool available = ffReadFileBuffer(dir->chars, &tmpBuffer); ffStrbufSubstrBefore(dir, dirLength); - if(available) - result->capacity = ffStrbufToDouble(&testBatteryBuffer); - if(!available) - { - result->capacity = 0.0/0.0; - --results->length; + if (!available) return; - } + + FFBatteryResult* result = ffListAdd(results); + result->capacity = ffStrbufToDouble(&tmpBuffer); //At this point, we have a battery. Try to get as much values as possible. ffStrbufInit(&result->manufacturer); ffStrbufAppendS(dir, "/manufacturer"); - ffReadFileBuffer(dir->chars, &result->manufacturer); - ffStrbufTrimRightSpace(&result->manufacturer); + if (ffReadFileBuffer(dir->chars, &result->manufacturer)) + ffStrbufTrimRightSpace(&result->manufacturer); + else if (ffStrEquals(id, "macsmc-battery")) // asahi + ffStrbufSetStatic(&result->manufacturer, "Apple Inc."); ffStrbufSubstrBefore(dir, dirLength); ffStrbufInit(&result->modelName); ffStrbufAppendS(dir, "/model_name"); - ffReadFileBuffer(dir->chars, &result->modelName); - ffStrbufTrimRightSpace(&result->modelName); + if (ffReadFileBuffer(dir->chars, &result->modelName)) + ffStrbufTrimRightSpace(&result->modelName); ffStrbufSubstrBefore(dir, dirLength); ffStrbufInit(&result->technology); ffStrbufAppendS(dir, "/technology"); - ffReadFileBuffer(dir->chars, &result->technology); - ffStrbufTrimRightSpace(&result->technology); + if (ffReadFileBuffer(dir->chars, &result->technology)) + ffStrbufTrimRightSpace(&result->technology); ffStrbufSubstrBefore(dir, dirLength); ffStrbufInit(&result->status); ffStrbufAppendS(dir, "/status"); - ffReadFileBuffer(dir->chars, &result->status); - ffStrbufTrimRightSpace(&result->status); + if (ffReadFileBuffer(dir->chars, &result->status)) + ffStrbufTrimRightSpace(&result->status); + ffStrbufSubstrBefore(dir, dirLength); + + ffStrbufInit(&result->serial); + ffStrbufAppendS(dir, "/serial_number"); + if (ffReadFileBuffer(dir->chars, &result->serial)) + ffStrbufTrimRightSpace(&result->serial); ffStrbufSubstrBefore(dir, dirLength); ffStrbufAppendS(dir, "/cycle_count"); - ffReadFileBuffer(dir->chars, &testBatteryBuffer); - ffStrbufTrimRightSpace(&testBatteryBuffer); + available = ffReadFileBuffer(dir->chars, &tmpBuffer); ffStrbufSubstrBefore(dir, dirLength); - if (dir->length) + if (available) { - int64_t cycleCount = 0; - cycleCount = (int64_t) strtoll(testBatteryBuffer.chars, NULL, 10); + int64_t cycleCount = ffStrbufToSInt(&tmpBuffer, 0); result->cycleCount = cycleCount < 0 || cycleCount > UINT32_MAX ? 0 : (uint32_t) cycleCount; } + ffStrbufInit(&result->manufactureDate); + ffStrbufAppendS(dir, "/manufacture_year"); + available = ffReadFileBuffer(dir->chars, &tmpBuffer); + ffStrbufSubstrBefore(dir, dirLength); + if (available) + { + int year = (int) ffStrbufToSInt(&tmpBuffer, 0); + if (year > 0) + { + ffStrbufAppendS(dir, "/manufacture_month"); + available = ffReadFileBuffer(dir->chars, &tmpBuffer); + ffStrbufSubstrBefore(dir, dirLength); + if (available) + { + int month = (int) ffStrbufToSInt(&tmpBuffer, 0); + if (month > 0) + { + ffStrbufAppendS(dir, "/manufacture_day"); + available = ffReadFileBuffer(dir->chars, &tmpBuffer); + ffStrbufSubstrBefore(dir, dirLength); + if (available) + { + int day = (int) ffStrbufToSInt(&tmpBuffer, 0); + if (day > 0) + { + ffStrbufSetF(&result->manufactureDate, "%.4d-%.2d-%.2d", year, month, day); + } + } + } + } + } + } + result->temperature = FF_BATTERY_TEMP_UNSET; if (options->temp) { - const FFlist* tempsResult = ffDetectTemps(); - - FF_LIST_FOR_EACH(FFTempValue, value, *tempsResult) - { - if (ffStrbufEqualS(&value->deviceName, id)) - { - result->temperature = value->value; - break; - } - } + ffStrbufAppendS(dir, "/temp"); + if (ffReadFileBuffer(dir->chars, &tmpBuffer)) + result->temperature = ffStrbufToDouble(&tmpBuffer) / 10; + ffStrbufSubstrBefore(dir, dirLength); } } const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) { FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateA(64); - ffStrbufAppend(&baseDir, &options->dir); - ffStrbufEnsureEndsWithC(&baseDir, '/'); + ffStrbufAppendS(&baseDir, "/sys/class/power_supply/"); uint32_t baseDirLength = baseDir.length; FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir.chars); if(dirp == NULL) - return "opendir(batteryDir) == NULL"; + return "opendir(\"/sys/class/power_supply/\") == NULL"; struct dirent* entry; while((entry = readdir(dirp)) != NULL) @@ -122,7 +149,7 @@ const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) } if(results->length == 0) - return "batteryDir doesn't contain any battery folder"; + return "\"/sys/class/power_supply/\" doesn't contain any battery folder"; return NULL; } diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c index 9deea532c..4e1a44db4 100644 --- a/src/detection/battery/battery_windows.c +++ b/src/detection/battery/battery_windows.c @@ -1,6 +1,7 @@ #include "battery.h" #include "util/windows/unicode.h" #include "util/mallocHelper.h" +#include "util/smbiosHelper.h" #undef WIN32_LEAN_AND_MEAN #include @@ -75,19 +76,19 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results); if(memcmp(bi.Chemistry, "PbAc", 4) == 0) - ffStrbufInitS(&battery->technology, "Lead Acid"); + ffStrbufInitStatic(&battery->technology, "Lead Acid"); else if(memcmp(bi.Chemistry, "LION", 4) == 0 || memcmp(bi.Chemistry, "Li-I", 4) == 0) - ffStrbufInitS(&battery->technology, "Lithium Ion"); + ffStrbufInitStatic(&battery->technology, "Lithium Ion"); else if(memcmp(bi.Chemistry, "NiCd", 4) == 0) - ffStrbufInitS(&battery->technology, "Nickel Cadmium"); + ffStrbufInitStatic(&battery->technology, "Nickel Cadmium"); else if(memcmp(bi.Chemistry, "NiMH", 4) == 0) - ffStrbufInitS(&battery->technology, "Nickel Metal Hydride"); + ffStrbufInitStatic(&battery->technology, "Nickel Metal Hydride"); else if(memcmp(bi.Chemistry, "NiZn", 4) == 0) - ffStrbufInitS(&battery->technology, "Nickel Zinc"); + ffStrbufInitStatic(&battery->technology, "Nickel Zinc"); else if(memcmp(bi.Chemistry, "RAM\0", 4) == 0) - ffStrbufInitS(&battery->technology, "Rechargeable Alkaline-Manganese"); + ffStrbufInitStatic(&battery->technology, "Rechargeable Alkaline-Manganese"); else - ffStrbufInitS(&battery->technology, "Unknown"); + ffStrbufInitStatic(&battery->technology, "Unknown"); { ffStrbufInit(&battery->modelName); @@ -105,6 +106,22 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results ffStrbufSetWS(&battery->manufacturer, name); } + { + ffStrbufInit(&battery->manufactureDate); + bqi.InformationLevel = BatteryManufactureDate; + BATTERY_MANUFACTURE_DATE date; + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &date, sizeof(date), &dwOut, NULL)) + ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", date.Year + 1900, date.Month, date.Day); + } + + { + ffStrbufInit(&battery->serial); + bqi.InformationLevel = BatterySerialNumber; + wchar_t name[64]; + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL)) + ffStrbufSetWS(&battery->serial, name); + } + battery->cycleCount = bi.CycleCount; battery->temperature = 0.0/0.0; @@ -113,7 +130,7 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results bqi.InformationLevel = BatteryTemperature; ULONG temp; if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &temp, sizeof(temp), &dwOut, NULL)) - battery->temperature = temp; + battery->temperature = temp / 10.0 - 273.15; } { @@ -140,6 +157,83 @@ static const char* detectWithSetupApi(FFBatteryOptions* options, FFlist* results return NULL; } + + +typedef struct FFSmbiosPortableBattery +{ + FFSmbiosHeader Header; + + // 2.1+ + uint8_t Location; // string + uint8_t Manufacturer; // string + uint8_t ManufactureDate; // string + uint8_t SerialNumber; // string + uint8_t DeviceName; // string + uint8_t DeviceChemistry; // enum + uint16_t DesignCapacity; // varies + uint16_t DesignVoltage; // varies + uint8_t SbdsVersionNumber; // string + uint8_t MaximumErrorInBatteryData; // varies + + // 2.2+ + uint16_t SbdsSerialNumber; // varies + uint16_t SbdsManufactureDate; // varies + uint8_t SbdsDeviceChemistry; // string + uint8_t DesignCapacityMultiplier; // varies + uint16_t OEMSpecific; // varies +} FFSmbiosPortableBattery; + +const char* detectBySmbios(FFBatteryResult* battery) +{ + const FFSmbiosPortableBattery* data = (const FFSmbiosPortableBattery*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_PORTABLE_BATTERY]; + if (!data) + return "Portable battery section is not found in SMBIOS data"; + + const char* strings = (const char*) data + data->Header.Length; + + ffStrbufSetStatic(&battery->modelName, ffSmbiosLocateString(strings, data->DeviceName)); + ffCleanUpSmbiosValue(&battery->modelName); + ffStrbufSetStatic(&battery->manufacturer, ffSmbiosLocateString(strings, data->Manufacturer)); + ffCleanUpSmbiosValue(&battery->manufacturer); + + if (data->ManufactureDate) + { + ffStrbufSetStatic(&battery->manufactureDate, ffSmbiosLocateString(strings, data->ManufactureDate)); + ffCleanUpSmbiosValue(&battery->manufactureDate); + } + else if (data->Header.Length > offsetof(FFSmbiosPortableBattery, SbdsManufactureDate)) + { + uint16_t day = data->SbdsManufactureDate & 0b11111; + uint16_t month = (data->SbdsManufactureDate >> 5) & 0b1111; + uint16_t year = (data->SbdsManufactureDate >> 9) + 1800; + ffStrbufSetF(&battery->manufactureDate, "%.4d-%.2d-%.2d", year, month, day); + } + + switch (data->DeviceChemistry) + { + case 0x01: ffStrbufSetStatic(&battery->technology, "Other"); break; + case 0x02: ffStrbufSetStatic(&battery->technology, "Unknown"); break; + case 0x03: ffStrbufSetStatic(&battery->technology, "Lead Acid"); break; + case 0x04: ffStrbufSetStatic(&battery->technology, "Nickel Cadmium"); break; + case 0x05: ffStrbufSetStatic(&battery->technology, "Nickel metal hydride"); break; + case 0x06: ffStrbufSetStatic(&battery->technology, "Lithium-ion"); break; + case 0x07: ffStrbufSetStatic(&battery->technology, "Zinc air"); break; + case 0x08: ffStrbufSetStatic(&battery->technology, "Lithium Polymer"); break; + } + + if (data->SerialNumber) + { + ffStrbufSetStatic(&battery->serial, ffSmbiosLocateString(strings, data->SerialNumber)); + ffCleanUpSmbiosValue(&battery->serial); + } + else if (data->Header.Length > offsetof(FFSmbiosPortableBattery, SbdsSerialNumber)) + { + ffStrbufSetF(&battery->serial, "%4X", data->SbdsSerialNumber); + } + + return NULL; +} + static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FFlist* results) { SYSTEM_BATTERY_STATE info; @@ -148,8 +242,10 @@ static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FF FFBatteryResult* battery = (FFBatteryResult*)ffListAdd(results); ffStrbufInit(&battery->modelName); ffStrbufInit(&battery->manufacturer); + ffStrbufInit(&battery->manufactureDate); ffStrbufInit(&battery->technology); ffStrbufInit(&battery->status); + ffStrbufInit(&battery->serial); battery->temperature = FF_BATTERY_TEMP_UNSET; battery->cycleCount = 0; @@ -162,6 +258,9 @@ static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FF } else if(info.Discharging) ffStrbufAppendS(&battery->status, "Discharging"); + + detectBySmbios(battery); + return NULL; } return "NtPowerInformation(SystemBatteryState) failed"; @@ -169,7 +268,7 @@ static const char* detectWithNtApi(FF_MAYBE_UNUSED FFBatteryOptions* options, FF const char* ffDetectBattery(FFBatteryOptions* options, FFlist* results) { - return true + return options->useSetupApi ? detectWithSetupApi(options, results) : detectWithNtApi(options, results); } diff --git a/src/detection/bios/bios_bsd.c b/src/detection/bios/bios_bsd.c index 6111b4315..aa115de21 100644 --- a/src/detection/bios/bios_bsd.c +++ b/src/detection/bios/bios_bsd.c @@ -2,6 +2,7 @@ #include "common/settings.h" #include "common/sysctl.h" +#include "common/io/io.h" #include "util/smbiosHelper.h" const char* ffDetectBios(FFBiosResult* result) @@ -15,5 +16,18 @@ const char* ffDetectBios(FFBiosResult* result) ffSettingsGetFreeBSDKenv("smbios.bios.version", &result->version); ffCleanUpSmbiosValue(&result->version); ffSysctlGetString("machdep.bootmethod", &result->type); + + if (result->type.length == 0) + { + if (ffSettingsGetFreeBSDKenv("loader.efi", &result->type)) + ffStrbufSetStatic(&result->type, ffStrbufEqualS(&result->type, "1") ? "UEFI" : "BIOS"); + else + { + ffStrbufSetStatic(&result->type, + ffPathExists("/dev/efi" /*efidev*/, FF_PATHTYPE_FILE) || + ffPathExists("/boot/efi/efi" /*efi partition*/, FF_PATHTYPE_DIRECTORY) + ? "UEFI" : "BIOS"); + } + } return NULL; } diff --git a/src/detection/bios/bios_windows.c b/src/detection/bios/bios_windows.c index 5adfbdd0d..23ff25275 100644 --- a/src/detection/bios/bios_windows.c +++ b/src/detection/bios/bios_windows.c @@ -1,5 +1,4 @@ #include "bios.h" -#include "util/windows/registry.h" #include "util/smbiosHelper.h" #include @@ -26,27 +25,46 @@ typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION }; } SYSTEM_BOOT_ENVIRONMENT_INFORMATION; + +typedef struct FFSmbiosBios +{ + FFSmbiosHeader Header; + + uint8_t Vendor; // string + uint8_t BiosVersion; // string + uint16_t BiosStartingAddressSegment; // varies + uint8_t BiosReleaseDate; // string + uint8_t BiosRomSize; // string + uint32_t BiosCharacteristics; // bit field + + // 2.4+ + uint8_t BiosCharacteristicsExtensionBytes[2]; // bit field + uint8_t SystemBiosMajorRelease; // varies + uint8_t SystemBiosMinorRelease; // varies + uint8_t EmbeddedControllerFirmwareMajorRelease; // varies + uint8_t EmbeddedControllerFirmwareMinorRelease; // varies + + // 3.1+ + uint16_t ExtendedBiosRomSize; // bit field +} FFSmbiosBios; + const char* ffDetectBios(FFBiosResult* bios) { - FF_HKEY_AUTO_DESTROY hKey = NULL; - if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, NULL)) - return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\BIOS\", &hKey, NULL) failed"; + const FFSmbiosBios* data = (const FFSmbiosBios*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_BIOS]; + if (!data) + return "BIOS section is not found in SMBIOS data"; - if(!ffRegReadStrbuf(hKey, L"BIOSVersion", &bios->version, NULL)) - return "\"HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\BIOS\\BIOSVersion\" doesn't exist"; + const char* strings = (const char*) data + data->Header.Length; + ffStrbufSetStatic(&bios->version, ffSmbiosLocateString(strings, data->BiosVersion)); ffCleanUpSmbiosValue(&bios->version); - ffRegReadStrbuf(hKey, L"BIOSVendor", &bios->vendor, NULL); + ffStrbufSetStatic(&bios->vendor, ffSmbiosLocateString(strings, data->Vendor)); ffCleanUpSmbiosValue(&bios->vendor); - ffRegReadStrbuf(hKey, L"BIOSReleaseDate", &bios->date, NULL); + ffStrbufSetStatic(&bios->date, ffSmbiosLocateString(strings, data->BiosReleaseDate)); ffCleanUpSmbiosValue(&bios->date); - uint32_t major, minor; - if( - ffRegReadUint(hKey, L"BiosMajorRelease", &major, NULL) && - ffRegReadUint(hKey, L"BiosMinorRelease", &minor, NULL) - ) - ffStrbufAppendF(&bios->release, "%u.%u", (unsigned)major, (unsigned)minor); + if (data->Header.Length > offsetof(FFSmbiosBios, SystemBiosMajorRelease)) + ffStrbufSetF(&bios->release, "%u.%u", data->SystemBiosMajorRelease, data->SystemBiosMinorRelease); // Same as GetFirmwareType, but support (?) Windows 7 // https://ntdoc.m417z.com/system_information_class diff --git a/src/detection/board/board.h b/src/detection/board/board.h index 44ba5c8ab..64a8dd3a7 100644 --- a/src/detection/board/board.h +++ b/src/detection/board/board.h @@ -7,6 +7,7 @@ typedef struct FFBoardResult FFstrbuf name; FFstrbuf vendor; FFstrbuf version; + FFstrbuf serial; } FFBoardResult; const char* ffDetectBoard(FFBoardResult* board); diff --git a/src/detection/board/board_apple.c b/src/detection/board/board_apple.c index 56f582215..6381590d8 100644 --- a/src/detection/board/board_apple.c +++ b/src/detection/board/board_apple.c @@ -1,13 +1,21 @@ #include "board.h" #include "common/sysctl.h" +#include "util/apple/cf_helpers.h" const char* ffDetectBoard(FFBoardResult* result) { - const char* error = ffSysctlGetString("hw.model", &result->name); - if (error) - return error; + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t service = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("IOPlatformExpertDevice")); + if (!service) + return "No IOPlatformExpertDevice found"; + + io_name_t name; + if (IORegistryEntryGetName(service, name) == kIOReturnSuccess) + ffStrbufSetS(&result->name, name); + + FF_CFTYPE_AUTO_RELEASE CFTypeRef manufacturer = IORegistryEntryCreateCFProperty(service, CFSTR("manufacturer"), kCFAllocatorDefault, kNilOptions); + if (manufacturer) + ffCfStrGetString(manufacturer, &result->vendor); - ffStrbufSetStatic(&result->vendor, "Apple"); return NULL; } diff --git a/src/detection/board/board_bsd.c b/src/detection/board/board_bsd.c index 871cf87aa..3fc5f9637 100644 --- a/src/detection/board/board_bsd.c +++ b/src/detection/board/board_bsd.c @@ -6,6 +6,8 @@ const char* ffDetectBoard(FFBoardResult* result) { ffSettingsGetFreeBSDKenv("smbios.planar.product", &result->name); ffCleanUpSmbiosValue(&result->name); + ffSettingsGetFreeBSDKenv("smbios.planar.serial", &result->serial); + ffCleanUpSmbiosValue(&result->serial); ffSettingsGetFreeBSDKenv("smbios.planar.maker", &result->vendor); ffCleanUpSmbiosValue(&result->vendor); ffSettingsGetFreeBSDKenv("smbios.planar.version", &result->version); diff --git a/src/detection/board/board_linux.c b/src/detection/board/board_linux.c index 1092b54ee..380db6c14 100644 --- a/src/detection/board/board_linux.c +++ b/src/detection/board/board_linux.c @@ -7,6 +7,7 @@ const char* ffDetectBoard(FFBoardResult* board) { ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &board->name); + ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_serial", "/sys/class/dmi/id/board_serial", &board->serial); ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &board->vendor); ffGetSmbiosValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &board->version); return NULL; diff --git a/src/detection/board/board_windows.c b/src/detection/board/board_windows.c index a2bfce4ff..d16d4a905 100644 --- a/src/detection/board/board_windows.c +++ b/src/detection/board/board_windows.c @@ -1,21 +1,38 @@ #include "board.h" -#include "util/windows/registry.h" #include "util/smbiosHelper.h" +typedef struct FFSmbiosBaseboard +{ + FFSmbiosHeader Header; + + uint8_t Manufacturer; // string + uint8_t Product; // string + uint8_t Version; // string + uint8_t SerialNumber; // string + uint8_t AssetTag; // string + uint8_t FeatureFlags; // bit field + uint8_t LocationInChassis; // string + uint16_t ChassisHandle; // varies + uint8_t BoardType; // enum + uint8_t NumberOfContainedObjectHandle; // varies + uint16_t ContainedObjectHandle[]; // varies +} FFSmbiosBaseboard; + const char* ffDetectBoard(FFBoardResult* board) { - FF_HKEY_AUTO_DESTROY hKey = NULL; + const FFSmbiosBaseboard* data = (const FFSmbiosBaseboard*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_BASEBOARD_INFO]; + if (!data) + return "Baseboard information section is not found in SMBIOS data"; - if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, NULL)) - return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\BIOS\") failed"; - - if(!ffRegReadStrbuf(hKey, L"BaseBoardProduct", &board->name, NULL)) - return "ffRegReadStrbuf(hKey, L\"BaseBoardProduct\") failed"; + const char* strings = (const char*) data + data->Header.Length; + ffStrbufSetStatic(&board->name, ffSmbiosLocateString(strings, data->Product)); ffCleanUpSmbiosValue(&board->name); - ffRegReadStrbuf(hKey, L"BaseBoardManufacturer", &board->vendor, NULL); + ffStrbufSetStatic(&board->serial, ffSmbiosLocateString(strings, data->SerialNumber)); + ffCleanUpSmbiosValue(&board->serial); + ffStrbufSetStatic(&board->vendor, ffSmbiosLocateString(strings, data->Manufacturer)); ffCleanUpSmbiosValue(&board->vendor); - ffRegReadStrbuf(hKey, L"BaseBoardVersion", &board->version, NULL); + ffStrbufSetStatic(&board->version, ffSmbiosLocateString(strings, data->Version)); ffCleanUpSmbiosValue(&board->version); return NULL; diff --git a/src/detection/brightness/brightness_apple.c b/src/detection/brightness/brightness_apple.c index 4c58b5e6a..a9f8daf0c 100644 --- a/src/detection/brightness/brightness_apple.c +++ b/src/detection/brightness/brightness_apple.c @@ -57,35 +57,35 @@ static const char* detectWithDdcci(FF_MAYBE_UNUSED const FFDisplayServerResult* if (!IOAVServiceCreate || !IOAVServiceReadI2C) return "IOAVService is not available"; - io_iterator_t iterator; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("DCPAVServiceProxy"), &iterator) != kIOReturnSuccess) + FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL; + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("DCPAVServiceProxy"), &iterator) != kIOReturnSuccess) return "IOServiceGetMatchingServices() failed"; - FF_STRBUF_AUTO_DESTROY location = ffStrbufCreate(); - io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { - CFMutableDictionaryRef properties; - if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) + FF_CFTYPE_AUTO_RELEASE IOAVServiceRef service = NULL; { - IOObjectRelease(registryEntry); - continue; + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryAv = registryEntry; + + FF_CFTYPE_AUTO_RELEASE CFBooleanRef IOAVServiceUserInterfaceSupported = IORegistryEntryCreateCFProperty(entryAv, CFSTR("IOAVServiceUserInterfaceSupported"), kCFAllocatorDefault, kNilOptions); + if (IOAVServiceUserInterfaceSupported && !CFBooleanGetValue(IOAVServiceUserInterfaceSupported)) + { + // IOAVServiceCreateWithService won't work + continue; + } + + FF_CFTYPE_AUTO_RELEASE CFStringRef location = IORegistryEntryCreateCFProperty(entryAv, CFSTR("Location"), kCFAllocatorDefault, kNilOptions); + if (location && CFStringCompare(location, CFSTR("Embedded"), 0) == 0) + { + // Builtin display should be handled by DisplayServices + continue; + } + + service = IOAVServiceCreateWithService(kCFAllocatorDefault, (io_service_t) registryEntry); + if (!service) continue; } - ffStrbufClear(&location); - if(ffCfDictGetString(properties, CFSTR("Location"), &location) || ffStrbufEqualS(&location, "Embedded")) - { - // Builtin display should be handled by DisplayServices - IOObjectRelease(registryEntry); - continue; - } - - FF_CFTYPE_AUTO_RELEASE IOAVServiceRef service = IOAVServiceCreateWithService(kCFAllocatorDefault, (io_service_t) registryEntry); - IOObjectRelease(registryEntry); - - if (!service) continue; - { uint8_t i2cIn[4] = { 0x82, 0x01, 0x10 /* luminance */ }; i2cIn[3] = 0x6e ^ i2cIn[0] ^ i2cIn[1] ^ i2cIn[2]; diff --git a/src/detection/brightness/brightness_bsd.c b/src/detection/brightness/brightness_bsd.c index 34b91ab5c..22b3772fb 100644 --- a/src/detection/brightness/brightness_bsd.c +++ b/src/detection/brightness/brightness_bsd.c @@ -36,6 +36,8 @@ const char* ffDetectBrightness(FF_MAYBE_UNUSED FFBrightnessOptions* options, FFl struct backlight_info info; if(ioctl(blfd, BACKLIGHTGETINFO, &info) < 0) ffStrbufAppendS(&brightness->name, info.name); + else + ffStrbufAppendS(&brightness->name, path + strlen("/dev/backlight/")); } return NULL; } diff --git a/src/detection/chassis/chassis.c b/src/detection/chassis/chassis.c index f2ebc2da1..9e76bcfc8 100644 --- a/src/detection/chassis/chassis.c +++ b/src/detection/chassis/chassis.c @@ -2,9 +2,9 @@ const char* ffChassisTypeToString(uint32_t type) { - // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf + // https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.7.0.pdf // 7.4.1 System Enclosure or Chassis Types - switch (type) + switch (type & 0b01111111) { case 0x01: return "Other"; case 0x02: return "Unknown"; diff --git a/src/detection/chassis/chassis.h b/src/detection/chassis/chassis.h index 60befc83e..0c9c0380d 100644 --- a/src/detection/chassis/chassis.h +++ b/src/detection/chassis/chassis.h @@ -5,9 +5,10 @@ typedef struct FFChassisResult { FFstrbuf type; + FFstrbuf serial; FFstrbuf vendor; FFstrbuf version; } FFChassisResult; -const char* ffDetectChassis(FFChassisResult* result, FFChassisOptions* options); +const char* ffDetectChassis(FFChassisResult* result); const char* ffChassisTypeToString(uint32_t type); diff --git a/src/detection/chassis/chassis_bsd.c b/src/detection/chassis/chassis_bsd.c index 54a84c7e9..816a5dcad 100644 --- a/src/detection/chassis/chassis_bsd.c +++ b/src/detection/chassis/chassis_bsd.c @@ -2,13 +2,15 @@ #include "common/settings.h" #include "util/smbiosHelper.h" -const char* ffDetectChassis(FFChassisResult* result, FF_MAYBE_UNUSED FFChassisOptions* options) +const char* ffDetectChassis(FFChassisResult* result) { // Unlike other platforms, `smbios.chassis.type` return display string directly on my machine ffSettingsGetFreeBSDKenv("smbios.chassis.type", &result->type); ffCleanUpSmbiosValue(&result->type); ffSettingsGetFreeBSDKenv("smbios.chassis.maker", &result->vendor); ffCleanUpSmbiosValue(&result->vendor); + ffSettingsGetFreeBSDKenv("smbios.chassis.serial", &result->serial); + ffCleanUpSmbiosValue(&result->serial); ffSettingsGetFreeBSDKenv("smbios.chassis.version", &result->version); ffCleanUpSmbiosValue(&result->version); return NULL; diff --git a/src/detection/chassis/chassis_linux.c b/src/detection/chassis/chassis_linux.c index 8c050b859..8968a2093 100644 --- a/src/detection/chassis/chassis_linux.c +++ b/src/detection/chassis/chassis_linux.c @@ -4,9 +4,10 @@ #include -const char* ffDetectChassis(FFChassisResult* result, FF_MAYBE_UNUSED FFChassisOptions* options) +const char* ffDetectChassis(FFChassisResult* result) { ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &result->type); + ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_serial", "/sys/class/dmi/id/chassis_serial", &result->serial); ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_vendor", "/sys/class/dmi/id/chassis_vendor", &result->vendor); ffGetSmbiosValue("/sys/devices/virtual/dmi/id/chassis_version", "/sys/class/dmi/id/chassis_version", &result->version); diff --git a/src/detection/chassis/chassis_nosupport.c b/src/detection/chassis/chassis_nosupport.c index 6d83b90d1..c2acd7579 100644 --- a/src/detection/chassis/chassis_nosupport.c +++ b/src/detection/chassis/chassis_nosupport.c @@ -1,6 +1,6 @@ #include "chassis.h" -const char* ffDetectChassis(FF_MAYBE_UNUSED FFChassisResult* result, FF_MAYBE_UNUSED FFChassisOptions* options) +const char* ffDetectChassis(FF_MAYBE_UNUSED FFChassisResult* result) { return "Not supported on this platform"; } diff --git a/src/detection/chassis/chassis_windows.c b/src/detection/chassis/chassis_windows.c new file mode 100644 index 000000000..754b85a3d --- /dev/null +++ b/src/detection/chassis/chassis_windows.c @@ -0,0 +1,47 @@ +#include "chassis.h" +#include "util/smbiosHelper.h" + +// 7.4 +typedef struct FFSmbiosSystemEnclosure +{ + FFSmbiosHeader Header; + + uint8_t Manufacturer; // string + uint8_t Type; // varies + uint8_t Version; // string + uint8_t SerialNumber; // string + uint8_t AssetTagNumber; // string + + // 2.1+ + uint8_t BootupState; // enum + uint8_t PowerSupplyState; // enum + uint8_t ThermalState; // enum + uint8_t SecurityStatus; // enum + + // 2.3+ + uint32_t OEMDefined; // varies + uint8_t Height; // varies + uint8_t NumberOfPowerCords; // varies + uint8_t ContainedElementCount; // varies + uint8_t ContainedRecordLength; // varies + uint8_t ContainedElements[]; // varies +} FFSmbiosSystemEnclosure; + +const char* ffDetectChassis(FFChassisResult* result) +{ + const FFSmbiosSystemEnclosure* data = (const FFSmbiosSystemEnclosure*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_SYSTEM_ENCLOSURE]; + if (!data) + return "System enclosure is not found in SMBIOS data"; + + const char* strings = (const char*) data + data->Header.Length; + + ffStrbufSetStatic(&result->vendor, ffSmbiosLocateString(strings, data->Manufacturer)); + ffCleanUpSmbiosValue(&result->vendor); + ffStrbufSetStatic(&result->serial, ffSmbiosLocateString(strings, data->SerialNumber)); + ffCleanUpSmbiosValue(&result->serial); + ffStrbufSetStatic(&result->version, ffSmbiosLocateString(strings, data->Version)); + ffCleanUpSmbiosValue(&result->version); + ffStrbufSetStatic(&result->type, ffChassisTypeToString(data->Type)); + + return NULL; +} diff --git a/src/detection/chassis/chassis_windows.cpp b/src/detection/chassis/chassis_windows.cpp deleted file mode 100644 index 9a3d37fcc..000000000 --- a/src/detection/chassis/chassis_windows.cpp +++ /dev/null @@ -1,73 +0,0 @@ -extern "C" { -#include "chassis.h" -#include "util/windows/registry.h" -#include "util/smbiosHelper.h" -} -#include "util/windows/unicode.hpp" -#include "util/windows/wmi.hpp" - -static const char* detectWithRegistry(FFChassisResult* result) -{ - FF_HKEY_AUTO_DESTROY hKey = NULL; - if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, NULL)) // SMBIOS - return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\BIOS\", &hKey, NULL) failed"; - - uint32_t type = 0; - if(!ffRegReadUint(hKey, L"EnclosureType", &type, NULL)) - return "\"HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\BIOS\\EnclosureType\" doesn't exist"; - - const char* typeStr = ffChassisTypeToString(type); - if(!typeStr) - return "Unknown chassis type"; - - ffStrbufAppendS(&result->type, typeStr); - return NULL; -} - -FF_MAYBE_UNUSED static const char* detectWithWmi(FFChassisResult* result) -{ - FFWmiQuery query(L"SELECT Version, ChassisTypes, Manufacturer FROM Win32_SystemEnclosure"); - if (!query) - return "Query WMI service failed"; - - if (FFWmiRecord record = query.next()) - { - if (auto vtProp = record.get(L"ChassisTypes")) - { - auto [arr, len] = vtProp.get>(); - if(len == 0) - return "ChassisTypes contain no data"; - for (uint32_t i = 0; i < len; ++i) - { - if (i > 0) - ffStrbufAppendS(&result->type, ", "); - ffStrbufAppendS(&result->type, ffChassisTypeToString((uint32_t) arr[i])); - } - } - else - return "Get ChassisTypes failed"; - - if (auto vtProp = record.get(L"Version")) - { - ffStrbufSetWSV(&result->version, vtProp.get()); - ffCleanUpSmbiosValue(&result->version); - } - - if (auto vtProp = record.get(L"Manufacturer")) - { - ffStrbufSetWSV(&result->vendor, vtProp.get()); - ffCleanUpSmbiosValue(&result->vendor); - } - - return NULL; - } - return "No WMI result returned"; -} - -extern "C" -const char* ffDetectChassis(FFChassisResult* result, FFChassisOptions* options) -{ - if (options->useWmi) - return detectWithWmi(result); - return detectWithRegistry(result); -} diff --git a/src/detection/cpu/cpu_apple.c b/src/detection/cpu/cpu_apple.c index a0afaa697..2f3ec2de7 100644 --- a/src/detection/cpu/cpu_apple.c +++ b/src/detection/cpu/cpu_apple.c @@ -1,6 +1,7 @@ #include "cpu.h" #include "common/sysctl.h" #include "detection/temps/temps_apple.h" +#include "util/stringUtils.h" static double detectCpuTemp(const FFstrbuf* cpuName) { @@ -35,44 +36,33 @@ static const char* detectFrequency(FFCPUResult* cpu) { // https://github.com/giampaolo/psutil/pull/2222/files - io_iterator_t iterator; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("AppleARMIODevice"), &iterator) != kIOReturnSuccess) + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryDevice = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceNameMatching("pmgr")); + if (!entryDevice) return "IOServiceGetMatchingServices() failed"; - io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + if (!IOObjectConformsTo(entryDevice, "AppleARMIODevice")) + return "\"pmgr\" should conform to \"AppleARMIODevice\""; + + FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef properties = NULL; + if (IORegistryEntryCreateCFProperties(entryDevice, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) + return "IORegistryEntryCreateCFProperties() failed"; + + uint32_t pMin, eMin, aMax, pCoreLength; + if (ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), 0, 4, (uint8_t*) &pMin, &pCoreLength) != NULL) // pCore + return "\"voltage-states5-sram\" in \"pmgr\" is not found"; + if (ffCfDictGetData(properties, CFSTR("voltage-states1-sram"), 0, 4, (uint8_t*) &eMin, NULL) != NULL) // eCore + return "\"voltage-states1-sram\" in \"pmgr\" is not found"; + + cpu->frequencyMin = (pMin < eMin ? pMin : eMin) / (1000.0 * 1000 * 1000); + + if (pCoreLength >= 8) { - CFMutableDictionaryRef properties; - if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) - { - IOObjectRelease(registryEntry); - continue; - } - - io_name_t name; - if (IORegistryEntryGetName(registryEntry, name) != KERN_SUCCESS) - continue; - if (strcmp(name, "pmgr") != 0) - continue; - - uint32_t pMin, eMin, aMax, pCoreLength; - ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), 0, 4, (uint8_t*) &pMin, &pCoreLength); // pCore - ffCfDictGetData(properties, CFSTR("voltage-states1-sram"), 0, 4, (uint8_t*) &eMin, NULL); // eCore - cpu->frequencyMin = (pMin < eMin ? pMin : eMin) / (1000.0 * 1000 * 1000); - - if (pCoreLength >= 8) - { - ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), pCoreLength - 8, 4, (uint8_t*) &aMax, NULL); - cpu->frequencyMax = aMax / (1000.0 * 1000 * 1000); - } - else - cpu->frequencyMax = 0.0; - - CFRelease(properties); - IOObjectRelease(registryEntry); + ffCfDictGetData(properties, CFSTR("voltage-states5-sram"), pCoreLength - 8, 4, (uint8_t*) &aMax, NULL); + cpu->frequencyMax = aMax / (1000.0 * 1000 * 1000); } + else + cpu->frequencyMax = 0.0; - IOObjectRelease(iterator); return NULL; } #else diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c index 59f07ab7a..1bb169d74 100644 --- a/src/detection/cpu/cpu_windows.c +++ b/src/detection/cpu/cpu_windows.c @@ -2,8 +2,81 @@ #include "detection/temps/temps_windows.h" #include "util/windows/registry.h" #include "util/mallocHelper.h" +#include "util/smbiosHelper.h" -const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) +// 7.5 +typedef struct FFSmbiosProcessorInfo +{ + FFSmbiosHeader Header; + + uint8_t SocketDesignation; // string + uint8_t ProcessorType; // enum + uint8_t ProcessorFamily; // enum + uint8_t ProcessorManufacturer; // string + uint64_t ProcessorID; // varies + uint8_t ProcessorVersion; // string + uint8_t Voltage; // varies + uint16_t ExternalClock; // varies + uint16_t MaxSpeed; // varies + uint16_t CurrentSpeed; // varies + uint8_t Status; // varies + uint8_t ProcessorUpgrade; // enum + + // 2.1+ + uint16_t L1CacheHandle; // varies + uint16_t L2CacheHandle; // varies + uint16_t L3CacheHandle; // varies + + // 2.3+ + uint8_t SerialNumber; // string + uint8_t AssertTag; // string + uint8_t PartNumber; // string + + // 2.5+ + uint8_t CoreCount; // varies + uint8_t CoreEnabled; // varies + uint8_t ThreadCount; // varies + uint16_t ProcessorCharacteristics; // bit field + + // 2.6+ + uint16_t ProcessorFamily2; // enum + + // 3.0+ + uint16_t CoreCount2; // varies + uint16_t CoreEnabled2; // varies + uint16_t ThreadCount2; // varies + + // 3.6+ + uint16_t ThreadEnabled; // varies +} FFSmbiosProcessorInfo; + +static const char* detectMaxSpeedBySmbios(FFCPUResult* cpu) +{ + const FFSmbiosProcessorInfo* data = (const FFSmbiosProcessorInfo*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_PROCESSOR_INFO]; + + if (!data) + return "Processor information is not found in SMBIOS data"; + + while (data->ProcessorType != 0x03 /*Central Processor*/ || (data->Status & 0b00000111) != 1 /*Enabled*/) + { + data = (const FFSmbiosProcessorInfo*) ffSmbiosNextEntry(&data->Header); + if (data->Header.Type != FF_SMBIOS_TYPE_PROCESSOR_INFO) + return "No active CPU is found in SMBIOS data"; + } + + double speed; + if (data->MaxSpeed > 0 && data->MaxSpeed < 30000) // VMware reports weird values + speed = data->MaxSpeed / 1000.0; + else + speed = data->CurrentSpeed / 1000.0; + + if (cpu->frequencyMax < speed) + cpu->frequencyMax = speed; + + return NULL; +} + +static const char* detectByOS(FFCPUResult* cpu) { { DWORD length = 0; @@ -42,14 +115,24 @@ const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) { uint32_t mhz; if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL)) - cpu->frequencyMax = mhz / 1000.0; + cpu->frequencyMin = cpu->frequencyMax = mhz / 1000.0; } ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL); ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL); + return NULL; +} + +const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) +{ + const char* error = detectByOS(cpu); + if (error) + return error; + if(options->temp) ffDetectSmbiosTemp(&cpu->temperature, NULL); + detectMaxSpeedBySmbios(cpu); return NULL; } diff --git a/src/detection/diskio/diskio_apple.c b/src/detection/diskio/diskio_apple.c index bc5d57f25..2e202f3d9 100644 --- a/src/detection/diskio/diskio_apple.c +++ b/src/detection/diskio/diskio_apple.c @@ -8,22 +8,14 @@ #include #include -static inline void wrapIoObjectRelease(io_service_t* service) -{ - assert(service); - if (*service) - IOObjectRelease(*service); -} -#define FF_IOOBJECT_AUTO_RELEASE __attribute__((__cleanup__(wrapIoObjectRelease))) - const char* ffDiskIOGetIoCounters(FFlist* result, FFDiskIOOptions* options) { FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = 0; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOMediaClass), &iterator) != KERN_SUCCESS) + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOMediaClass), &iterator) != KERN_SUCCESS) return "IOServiceGetMatchingServices() failed"; io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryPartition = registryEntry; diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c index 684f29928..c57c03992 100644 --- a/src/detection/displayserver/displayserver_windows.c +++ b/src/detection/displayserver/displayserver_windows.c @@ -129,8 +129,9 @@ static void detectDisplays(FFDisplayServerResult* ds) (uint32_t) (monitorInfo->info.rcMonitor.bottom - monitorInfo->info.rcMonitor.top), rotation, &name, - path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL || - path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED || + path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER ? FF_DISPLAY_TYPE_UNKNOWN : + path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL || + path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED || path->targetInfo.outputTechnology == DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED ? FF_DISPLAY_TYPE_BUILTIN : FF_DISPLAY_TYPE_EXTERNAL, !!(monitorInfo->info.dwFlags & MONITORINFOF_PRIMARY), diff --git a/src/detection/displayserver/linux/displayserver_linux.c b/src/detection/displayserver/linux/displayserver_linux.c index 60168b0dc..78483b7e4 100644 --- a/src/detection/displayserver/linux/displayserver_linux.c +++ b/src/detection/displayserver/linux/displayserver_linux.c @@ -1,5 +1,8 @@ #include "displayserver_linux.h" -#include "common/io/io.h" + +#ifdef __FreeBSD__ + #include "common/settings.h" +#endif void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) { @@ -31,6 +34,26 @@ void ffConnectDisplayServerImpl(FFDisplayServerResult* ds) if(ds->displays.length == 0) ffdsConnectDrm(ds); + #ifdef __FreeBSD__ + if(ds->displays.length == 0) + { + FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); + if (ffSettingsGetFreeBSDKenv("screen.width", &buf)) + { + uint32_t width = (uint32_t) ffStrbufToUInt(&buf, 0); + if (width) + { + ffStrbufClear(&buf); + 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); + } + } + } + } + #endif + //This fills in missing information about WM / DE by using env vars and iterating processes ffdsDetectWMDE(ds); } diff --git a/src/detection/gamepad/gamepad.h b/src/detection/gamepad/gamepad.h index be63e9dea..3caed4cc7 100644 --- a/src/detection/gamepad/gamepad.h +++ b/src/detection/gamepad/gamepad.h @@ -4,8 +4,9 @@ typedef struct FFGamepadDevice { - FFstrbuf identifier; + FFstrbuf serial; FFstrbuf name; + uint8_t battery; // 0-100% } FFGamepadDevice; const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */); diff --git a/src/detection/gamepad/gamepad_apple.c b/src/detection/gamepad/gamepad_apple.c index b1a8436ca..bfeb65ed0 100644 --- a/src/detection/gamepad/gamepad_apple.c +++ b/src/detection/gamepad/gamepad_apple.c @@ -1,18 +1,34 @@ #include "gamepad.h" #include "util/apple/cf_helpers.h" +#include "util/mallocHelper.h" #include #include -static void enumSet(const void* value, void *context) +static void enumSet(IOHIDDeviceRef value, FFlist* results) { - CFStringRef product = IOHIDDeviceGetProperty((IOHIDDeviceRef) value, CFSTR(kIOHIDProductKey)); - CFStringRef serialNumber = IOHIDDeviceGetProperty((IOHIDDeviceRef) value, CFSTR(kIOHIDSerialNumberKey)); - FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd((FFlist*) context); - ffStrbufInit(&device->identifier); - ffCfStrGetString(serialNumber, &device->identifier); + FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(results); + ffStrbufInit(&device->serial); ffStrbufInit(&device->name); - ffCfStrGetString(product, &device->name); + device->battery = 0; + + CFStringRef manufacturer = IOHIDDeviceGetProperty(value, CFSTR(kIOHIDManufacturerKey)); + ffCfStrGetString(manufacturer, &device->name); + + CFStringRef product = IOHIDDeviceGetProperty(value, CFSTR(kIOHIDProductKey)); + if (device->name.length) + { + ffCfStrGetString(product, &device->serial); + ffStrbufAppendC(&device->name, ' '); + ffStrbufAppend(&device->name, &device->serial); + } + else + { + ffCfStrGetString(product, &device->name); + } + + CFStringRef serialNumber = IOHIDDeviceGetProperty(value, CFSTR(kIOHIDSerialNumberKey)); + ffCfStrGetString(serialNumber, &device->serial); } const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) @@ -21,21 +37,29 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) if (IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) return "IOHIDManagerOpen() failed"; - CFNumberRef FF_CFTYPE_AUTO_RELEASE genericDesktop = ffCfCreateInt(kHIDPage_GenericDesktop); - CFNumberRef FF_CFTYPE_AUTO_RELEASE gamePad = ffCfCreateInt(kHIDUsage_GD_GamePad); - CFDictionaryRef FF_CFTYPE_AUTO_RELEASE matching = CFDictionaryCreate(kCFAllocatorDefault, (const void **)(CFStringRef[]){ + CFDictionaryRef FF_CFTYPE_AUTO_RELEASE matching1 = CFDictionaryCreate(kCFAllocatorDefault, (const void **)(CFStringRef[]){ CFSTR(kIOHIDDeviceUsagePageKey), CFSTR(kIOHIDDeviceUsageKey) }, (const void **)(CFNumberRef[]){ - genericDesktop, - gamePad + ffCfCreateInt(kHIDPage_GenericDesktop), + ffCfCreateInt(kHIDUsage_GD_Joystick) }, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - IOHIDManagerSetDeviceMatching(manager, matching); + CFDictionaryRef FF_CFTYPE_AUTO_RELEASE matching2 = CFDictionaryCreate(kCFAllocatorDefault, (const void **)(CFStringRef[]){ + CFSTR(kIOHIDDeviceUsagePageKey), + CFSTR(kIOHIDDeviceUsageKey) + }, (const void **)(CFNumberRef[]){ + ffCfCreateInt(kHIDPage_GenericDesktop), + ffCfCreateInt(kHIDUsage_GD_GamePad) + }, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFArrayRef FF_CFTYPE_AUTO_RELEASE matchings = CFArrayCreate(kCFAllocatorDefault, (const void **)(CFTypeRef[]){ + matching1, matching2 + }, 2, &kCFTypeArrayCallBacks); + IOHIDManagerSetDeviceMatchingMultiple(manager, matchings); CFSetRef FF_CFTYPE_AUTO_RELEASE set = IOHIDManagerCopyDevices(manager); if (set) - CFSetApplyFunction(set, &enumSet, devices); - IOHIDManagerClose(manager, 0); + CFSetApplyFunction(set, (CFSetApplierFunction) &enumSet, devices); + IOHIDManagerClose(manager, kIOHIDOptionsTypeNone); return NULL; } diff --git a/src/detection/gamepad/gamepad_bsd.c b/src/detection/gamepad/gamepad_bsd.c index ade8fb996..340ff1165 100644 --- a/src/detection/gamepad/gamepad_bsd.c +++ b/src/detection/gamepad/gamepad_bsd.c @@ -42,8 +42,9 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) if (ioctl(fd, USB_GET_DEVICEINFO, &di) != -1) { FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices); - ffStrbufInitS(&device->identifier, di.udi_serial); + ffStrbufInitS(&device->serial, di.udi_serial); ffStrbufInitF(&device->name, "%s %s", di.udi_vendor, di.udi_product); + device->battery = 0; } } } diff --git a/src/detection/gamepad/gamepad_linux.c b/src/detection/gamepad/gamepad_linux.c index a5273a0fa..791412a70 100644 --- a/src/detection/gamepad/gamepad_linux.c +++ b/src/detection/gamepad/gamepad_linux.c @@ -5,39 +5,88 @@ #include #include +static void detectGamepad(FFlist* devices, FFstrbuf* name, FFstrbuf* path) +{ + uint32_t baseLen = path->length; + FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices); + ffStrbufInit(&device->serial); + ffStrbufInitMove(&device->name, name); + device->battery = 0; + + ffStrbufAppendS(path, "uniq"); + if (ffAppendFileBuffer(path->chars, &device->serial)) + ffStrbufTrimRightSpace(&device->serial); + + ffStrbufSubstrBefore(path, baseLen); + ffStrbufAppendS(path, "device/power_supply/"); // /sys/class/input/jsX/device/device/power_supply + + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(path->chars); + if (dirp) + { + struct dirent* entry; + while ((entry = readdir(dirp)) != NULL) + { + if (entry->d_name[0] == '.' || (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN)) + continue; + ffStrbufAppendS(path, entry->d_name); + ffStrbufAppendS(path, "/capacity"); // /sys/class/input/jsX/device/device/power_supply/XXX/capacity + char capacity[32]; + ssize_t nRead = ffReadFileData(path->chars, sizeof(capacity) - 1, capacity); + if (nRead > 0) // Tested with a PS4 controller + { + capacity[nRead] = '\0'; + device->battery = (uint8_t) strtoul(capacity, NULL, 10); + break; + } + + ffStrbufAppendS(path, "_level"); + nRead = ffReadFileData(path->chars, sizeof(capacity) - 1, capacity); + if (nRead > 0) // Tested with a NS Pro controller + { + // https://github.com/torvalds/linux/blob/52b1853b080a082ec3749c3a9577f6c71b1d4a90/drivers/power/supply/power_supply_sysfs.c#L124 + switch (capacity[0]) + { + case 'C': device->battery = 1; break; // Critical + case 'L': device->battery = 25; break; // Low + case 'N': device->battery = 50; break; // Normal + case 'H': device->battery = 75; break; // High + case 'F': device->battery = 100; break; // Full + } + } + } + } +} + const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) { - DIR* dirp = opendir("/sys/class/input/"); - if(dirp == NULL) + FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/sys/class/input/"); + if (dirp == NULL) return "opendir(\"/sys/class/input/\") == NULL"; FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateS("/sys/class/input/"); uint32_t baseLen = path.length; struct dirent* entry; - while((entry = readdir(dirp)) != NULL) + while ((entry = readdir(dirp)) != NULL) { - if(!ffStrStartsWith(entry->d_name, "js")) + if (!ffStrStartsWith(entry->d_name, "js")) continue; - if(!isdigit(entry->d_name[2])) + if (!isdigit(entry->d_name[2])) continue; ffStrbufAppendS(&path, entry->d_name); ffStrbufAppendS(&path, "/device/name"); - if (ffPathExists(path.chars, FF_PATHTYPE_FILE)) + FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate(); + if (ffAppendFileBuffer(path.chars, &name)) { - FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices); - ffStrbufInitS(&device->identifier, entry->d_name); - ffStrbufInit(&device->name); - if (ffAppendFileBuffer(path.chars, &device->name)) - ffStrbufTrimRightSpace(&device->name); + ffStrbufTrimRightSpace(&name); + ffStrbufSubstrBefore(&path, path.length - 4); + detectGamepad(devices, &name, &path); } ffStrbufSubstrBefore(&path, baseLen); } - closedir(dirp); - return NULL; } diff --git a/src/detection/gamepad/gamepad_windows.c b/src/detection/gamepad/gamepad_windows.c index 8a5799717..ab4540af0 100644 --- a/src/detection/gamepad/gamepad_windows.c +++ b/src/detection/gamepad/gamepad_windows.c @@ -3,10 +3,11 @@ #include "util/mallocHelper.h" #include "util/windows/unicode.h" +#include #include #include -static const char* detectKnownGamepad(uint32_t vendorId, uint32_t productId) +static const char* detectKnownDeviceName(uint32_t vendorId, uint32_t productId) { switch (vendorId) { @@ -30,38 +31,14 @@ static const char* detectKnownGamepad(uint32_t vendorId, uint32_t productId) { switch (productId) { - case 0x0268: return "Sony Playstation 3 Controller"; + case 0x0268: return "Sony DualShock 3 / Six Axis"; case 0x05C4: return "Sony DualShock 4 Gen1"; case 0x09CC: return "Sony DualShock 4 Gen2"; - case 0x0BA0: return "Sony PS4 Controller USB receiver"; + case 0x0BA0: return "Sony DualShock 4 USB receiver"; case 0x0CE6: return "Sony DualSense"; - - default: return NULL; - } - } - - // Microsoft - case 0x045E: - { - switch (productId) - { - case 0x02E0: return "Microsoft X-Box One S pad (Wireless)"; - case 0x02FD: return "Microsoft X-Box One S pad (Wireless, 2016 FW)"; - case 0x0B05: return "Microsoft X-Box One Elite Series 2 pad (Wireless)"; - case 0x0B13: return "Microsoft X-Box Series X (Wireless)"; - - case 0x028E: return "Microsoft XBox 360"; - case 0x028F: return "Microsoft XBox 360 v2"; - case 0x02A1: return "Microsoft XBox 360"; - case 0x0291: return "Microsoft XBox 360 USB receiver"; - case 0x02A0: return "Microsoft XBox 360 Big Button IR"; - case 0x02DD: return "Microsoft XBox One"; - case 0xB326: return "Microsoft XBox One Firmware 2015"; - case 0x02E3: return "Microsoft XBox One Elite"; - case 0x02FF: return "Microsoft XBox One Elite"; - case 0x02EA: return "Microsoft XBox One S"; + case 0x0DF2: return "Sony DualSense Edge"; default: return NULL; } @@ -83,6 +60,7 @@ static const char* detectKnownGamepad(uint32_t vendorId, uint32_t productId) } } + case 0x045E: // Microsoft Xbox compatible controllers should be handled by Windows without problems default: return NULL; } } @@ -98,7 +76,8 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) if ((nDevices = GetRawInputDeviceList(pRawInputDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST))) == (UINT) -1) return "GetRawInputDeviceList(pRawInputDeviceList) failed"; - for (UINT i = 0; i < nDevices; ++i) { + for (UINT i = 0; i < nDevices; ++i) + { if (pRawInputDeviceList[i].dwType != 2) continue; HANDLE hDevice = pRawInputDeviceList[i].hDevice; @@ -117,20 +96,94 @@ const char* ffDetectGamepad(FFlist* devices /* List of FFGamepadDevice */) continue; FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices); - ffStrbufInitWS(&device->identifier, devName); + ffStrbufInit(&device->serial); ffStrbufInit(&device->name); + device->battery = 0; - const char* knownGamepad = detectKnownGamepad(rdi.hid.dwVendorId, rdi.hid.dwProductId); + const char* knownGamepad = detectKnownDeviceName(rdi.hid.dwVendorId, rdi.hid.dwProductId); if (knownGamepad) ffStrbufSetS(&device->name, knownGamepad); - else + HANDLE FF_AUTO_CLOSE_FD hHidFile = CreateFileW(devName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (!hHidFile) + { + if (!knownGamepad) + ffStrbufSetF(&device->name, "Unknown gamepad %04X-%04X", (unsigned) rdi.hid.dwVendorId, (unsigned) rdi.hid.dwProductId); + continue; + } + + if (!knownGamepad) { wchar_t displayName[126]; - HANDLE FF_AUTO_CLOSE_FD hHidFile = CreateFileW(devName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (hHidFile && HidD_GetProductString(hHidFile, displayName, sizeof(wchar_t) * 126)) - ffStrbufSetWS(&device->name, displayName); - else - ffStrbufSetF(&device->name, "Unknown gamepad %4X-%4X", (unsigned) rdi.hid.dwVendorId, (unsigned) rdi.hid.dwProductId); + if (HidD_GetProductString(hHidFile, displayName, sizeof(displayName))) + { + wchar_t manufacturer[126]; + if (HidD_GetManufacturerString(hHidFile, manufacturer, sizeof(manufacturer))) + { + ffStrbufSetWS(&device->name, manufacturer); + FF_STRBUF_AUTO_DESTROY displayNameStr = ffStrbufCreateWS(displayName); + ffStrbufAppendC(&device->name, ' '); + ffStrbufAppend(&device->name, &displayNameStr); + } + else + { + ffStrbufSetWS(&device->name, displayName); + } + } + } + + PHIDP_PREPARSED_DATA preparsedData = NULL; + if (HidD_GetPreparsedData(hHidFile, &preparsedData)) + { + HIDP_CAPS caps; + NTSTATUS capsResult = HidP_GetCaps(preparsedData, &caps); + HidD_FreePreparsedData(preparsedData); + if (!NT_SUCCESS(capsResult)) + continue; + + wchar_t serialNumber[127] = L""; + if (HidD_GetSerialNumberString(hHidFile, serialNumber, sizeof(serialNumber))) + ffStrbufSetWS(&device->serial, serialNumber); + + if ( + (rdi.hid.dwVendorId == 0x054C && ( + rdi.hid.dwProductId == 0x05C4 || // PS4 Gen1 + rdi.hid.dwProductId == 0x09CC // PS4 Gen2 + )) || + (rdi.hid.dwVendorId == 0x057E && ( + rdi.hid.dwProductId == 0x2009 // NS Pro + )) + ) + { + // Controller must be connected by other programs + FF_AUTO_FREE uint8_t* reportBuffer = malloc(caps.InputReportByteLength); + OVERLAPPED overlapped = { }; + DWORD nBytes; + if (ReadFile(hHidFile, reportBuffer, caps.InputReportByteLength, &nBytes, &overlapped) || + (WaitForSingleObject(hHidFile, FF_IO_TERM_RESP_WAIT_MS) == WAIT_OBJECT_0 && GetOverlappedResult(hHidFile, &overlapped, &nBytes, FALSE))) + { + if (rdi.hid.dwVendorId == 0x054C) + { + if (nBytes > 31) + { + uint8_t batteryInfo = reportBuffer[caps.InputReportByteLength == 64 /*USB?*/ ? 30 : 32]; + device->battery = (uint8_t) ((batteryInfo & 0x0f) * 100 / (batteryInfo & 0x10 /*charging?*/ ? 11 /*BATTERY_MAX_USB*/ : 8 /*BATTERY_MAX*/)); + if (device->battery > 100) device->battery = 100; + } + } + else + { + if (nBytes > 3 && reportBuffer[0] == 0x30) + { + uint8_t batteryInfo = reportBuffer[2]; + device->battery = (uint8_t) (((batteryInfo & 0xE0) >> 4) * 100 / 8); + if (device->battery == 0) device->battery = 1; + else if (device->battery > 100) device->battery = 100; + } + } + } + else + CancelIo(hHidFile); + } } } diff --git a/src/detection/gpu/gpu.c b/src/detection/gpu/gpu.c index 435d687aa..66e11c805 100644 --- a/src/detection/gpu/gpu.c +++ b/src/detection/gpu/gpu.c @@ -73,15 +73,17 @@ const char* detectByOpenGL(FFlist* gpus) if (!gpu->vendor.length) { - if (ffStrbufContainS(&result.renderer, "Apple")) + if (ffStrbufContainS(&gpu->name, "Apple")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE); - else if (ffStrbufContainS(&result.renderer, "Intel")) + else if (ffStrbufContainS(&gpu->name, "Intel")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); - else if (ffStrbufContainS(&result.renderer, "AMD") || ffStrbufContainS(&result.renderer, "ATI")) + else if (ffStrbufContainS(&gpu->name, "AMD") || ffStrbufContainS(&gpu->name, "ATI")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); - else if (ffStrbufContainS(&result.renderer, "NVIDIA")) + else if (ffStrbufContainS(&gpu->name, "NVIDIA")) ffStrbufSetStatic(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); } + if (ffStrbufEqualS(&gpu->vendor, FF_GPU_VENDOR_NAME_APPLE)) + gpu->type = FF_GPU_TYPE_INTEGRATED; } ffStrbufDestroy(&result.version); diff --git a/src/detection/gpu/gpu_apple.c b/src/detection/gpu/gpu_apple.c index b7db8c562..6113dfce6 100644 --- a/src/detection/gpu/gpu_apple.c +++ b/src/detection/gpu/gpu_apple.c @@ -38,12 +38,12 @@ static double detectGpuTemp(const FFstrbuf* gpuName) const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) { - io_iterator_t iterator; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOAcceleratorClassName), &iterator) != kIOReturnSuccess) + FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = IO_OBJECT_NULL; + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOAcceleratorClassName), &iterator) != kIOReturnSuccess) return "IOServiceGetMatchingServices() failed"; io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { CFMutableDictionaryRef properties; if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) @@ -106,8 +106,6 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) IOObjectRelease(registryEntry); } - IOObjectRelease(iterator); - ffGpuDetectMetal(gpus); return NULL; } diff --git a/src/detection/gpu/gpu_apple.m b/src/detection/gpu/gpu_apple.m index 1079ff1e0..0323fdaff 100644 --- a/src/detection/gpu/gpu_apple.m +++ b/src/detection/gpu/gpu_apple.m @@ -15,7 +15,7 @@ const char* ffGpuDetectMetal(FFlist* gpus) FFGPUResult* gpu = NULL; FF_LIST_FOR_EACH(FFGPUResult, x, *gpus) { - if (x->deviceId == device.registryID || ffStrbufEqualS(&x->name, device.name.UTF8String)) + if (x->deviceId == device.registryID) { gpu = x; break; @@ -32,9 +32,20 @@ const char* ffGpuDetectMetal(FFlist* gpus) else if ([device supportsFamily:MTLGPUFamilyCommon1]) ffStrbufSetStatic(&gpu->platformApi, "Metal Common 1"); - gpu->type = device.hasUnifiedMemory ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE; + if (gpu->type == device.hasUnifiedMemory) + { + gpu->type = FF_GPU_TYPE_INTEGRATED; + gpu->shared.total = device.recommendedMaxWorkingSetSize; + gpu->shared.used = device.currentAllocatedSize; + } + else + { + gpu->type = FF_GPU_TYPE_DISCRETE; + gpu->dedicated.total = device.recommendedMaxWorkingSetSize; + gpu->dedicated.used = device.currentAllocatedSize; + } } + return NULL; } - - return NULL; + return "Metal API is not supported by this macOS version"; } diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 1a7a19607..340893090 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -1,7 +1,10 @@ #include "detection/gpu/gpu.h" -#include "detection/gpu/gpu_driver_specific.h" #include "detection/vulkan/vulkan.h" +#ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + #include "detection/gpu/gpu_driver_specific.h" +#endif + #ifdef FF_HAVE_LIBPCI #include "common/io/io.h" #include "common/library.h" @@ -245,6 +248,7 @@ static void pciHandleDevice(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpu->temperature = FF_GPU_TEMP_UNSET; gpu->frequency = FF_GPU_FREQUENCY_UNSET; + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) { ffDetectNvidiaGpuInfo(&(FFGpuDriverCondition) { @@ -266,6 +270,7 @@ static void pciHandleDevice(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* if (gpu->dedicated.total != FF_GPU_VMEM_SIZE_UNSET) gpu->type = gpu->dedicated.total > (uint64_t)1024 * 1024 * 1024 ? FF_GPU_TYPE_DISCRETE : FF_GPU_TYPE_INTEGRATED; } + #endif // FF_USE_PROPRIETARY_GPU_DRIVER_API #ifdef __linux__ if(options->temp && gpu->temperature != gpu->temperature) diff --git a/src/detection/gpu/gpu_nvidia.c b/src/detection/gpu/gpu_nvidia.c index 717d6389b..b55cd3258 100644 --- a/src/detection/gpu/gpu_nvidia.c +++ b/src/detection/gpu/gpu_nvidia.c @@ -12,6 +12,7 @@ struct FFNvmlData { FF_LIBRARY_SYMBOL(nvmlDeviceGetMemoryInfo_v2) FF_LIBRARY_SYMBOL(nvmlDeviceGetNumGpuCores) FF_LIBRARY_SYMBOL(nvmlDeviceGetClockInfo) + FF_LIBRARY_SYMBOL(nvmlDeviceGetBrand) bool inited; } nvmlData; @@ -32,6 +33,7 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetMemoryInfo_v2) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetNumGpuCores) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetClockInfo) + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libnvml, nvmlData, nvmlDeviceGetBrand) if (ffnvmlInit_v2() != NVML_SUCCESS) { @@ -79,6 +81,24 @@ const char* ffDetectNvidiaGpuInfo(const FFGpuDriverCondition* cond, FFGpuDriverR if (!device) return "Device not found"; } + nvmlBrandType_t brand; + if (nvmlData.ffnvmlDeviceGetBrand(device, &brand) == NVML_SUCCESS) + { + switch (brand) + { + case NVML_BRAND_NVIDIA_RTX: + case NVML_BRAND_QUADRO_RTX: + case NVML_BRAND_GEFORCE: + case NVML_BRAND_TITAN: + case NVML_BRAND_TESLA: + case NVML_BRAND_QUADRO: + *result.type = FF_GPU_TYPE_DISCRETE; + break; + default: + break; + } + } + if (result.temp) { uint32_t value; diff --git a/src/detection/gpu/gpu_windows.c b/src/detection/gpu/gpu_windows.c index 719d98a50..3096e0a52 100644 --- a/src/detection/gpu/gpu_windows.c +++ b/src/detection/gpu/gpu_windows.c @@ -1,8 +1,11 @@ #include "gpu.h" -#include "detection/gpu/gpu_driver_specific.h" #include "util/windows/unicode.h" #include "util/windows/registry.h" +#ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API + #include "detection/gpu/gpu_driver_specific.h" +#endif + #include static int isGpuNameEqual(const FFGPUResult* gpu, const FFstrbuf* name) @@ -10,6 +13,7 @@ static int isGpuNameEqual(const FFGPUResult* gpu, const FFstrbuf* name) return ffStrbufEqual(&gpu->name, name); } +#ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API static inline bool getDriverSpecificDetectionFn(const char* vendor, __typeof__(&ffDetectNvidiaGpuInfo)* pDetectFn, const char** pDllName) { if (vendor == FF_GPU_VENDOR_NAME_NVIDIA) @@ -44,6 +48,7 @@ static inline bool getDriverSpecificDetectionFn(const char* vendor, __typeof__(& return true; } +#endif // FF_USE_PROPRIETARY_GPU_DRIVER_API const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus) { @@ -139,6 +144,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* } } +#ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API __typeof__(&ffDetectNvidiaGpuInfo) detectFn; const char* dllName; @@ -168,6 +174,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* ); } } +#endif // FF_USE_PROPRIETARY_GPU_DRIVER_API } return NULL; diff --git a/src/detection/gpu/gpu_wsl.cpp b/src/detection/gpu/gpu_wsl.cpp index a847a53ec..91d67e54a 100644 --- a/src/detection/gpu/gpu_wsl.cpp +++ b/src/detection/gpu/gpu_wsl.cpp @@ -98,6 +98,7 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF const char* vendorStr = ffGetGPUVendorString((unsigned) hardwareId.vendorID); ffStrbufSetStatic(&gpu->vendor, vendorStr); + #ifdef FF_USE_PROPRIETARY_GPU_DRIVER_API if (vendorStr == FF_GPU_VENDOR_NAME_NVIDIA && (options->driverSpecific || options->temp)) { FFGpuDriverCondition cond = { @@ -117,6 +118,7 @@ const char* ffGPUDetectByDirectX(FF_MAYBE_UNUSED const FFGPUOptions* options, FF .frequency = &gpu->frequency, }, "/usr/lib/wsl/lib/libnvidia-ml.so"); } + #endif } } diff --git a/src/detection/host/host.h b/src/detection/host/host.h index 1957e9c6b..1b0c61187 100644 --- a/src/detection/host/host.h +++ b/src/detection/host/host.h @@ -4,11 +4,13 @@ typedef struct FFHostResult { - FFstrbuf productFamily; - FFstrbuf productName; - FFstrbuf productVersion; - FFstrbuf productSku; - FFstrbuf sysVendor; + FFstrbuf family; + FFstrbuf name; + FFstrbuf version; + FFstrbuf sku; + FFstrbuf serial; + FFstrbuf uuid; + FFstrbuf vendor; } FFHostResult; const char* ffDetectHost(FFHostResult* host); diff --git a/src/detection/host/host_android.c b/src/detection/host/host_android.c index 4b7c095a9..b496029a8 100644 --- a/src/detection/host/host_android.c +++ b/src/detection/host/host_android.c @@ -5,32 +5,32 @@ const char* ffDetectHost(FFHostResult* host) { // http://newandroidbook.com/ddb/ - ffSettingsGetAndroidProperty("ro.product.device", &host->productFamily); + ffSettingsGetAndroidProperty("ro.product.device", &host->family); - ffSettingsGetAndroidProperty("ro.product.marketname", &host->productName) - || ffSettingsGetAndroidProperty("ro.vendor.product.display", &host->productName) - || ffSettingsGetAndroidProperty("ro.config.devicename", &host->productName) - || ffSettingsGetAndroidProperty("ro.config.marketing_name", &host->productName) - || ffSettingsGetAndroidProperty("ro.product.vendor.model", &host->productName) - || ffSettingsGetAndroidProperty("ro.product.oppo_model", &host->productName) - || ffSettingsGetAndroidProperty("ro.oppo.market.name", &host->productName) - || ffSettingsGetAndroidProperty("ro.product.brand", &host->productName); + ffSettingsGetAndroidProperty("ro.product.marketname", &host->name) + || ffSettingsGetAndroidProperty("ro.vendor.product.display", &host->name) + || ffSettingsGetAndroidProperty("ro.config.devicename", &host->name) + || ffSettingsGetAndroidProperty("ro.config.marketing_name", &host->name) + || ffSettingsGetAndroidProperty("ro.product.vendor.model", &host->name) + || ffSettingsGetAndroidProperty("ro.product.oppo_model", &host->name) + || ffSettingsGetAndroidProperty("ro.oppo.market.name", &host->name) + || ffSettingsGetAndroidProperty("ro.product.brand", &host->name); - if (ffSettingsGetAndroidProperty("ro.product.model", &host->productVersion)) + if (ffSettingsGetAndroidProperty("ro.product.model", &host->version)) { - if (ffStrbufStartsWithIgnCase(&host->productVersion, &host->productName)) + if (ffStrbufStartsWithIgnCase(&host->version, &host->name)) { - ffStrbufSubstrAfter(&host->productVersion, host->productName.length); - ffStrbufTrimLeft(&host->productVersion, ' '); + ffStrbufSubstrAfter(&host->version, host->name.length); + ffStrbufTrimLeft(&host->version, ' '); } } - ffSettingsGetAndroidProperty("ro.product.manufacturer", &host->sysVendor); + ffSettingsGetAndroidProperty("ro.product.manufacturer", &host->vendor); - if(host->sysVendor.length && !ffStrbufStartsWithIgnCase(&host->productName, &host->sysVendor)) + if(host->vendor.length && !ffStrbufStartsWithIgnCase(&host->name, &host->vendor)) { - ffStrbufPrependS(&host->productName, " "); - ffStrbufPrepend(&host->productName, &host->sysVendor); + ffStrbufPrependS(&host->name, " "); + ffStrbufPrepend(&host->name, &host->vendor); } return NULL; diff --git a/src/detection/host/host_apple.c b/src/detection/host/host_apple.c index 6f5184e50..402c2f07e 100644 --- a/src/detection/host/host_apple.c +++ b/src/detection/host/host_apple.c @@ -169,40 +169,48 @@ static const char* getProductNameWithHwModel(const FFstrbuf* hwModel) const char* getProductNameWithIokit(FFstrbuf* result) { - io_iterator_t iterator; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceNameMatching("product"), &iterator) != kIOReturnSuccess) - return "IOServiceGetMatchingServices() failed"; + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceNameMatching("product")); + if (!registryEntry) + return "IOServiceGetMatchingService() failed"; - io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) - { - FF_CFTYPE_AUTO_RELEASE CFMutableDictionaryRef properties = NULL; - if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) - { - IOObjectRelease(registryEntry); - continue; - } + FF_CFTYPE_AUTO_RELEASE CFStringRef productName = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("product-name"), kCFAllocatorDefault, kNilOptions); + if (!productName) + return "IORegistryEntryCreateCFProperty() failed"; - if (ffCfDictGetString(properties, CFSTR("product-name"), result)) - break; - } + return ffCfStrGetString(productName, result); +} + +const char* getOthersByIokit(FFHostResult* host) +{ + FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("IOPlatformExpertDevice")); + if (!registryEntry) + return "IOServiceGetMatchingService() failed"; + + FF_CFTYPE_AUTO_RELEASE CFStringRef serialNumber = IORegistryEntryCreateCFProperty(registryEntry, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, kNilOptions); + if (serialNumber) + ffCfStrGetString(serialNumber, &host->serial); + + FF_CFTYPE_AUTO_RELEASE CFStringRef uuid = IORegistryEntryCreateCFProperty(registryEntry, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, kNilOptions); + if (uuid) + ffCfStrGetString(uuid, &host->uuid); + + FF_CFTYPE_AUTO_RELEASE CFStringRef manufacturer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("manufacturer"), kCFAllocatorDefault, kNilOptions); + if (manufacturer) + ffCfStrGetString(manufacturer, &host->vendor); - IOObjectRelease(registryEntry); return NULL; } const char* ffDetectHost(FFHostResult* host) { - ffStrbufSetStatic(&host->sysVendor, "Apple"); - - const char* error = ffSysctlGetString("hw.model", &host->productFamily); + const char* error = ffSysctlGetString("hw.model", &host->family); if (error) return error; - ffStrbufSetStatic(&host->productName, getProductNameWithHwModel(&host->productFamily)); - if (host->productName.length == 0) - getProductNameWithIokit(&host->productName); - if (host->productName.length == 0) - ffStrbufSet(&host->productName, &host->productFamily); - + ffStrbufSetStatic(&host->name, getProductNameWithHwModel(&host->family)); + if (host->name.length == 0) + getProductNameWithIokit(&host->name); + if (host->name.length == 0) + ffStrbufSet(&host->name, &host->family); + getOthersByIokit(host); return NULL; } diff --git a/src/detection/host/host_bsd.c b/src/detection/host/host_bsd.c index 9a7dcbd71..334b04cab 100644 --- a/src/detection/host/host_bsd.c +++ b/src/detection/host/host_bsd.c @@ -4,16 +4,20 @@ const char* ffDetectHost(FFHostResult* host) { - ffSettingsGetFreeBSDKenv("smbios.system.product", &host->productName); - ffCleanUpSmbiosValue(&host->productName); - ffSettingsGetFreeBSDKenv("smbios.system.family", &host->productFamily); - ffCleanUpSmbiosValue(&host->productFamily); - ffSettingsGetFreeBSDKenv("smbios.system.version", &host->productVersion); - ffCleanUpSmbiosValue(&host->productVersion); - ffSettingsGetFreeBSDKenv("smbios.system.sku", &host->productSku); - ffCleanUpSmbiosValue(&host->productSku); - ffSettingsGetFreeBSDKenv("smbios.system.maker", &host->sysVendor); - ffCleanUpSmbiosValue(&host->sysVendor); + ffSettingsGetFreeBSDKenv("smbios.system.product", &host->name); + ffCleanUpSmbiosValue(&host->name); + ffSettingsGetFreeBSDKenv("smbios.system.family", &host->family); + ffCleanUpSmbiosValue(&host->family); + ffSettingsGetFreeBSDKenv("smbios.system.version", &host->version); + ffCleanUpSmbiosValue(&host->version); + ffSettingsGetFreeBSDKenv("smbios.system.sku", &host->sku); + ffCleanUpSmbiosValue(&host->sku); + ffSettingsGetFreeBSDKenv("smbios.system.serial", &host->serial); + ffCleanUpSmbiosValue(&host->serial); + ffSettingsGetFreeBSDKenv("smbios.system.uuid", &host->uuid); + ffCleanUpSmbiosValue(&host->uuid); + ffSettingsGetFreeBSDKenv("smbios.system.maker", &host->vendor); + ffCleanUpSmbiosValue(&host->vendor); return NULL; } diff --git a/src/detection/host/host_linux.c b/src/detection/host/host_linux.c index 8b11628d6..92b1c49f9 100644 --- a/src/detection/host/host_linux.c +++ b/src/detection/host/host_linux.c @@ -7,48 +7,76 @@ static void getHostProductName(FFstrbuf* name) { - ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", name); - if(name->length > 0) - return; + if (ffReadFileBuffer("/sys/firmware/devicetree/base/model", name)) + { + ffStrbufTrimRightSpace(name); + ffStrbufTrimRight(name, '\0'); + if(ffIsSmbiosValueSet(name)) + return; + } - ffReadFileBuffer("/sys/firmware/devicetree/base/model", name); - if(ffIsSmbiosValueSet(name)) - return; + if (ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name)) + { + ffStrbufTrimRightSpace(name); + ffStrbufTrimRight(name, '\0'); + if(ffIsSmbiosValueSet(name)) + return; + } - ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name); - if(ffIsSmbiosValueSet(name)) - return; - - //does a clear before the read - ffReadFileBuffer("/tmp/sysinfo/model", name); - if(ffIsSmbiosValueSet(name)) - return; + if (ffReadFileBuffer("/tmp/sysinfo/model", name)) + { + ffStrbufTrimRightSpace(name); + ffStrbufTrimRight(name, '\0'); + if(ffIsSmbiosValueSet(name)) + return; + } ffStrbufClear(name); } +static void getHostSerialNumber(FFstrbuf* serial) +{ + if (ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial)) + { + ffStrbufTrimRightSpace(serial); + ffStrbufTrimRight(serial, '\0'); + if(ffIsSmbiosValueSet(serial)) + return; + } + + ffStrbufClear(serial); +} + const char* ffDetectHost(FFHostResult* host) { - ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->productFamily); - getHostProductName(&host->productName); - ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->productVersion); - ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->productSku); - ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->sysVendor); + ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family); + if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &host->name)) + getHostProductName(&host->name); + ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->version); + ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->sku); + if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_serial", "/sys/class/dmi/id/product_serial", &host->serial)) + getHostSerialNumber(&host->serial); + ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_uuid", "/sys/class/dmi/id/product_uuid", &host->uuid); + if (!ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->vendor)) + { + if (ffStrbufStartsWithS(&host->name, "Apple ")) + ffStrbufSetStatic(&host->vendor, "Apple Inc."); + } //KVM/Qemu virtual machine - if(ffStrbufStartsWithS(&host->productName, "Standard PC")) - ffStrbufPrependS(&host->productName, "KVM/QEMU "); + if(ffStrbufStartsWithS(&host->name, "Standard PC")) + ffStrbufPrependS(&host->name, "KVM/QEMU "); - if(host->productFamily.length == 0 && host->productName.length == 0) + if(host->family.length == 0 && host->name.length == 0) { const char* wslDistroName = getenv("WSL_DISTRO_NAME"); //On WSL, the real host can't be detected. Instead use WSL as host. if(wslDistroName != NULL || getenv("WSL_DISTRO") != NULL || getenv("WSL_INTEROP") != NULL) { - ffStrbufAppendS(&host->productName, "Windows Subsystem for Linux"); + ffStrbufAppendS(&host->name, "Windows Subsystem for Linux"); if (wslDistroName) - ffStrbufAppendF(&host->productName, " - %s", wslDistroName); - ffStrbufAppendS(&host->productFamily, "WSL"); + ffStrbufAppendF(&host->name, " - %s", wslDistroName); + ffStrbufAppendS(&host->family, "WSL"); FF_STRBUF_AUTO_DESTROY wslVer = ffStrbufCreate(); //Wide characters if(!ffProcessAppendStdOut(&wslVer, (char* const[]){ @@ -61,7 +89,7 @@ const char* ffDetectHost(FFHostResult* host) ffStrbufSubstrAfterLastC(&wslVer, ' '); for(uint32_t i = 0; i < wslVer.length; ++i) { if(wslVer.chars[i]) //don't append \0 - ffStrbufAppendC(&host->productVersion, wslVer.chars[i]); + ffStrbufAppendC(&host->version, wslVer.chars[i]); } } } diff --git a/src/detection/host/host_windows.c b/src/detection/host/host_windows.c index afc2b242c..8efe70a2f 100644 --- a/src/detection/host/host_windows.c +++ b/src/detection/host/host_windows.c @@ -1,24 +1,69 @@ #include "host.h" -#include "util/windows/registry.h" #include "util/smbiosHelper.h" +typedef struct FFSmbiosSystemInfo +{ + FFSmbiosHeader Header; + + uint8_t Manufacturer; // string + uint8_t ProductName; // string + uint8_t Version; // string + uint8_t SerialNumber; // string + + // 2.1+ + struct { + uint32_t TimeLow; + uint16_t TimeMid; + uint16_t TimeHighAndVersion; + uint8_t ClockSeqHiAndReserved; + uint8_t ClockSeqLow; + uint8_t Node[6]; + } UUID; // varies + uint8_t WakeUpType; // enum + + // 2.4+ + uint8_t SKUNumber; // string + uint8_t Family; // string +} FFSmbiosSystemInfo; + const char* ffDetectHost(FFHostResult* host) { - FF_HKEY_AUTO_DESTROY hKey = NULL; + const FFSmbiosSystemInfo* data = (const FFSmbiosSystemInfo*) (*ffGetSmbiosHeaderTable())[FF_SMBIOS_TYPE_SYSTEM_INFO]; + if (!data) + return "System information is not found in SMBIOS data"; - if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, NULL)) - return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\BIOS\", &hKey, NULL) failed"; + const char* strings = (const char*) data + data->Header.Length; - ffRegReadStrbuf(hKey, L"SystemProductName", &host->productName, NULL); - ffCleanUpSmbiosValue(&host->productName); - ffRegReadStrbuf(hKey, L"SystemFamily", &host->productFamily, NULL); - ffCleanUpSmbiosValue(&host->productFamily); - ffRegReadStrbuf(hKey, L"SystemVersion", &host->productVersion, NULL); - ffCleanUpSmbiosValue(&host->productVersion); - ffRegReadStrbuf(hKey, L"SystemSKU", &host->productSku, NULL); - ffCleanUpSmbiosValue(&host->productSku); - ffRegReadStrbuf(hKey, L"SystemManufacturer", &host->sysVendor, NULL); - ffCleanUpSmbiosValue(&host->sysVendor); + ffStrbufSetStatic(&host->vendor, ffSmbiosLocateString(strings, data->Manufacturer)); + ffCleanUpSmbiosValue(&host->vendor); + ffStrbufSetStatic(&host->name, ffSmbiosLocateString(strings, data->ProductName)); + ffCleanUpSmbiosValue(&host->name); + ffStrbufSetStatic(&host->version, ffSmbiosLocateString(strings, data->Version)); + ffCleanUpSmbiosValue(&host->version); + ffStrbufSetStatic(&host->serial, ffSmbiosLocateString(strings, data->SerialNumber)); + ffCleanUpSmbiosValue(&host->serial); + + static_assert(offsetof(FFSmbiosSystemInfo, UUID) == 0x08, "FFSmbiosSystemInfo.UUID offset is wrong"); + if (data->Header.Length > offsetof(FFSmbiosSystemInfo, UUID)) + { + ffStrbufSetF(&host->uuid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + data->UUID.TimeLow, data->UUID.TimeMid, data->UUID.TimeHighAndVersion, + data->UUID.ClockSeqHiAndReserved, data->UUID.ClockSeqLow, + data->UUID.Node[0], data->UUID.Node[1], data->UUID.Node[2], data->UUID.Node[3], data->UUID.Node[4], data->UUID.Node[5]); + } + + static_assert(offsetof(FFSmbiosSystemInfo, SKUNumber) == 0x19, "FFSmbiosSystemInfo.SKUNumber offset is wrong"); + if (data->Header.Length > offsetof(FFSmbiosSystemInfo, SKUNumber)) + { + ffStrbufSetStatic(&host->sku, ffSmbiosLocateString(strings, data->SKUNumber)); + ffCleanUpSmbiosValue(&host->sku); + } + + if (data->Header.Length > offsetof(FFSmbiosSystemInfo, Family)) + { + ffStrbufSetStatic(&host->family, ffSmbiosLocateString(strings, data->Family)); + ffCleanUpSmbiosValue(&host->family); + } return NULL; } diff --git a/src/detection/locale/locale.c b/src/detection/locale/locale_linux.c similarity index 100% rename from src/detection/locale/locale.c rename to src/detection/locale/locale_linux.c index e2f27a214..99edccec0 100644 --- a/src/detection/locale/locale.c +++ b/src/detection/locale/locale_linux.c @@ -21,14 +21,14 @@ static void getLocaleFromEnv(FFstrbuf* locale) static void getLocaleFromStdFn(FFstrbuf* locale) { - ffStrbufAppendS(locale, setlocale(LC_ALL, NULL)); - #ifdef LC_MESSAGES + ffStrbufAppendS(locale, setlocale(LC_MESSAGES, NULL)); + if(locale->length > 0) return; - - ffStrbufAppendS(locale, setlocale(LC_MESSAGES, NULL)); #endif + + ffStrbufAppendS(locale, setlocale(LC_ALL, NULL)); } void ffDetectLocale(FFstrbuf* result) diff --git a/src/detection/locale/locale_windows.c b/src/detection/locale/locale_windows.c new file mode 100644 index 000000000..128913129 --- /dev/null +++ b/src/detection/locale/locale_windows.c @@ -0,0 +1,18 @@ +#include "detection/locale/locale.h" +#include "util/windows/unicode.h" + +#include +#include + +void ffDetectLocale(FFstrbuf* result) +{ + wchar_t name[LOCALE_NAME_MAX_LENGTH]; + int size = GetUserDefaultLocaleName(name, LOCALE_NAME_MAX_LENGTH); + if (size > 1) // including '\0' + { + ffStrbufSetNWS(result, (uint32_t)size - 1, name); + ffStrbufAppendS(result, ".UTF-8"); // set explicitly in `ffInitInstance` + } + else + ffStrbufSetS(result, setlocale(LC_ALL, NULL)); +} diff --git a/src/detection/media/media_linux.c b/src/detection/media/media_linux.c index 35830f1b9..5dd673338 100644 --- a/src/detection/media/media_linux.c +++ b/src/detection/media/media_linux.c @@ -1,6 +1,5 @@ #include "fastfetch.h" #include "detection/media/media.h" -#include "common/thread.h" #include "util/stringUtils.h" #include @@ -11,7 +10,6 @@ #ifdef FF_HAVE_DBUS #include "common/dbus.h" #include "common/library.h" -#include "common/parsing.h" static bool getBusProperties(FFDBusData* data, const char* busName, FFMediaResult* result) { diff --git a/src/detection/monitor/monitor.h b/src/detection/monitor/monitor.h index 8314c4cf4..a6150e5d3 100644 --- a/src/detection/monitor/monitor.h +++ b/src/detection/monitor/monitor.h @@ -8,6 +8,9 @@ typedef struct FFMonitorResult uint32_t physicalWidth; // in mm uint32_t physicalHeight; // in mm bool hdrCompatible; + uint16_t manufactureYear; + uint16_t manufactureWeek; + uint32_t serial; } FFMonitorResult; const char* ffDetectMonitor(FFlist* results); diff --git a/src/detection/monitor/monitor_apple.m b/src/detection/monitor/monitor_apple.m index be3b51a46..348a1ad33 100644 --- a/src/detection/monitor/monitor_apple.m +++ b/src/detection/monitor/monitor_apple.m @@ -40,9 +40,13 @@ const char* ffDetectMonitor(FFlist* results) CFDictionaryRef FF_CFTYPE_AUTO_RELEASE displayInfo = CoreDisplay_DisplayCreateInfoDictionary((CGDirectDisplayID) display->id); if(!displayInfo) continue; + bool isVirtual = false; + if (ffCfDictGetBool(displayInfo, CFSTR("kCGDisplayIsVirtualDevice"), &isVirtual) == NULL && isVirtual) + continue; + uint8_t edidData[128] = {}; uint32_t edidLength = 0; - if (false || !ffCfDictGetData(displayInfo, CFSTR("IODisplayEDID"), 0, sizeof(edidData), edidData, &edidLength)) + if (ffCfDictGetData(displayInfo, CFSTR("IODisplayEDID"), 0, sizeof(edidData), edidData, &edidLength) == NULL) { uint32_t width, height; ffEdidGetPhysicalResolution(edidData, &width, &height); @@ -75,6 +79,14 @@ const char* ffDetectMonitor(FFlist* results) monitor->physicalHeight = (uint32_t) (size.height + 0.5); monitor->hdrCompatible = CFDictionaryContainsKey(displayInfo, CFSTR("ReferencePeakHDRLuminance")) || detectHdrSupportWithNSScreen(display); + + int64_t serial, year, week; + if (ffCfDictGetInt64(displayInfo, CFSTR("DisplaySerialNumber"), &serial) == NULL) + monitor->serial = (uint32_t) (uint64_t) serial; + if (ffCfDictGetInt64(displayInfo, CFSTR("DisplayYearManufacture"), &year) == NULL) + monitor->manufactureYear = (uint16_t) year; + if (ffCfDictGetInt64(displayInfo, CFSTR("DisplayWeekManufacture"), &week) == NULL) + monitor->manufactureWeek = (uint16_t) week; } return NULL; diff --git a/src/detection/monitor/monitor_linux.c b/src/detection/monitor/monitor_linux.c index 507efdfa2..8d0f5ec11 100644 --- a/src/detection/monitor/monitor_linux.c +++ b/src/detection/monitor/monitor_linux.c @@ -29,10 +29,10 @@ const char* ffDetectMonitor(FFlist* results) ffStrbufAppendS(&drmDir, entry->d_name); uint32_t drmDirWithDnameLength = drmDir.length; - ffStrbufAppendS(&drmDir, "/enabled"); - char enabled = 'd'; // disabled - ffReadFileData(drmDir.chars, sizeof(enabled), &enabled); - if (enabled != 'e') // enabled + ffStrbufAppendS(&drmDir, "/status"); + char status = 'd'; // disconnected + ffReadFileData(drmDir.chars, sizeof(status), &status); + if (status != 'c') // connected { ffStrbufSubstrBefore(&drmDir, drmDirLength); continue; @@ -59,6 +59,7 @@ const char* ffDetectMonitor(FFlist* results) ffStrbufInit(&display->name); ffEdidGetName(edidData, &display->name); ffEdidGetPhysicalSize(edidData, &display->physicalWidth, &display->physicalHeight); + ffEdidGetSerialAndManufactureDate(edidData, &display->serial, &display->manufactureYear, &display->manufactureWeek); display->hdrCompatible = ffEdidGetHdrCompatible(edidData, (uint32_t) edidLength); } diff --git a/src/detection/monitor/monitor_windows.c b/src/detection/monitor/monitor_windows.c index f56c5a188..b695cf67a 100644 --- a/src/detection/monitor/monitor_windows.c +++ b/src/detection/monitor/monitor_windows.c @@ -44,6 +44,7 @@ const char* ffDetectMonitor(FFlist* results) FFMonitorResult* display = (FFMonitorResult*) ffListAdd(results); display->width = width; display->height = height; + ffEdidGetSerialAndManufactureDate(edidData, &display->serial, &display->manufactureYear, &display->manufactureWeek); display->hdrCompatible = ffEdidGetHdrCompatible(edidData, edidLength); // Doesn't work. edidLength is always 128 ffStrbufInit(&display->name); ffEdidGetName(edidData, &display->name); diff --git a/src/detection/os/os_apple.m b/src/detection/os/os_apple.m index 70687811e..dbb55539e 100644 --- a/src/detection/os/os_apple.m +++ b/src/detection/os/os_apple.m @@ -1,6 +1,8 @@ #include "os.h" +#include "common/io/io.h" #include "common/sysctl.h" #include "util/stringUtils.h" +#include "util/mallocHelper.h" #include #include @@ -72,11 +74,11 @@ static bool detectOSCodeName(FFOSResult* os) static void parseOSXSoftwareLicense(FFOSResult* os) { - FILE* rtf = fopen("/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf", "r"); + FF_AUTO_CLOSE_FILE FILE* rtf = fopen("/System/Library/CoreServices/Setup Assistant.app/Contents/Resources/en.lproj/OSXSoftwareLicense.rtf", "r"); if(rtf == NULL) return; - char* line = NULL; + FF_AUTO_FREE char* line = NULL; size_t len = 0; const char* searchStr = "\\f0\\b SOFTWARE LICENSE AGREEMENT FOR macOS "; while(getline(&line, &len, rtf) != EOF) @@ -89,11 +91,6 @@ static void parseOSXSoftwareLicense(FFOSResult* os) break; } } - - if(line != NULL) - free(line); - - fclose(rtf); } void ffDetectOSImpl(FFOSResult* os) diff --git a/src/detection/physicaldisk/physicaldisk_apple.c b/src/detection/physicaldisk/physicaldisk_apple.c index 4fbc84951..8f4a11420 100644 --- a/src/detection/physicaldisk/physicaldisk_apple.c +++ b/src/detection/physicaldisk/physicaldisk_apple.c @@ -9,14 +9,6 @@ #include #include -static inline void wrapIoObjectRelease(io_service_t* service) -{ - assert(service); - if (*service) - IOObjectRelease(*service); -} -#define FF_IOOBJECT_AUTO_RELEASE __attribute__((__cleanup__(wrapIoObjectRelease))) - static inline void wrapIoDestroyPlugInInterface(IOCFPlugInInterface*** pluginInf) { assert(pluginInf); @@ -49,11 +41,11 @@ static const char* detectSsdTemp(io_service_t entryPhysical, double* temp) const char* ffDetectPhysicalDisk(FFlist* result, FFPhysicalDiskOptions* options) { FF_IOOBJECT_AUTO_RELEASE io_iterator_t iterator = 0; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOMediaClass), &iterator) != KERN_SUCCESS) + if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching(kIOMediaClass), &iterator) != KERN_SUCCESS) return "IOServiceGetMatchingServices() failed"; io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + while ((registryEntry = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t entryPartition = registryEntry; diff --git a/src/detection/poweradapter/poweradapter.h b/src/detection/poweradapter/poweradapter.h index 21c93b64a..8af98a6fa 100644 --- a/src/detection/poweradapter/poweradapter.h +++ b/src/detection/poweradapter/poweradapter.h @@ -8,10 +8,10 @@ typedef struct FFPowerAdapterResult FFstrbuf name; FFstrbuf modelName; FFstrbuf manufacturer; + FFstrbuf serial; int watts; } FFPowerAdapterResult; -const char* ffDetectPowerAdapterImpl(FFlist* results); +const char* ffDetectPowerAdapter(FFlist* results); -#define FF_POWERADAPTER_UNSET -2 #define FF_POWERADAPTER_NOT_CONNECTED -1 diff --git a/src/detection/poweradapter/poweradapter_apple.c b/src/detection/poweradapter/poweradapter_apple.c index 435532ee4..794fccd45 100644 --- a/src/detection/poweradapter/poweradapter_apple.c +++ b/src/detection/poweradapter/poweradapter_apple.c @@ -2,51 +2,30 @@ #include "poweradapter.h" #include "util/apple/cf_helpers.h" -#include +#include +#include -const char* ffDetectPowerAdapterImpl(FFlist* results) +const char* ffDetectPowerAdapter(FFlist* results) { - io_iterator_t iterator; - if(IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("AppleSmartBattery"), &iterator) != kIOReturnSuccess) - return "IOServiceGetMatchingServices() failed"; + FF_CFTYPE_AUTO_RELEASE CFDictionaryRef details = IOPSCopyExternalPowerAdapterDetails(); + FFPowerAdapterResult* adapter = ffListAdd(results); - io_registry_entry_t registryEntry; - while((registryEntry = IOIteratorNext(iterator)) != 0) + ffStrbufInit(&adapter->name); + ffStrbufInit(&adapter->description); + ffStrbufInit(&adapter->manufacturer); + ffStrbufInit(&adapter->modelName); + ffStrbufInit(&adapter->serial); + adapter->watts = FF_POWERADAPTER_NOT_CONNECTED; + + if (details) { - CFMutableDictionaryRef properties; - if(IORegistryEntryCreateCFProperties(registryEntry, &properties, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) - { - IOObjectRelease(registryEntry); - continue; - } - - FFPowerAdapterResult* adapter = ffListAdd(results); - - ffStrbufInit(&adapter->name); - ffStrbufInit(&adapter->description); - ffStrbufInit(&adapter->manufacturer); - ffStrbufInit(&adapter->modelName); - adapter->watts = FF_POWERADAPTER_UNSET; - - CFDictionaryRef adapterDict; - if(!ffCfDictGetDict(properties, CFSTR("AdapterDetails"), &adapterDict)) - { - if (ffCfDictGetInt(adapterDict, CFSTR("Watts"), &adapter->watts)) - { - adapter->watts = FF_POWERADAPTER_NOT_CONNECTED; - continue; - } - ffCfDictGetString(adapterDict, CFSTR("Name"), &adapter->name); - ffCfDictGetString(adapterDict, CFSTR("Description"), &adapter->description); - ffCfDictGetString(adapterDict, CFSTR("Manufacturer"), &adapter->manufacturer); - ffCfDictGetString(adapterDict, CFSTR("Model"), &adapter->modelName); - } - - CFRelease(properties); - IOObjectRelease(registryEntry); + ffCfDictGetString(details, CFSTR(kIOPSNameKey), &adapter->name); + ffCfDictGetString(details, CFSTR("Model"), &adapter->modelName); + ffCfDictGetString(details, CFSTR("Manufacturer"), &adapter->manufacturer); + ffCfDictGetString(details, CFSTR("Description"), &adapter->description); + ffCfDictGetString(details, CFSTR("SerialString"), &adapter->serial); + ffCfDictGetInt(details, CFSTR(kIOPSPowerAdapterWattsKey), &adapter->watts); } - IOObjectRelease(iterator); - return NULL; } diff --git a/src/detection/poweradapter/poweradapter_linux.c b/src/detection/poweradapter/poweradapter_linux.c new file mode 100644 index 000000000..8058ad7c6 --- /dev/null +++ b/src/detection/poweradapter/poweradapter_linux.c @@ -0,0 +1,102 @@ +#include "poweradapter.h" +#include "common/io/io.h" +#include "util/stringUtils.h" + +#include + +static void parsePowerAdapter(FFstrbuf* dir, FF_MAYBE_UNUSED const char* id, FFlist* results) +{ + uint32_t dirLength = dir->length; + + FF_STRBUF_AUTO_DESTROY tmpBuffer = ffStrbufCreate(); + + //type must exist and be "Mains" + ffStrbufAppendS(dir, "/type"); + if (ffReadFileBuffer(dir->chars, &tmpBuffer)) + ffStrbufTrimRightSpace(&tmpBuffer); + ffStrbufSubstrBefore(dir, dirLength); + + if(!ffStrbufIgnCaseEqualS(&tmpBuffer, "Mains")) + return; + + //scope may not exist or must not be "Device" (?) + ffStrbufAppendS(dir, "/scope"); + if (ffReadFileBuffer(dir->chars, &tmpBuffer)) + ffStrbufTrimRightSpace(&tmpBuffer); + ffStrbufSubstrBefore(dir, dirLength); + + if(ffStrbufIgnCaseEqualS(&tmpBuffer, "Device")) + return; + + //input_power_limit must exist and be not empty + ffStrbufAppendS(dir, "/input_power_limit"); + bool available = ffReadFileBuffer(dir->chars, &tmpBuffer); + ffStrbufSubstrBefore(dir, dirLength); + + if (!available) + return; + + FFPowerAdapterResult* result = ffListAdd(results); + ffStrbufInit(&result->name); + ffStrbufInit(&result->description); + + ffStrbufAppendS(dir, "/online"); + char online = '1'; + ffReadFileData(dir->chars, sizeof(online), &online); + ffStrbufSubstrBefore(dir, dirLength); + + if (online == '0') + result->watts = FF_POWERADAPTER_NOT_CONNECTED; + else + result->watts = (int) (ffStrbufToDouble(&tmpBuffer) / 1e6 + 0.5); + + //At this point, we have a battery. Try to get as much values as possible. + + ffStrbufInit(&result->manufacturer); + ffStrbufAppendS(dir, "/manufacturer"); + if (ffReadFileBuffer(dir->chars, &result->manufacturer)) + ffStrbufTrimRightSpace(&result->manufacturer); + else if (ffStrEquals(id, "macsmc-ac")) // asahi + ffStrbufSetStatic(&result->manufacturer, "Apple Inc."); + ffStrbufSubstrBefore(dir, dirLength); + + ffStrbufInit(&result->modelName); + ffStrbufAppendS(dir, "/model_name"); + if (ffReadFileBuffer(dir->chars, &result->modelName)) + ffStrbufTrimRightSpace(&result->modelName); + ffStrbufSubstrBefore(dir, dirLength); + + ffStrbufInit(&result->serial); + ffStrbufAppendS(dir, "/serial_number"); + if (ffReadFileBuffer(dir->chars, &result->serial)) + ffStrbufTrimRightSpace(&result->serial); + ffStrbufSubstrBefore(dir, dirLength); +} + +const char* ffDetectPowerAdapter(FFlist* results) +{ + FF_STRBUF_AUTO_DESTROY baseDir = ffStrbufCreateA(64); + ffStrbufAppendS(&baseDir, "/sys/class/power_supply/"); + + uint32_t baseDirLength = baseDir.length; + + FF_AUTO_CLOSE_DIR DIR* dirp = opendir(baseDir.chars); + if(dirp == NULL) + return "opendir(\"/sys/class/power_supply/\") == NULL"; + + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + if(ffStrEquals(entry->d_name, ".") || ffStrEquals(entry->d_name, "..")) + continue; + + ffStrbufAppendS(&baseDir, entry->d_name); + parsePowerAdapter(&baseDir, entry->d_name, results); + ffStrbufSubstrBefore(&baseDir, baseDirLength); + } + + if(results->length == 0) + return "\"/sys/class/power_supply/\" doesn't contain any power adapter folder"; + + return NULL; +} diff --git a/src/detection/poweradapter/poweradapter_nosupport.c b/src/detection/poweradapter/poweradapter_nosupport.c index 80730810d..1ae9d5395 100644 --- a/src/detection/poweradapter/poweradapter_nosupport.c +++ b/src/detection/poweradapter/poweradapter_nosupport.c @@ -1,7 +1,6 @@ -#include "fastfetch.h" +#include "poweradapter.h" -const char* ffDetectPowerAdapterImpl(FFlist* results) +const char* ffDetectPowerAdapter(FF_MAYBE_UNUSED FFlist* results) { - FF_UNUSED(results); return "Not supported on this platform"; } diff --git a/src/detection/sound/sound_bsd.c b/src/detection/sound/sound_bsd.c new file mode 100644 index 000000000..3053c14d5 --- /dev/null +++ b/src/detection/sound/sound_bsd.c @@ -0,0 +1,48 @@ +#include "sound.h" +#include "common/io/io.h" +#include "common/sysctl.h" + +#include +#include + +const char* ffDetectSound(FFlist* devices) +{ + char path[] = "/dev/mixer0"; + int defaultDev = ffSysctlGetInt("hw.snd.default_unit", -1); + + for (int idev = 0; idev <= 9; ++idev) + { + path[strlen("/dev/mixer")] = (char) ('0' + idev); + FF_AUTO_CLOSE_FD int fd = open(path, O_RDWR); + if (fd < 0) break; + + uint32_t devmask = 0; + if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) + continue; + if (!(devmask & SOUND_MASK_VOLUME)) + continue; + + uint32_t mutemask = 0; + if (ioctl(fd, SOUND_MIXER_READ_MUTE, &mutemask) < 0) + continue; + + struct oss_card_info ci = { .card = idev }; + if (ioctl(fd, SNDCTL_CARDINFO, &ci) < 0) + continue; + + uint32_t volume; + if (ioctl(fd, SOUND_MIXER_READ_VOLUME, &volume) < 0) + continue; + + FFSoundDevice* device = ffListAdd(devices); + ffStrbufInitS(&device->identifier, path); + ffStrbufInitF(&device->name, "%s %s", ci.longname, ci.hw_info); + device->volume = mutemask & SOUND_MASK_VOLUME + ? 0 + : ((uint8_t) volume /*left*/ + (uint8_t) (volume >> 8) /*right*/) / 2; + device->active = true; + device->main = defaultDev == idev; + } + + return NULL; +} diff --git a/src/detection/temps/temps_apple.c b/src/detection/temps/temps_apple.c index 2fbc2ae09..c4cef7827 100644 --- a/src/detection/temps/temps_apple.c +++ b/src/detection/temps/temps_apple.c @@ -85,12 +85,9 @@ static void smcUltostr(char *str, uint32_t val) static const char *smcCall(io_connect_t conn, uint32_t selector, SmcKeyData_t *inputStructure, SmcKeyData_t *outputStructure) { - size_t structureInputSize; - size_t structureOutputSize; - structureInputSize = sizeof(SmcKeyData_t); - structureOutputSize = sizeof(SmcKeyData_t); + size_t size = sizeof(SmcKeyData_t); - if (IOConnectCallStructMethod(conn, selector, inputStructure, structureInputSize, outputStructure, &structureOutputSize) != kIOReturnSuccess) + if (IOConnectCallStructMethod(conn, selector, inputStructure, size, outputStructure, &size) != kIOReturnSuccess) return "IOConnectCallStructMethod(conn) failed"; return NULL; } @@ -140,17 +137,13 @@ static const char *smcReadSmcVal(io_connect_t conn, const UInt32Char_t key, SmcV static const char *smcOpen(io_connect_t *conn) { - io_iterator_t iterator; - if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("AppleSMC"), &iterator) != kIOReturnSuccess) - return "IOServiceGetMatchingServices() failed"; - - io_object_t device = IOIteratorNext(iterator); - IOObjectRelease(iterator); - if (device == 0) + io_object_t device = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("AppleSMC")); + if (!device) return "No SMC device found"; kern_return_t result = IOServiceOpen(device, mach_task_self(), 0, conn); IOObjectRelease(device); + if (result != kIOReturnSuccess) return "IOServiceOpen() failed"; @@ -290,10 +283,13 @@ static bool detectTemp(io_connect_t conn, const char *sensor, double* sum) const char *ffDetectSmcTemps(enum FFTempType type, double *result) { static io_connect_t conn; - static dispatch_once_t once_control; - dispatch_once_f(&once_control, &conn, (dispatch_function_t) smcOpen); - if(!conn) - return "smcOpen() failed"; + if (!conn) + { + if (smcOpen(&conn) != NULL) + conn = (io_connect_t) -1; + } + else if (conn == (io_connect_t) -1) + return "Could not open SMC connection"; uint32_t count = 0; *result = 0; diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 23932ef81..532296af9 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -394,35 +394,35 @@ static bool detectContour(const FFstrbuf* exe, FFTerminalFontResult* result) return true; } -void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont); +void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFontResult* terminalFont); -static bool detectTerminalFontCommon(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +static bool detectTerminalFontCommon(const FFTerminalResult* terminal, FFTerminalFontResult* terminalFont) { - if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "alacritty")) + if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "alacritty")) detectAlacritty(terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "terminator")) + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "terminator")) detectTerminator(terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "wezterm-gui")) + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "wezterm-gui")) detectWezterm(terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "tabby")) + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "tabby")) detectTabby(terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "contour")) - detectContour(&terminalShell->terminalExe, terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "contour")) + detectContour(&terminal->exe, terminalFont); #ifndef _WIN32 - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/pts/")) + else if(ffStrbufStartsWithIgnCaseS(&terminal->exe, "/dev/pts/")) ffStrbufAppendS(&terminalFont->error, "Terminal font detection is not supported on PTS"); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "kitty")) - detectKitty(&terminalShell->terminalExe, terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalExe, "/dev/tty")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "kitty")) + detectKitty(&terminal->exe, terminalFont); + else if(ffStrbufStartsWithIgnCaseS(&terminal->exe, "/dev/tty")) detectTTY(terminalFont); #endif #if defined(_WIN32) || defined(__linux__) //Used by both Linux (WSL) and Windows - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "Windows Terminal") || - ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "WindowsTerminal.exe")) - detectFromWindowsTeriminal(&terminalShell->terminalExe, terminalFont); + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "Windows Terminal") || + ffStrbufIgnCaseEqualS(&terminal->processName, "WindowsTerminal.exe")) + detectFromWindowsTeriminal(&terminal->exe, terminalFont); #endif else @@ -433,16 +433,16 @@ static bool detectTerminalFontCommon(const FFTerminalShellResult* terminalShell, bool ffDetectTerminalFont(FFTerminalFontResult* result) { - const FFTerminalShellResult* terminalShell = ffDetectTerminalShell(); + const FFTerminalResult* terminal = ffDetectTerminal(); - if(terminalShell->terminalProcessName.length == 0) + if(terminal->processName.length == 0) ffStrbufAppendS(&result->error, "Terminal font needs successful terminal detection"); - else if(!detectTerminalFontCommon(terminalShell, result)) - ffDetectTerminalFontPlatform(terminalShell, result); + else if(!detectTerminalFontCommon(terminal, result)) + ffDetectTerminalFontPlatform(terminal, result); if(result->error.length == 0 && result->font.pretty.length == 0) - ffStrbufAppendF(&result->error, "Unknown terminal: %s", terminalShell->terminalProcessName.chars); + ffStrbufAppendF(&result->error, "Unknown terminal: %s", terminal->processName.chars); return result->error.length == 0; } diff --git a/src/detection/terminalfont/terminalfont_android.c b/src/detection/terminalfont/terminalfont_android.c index 72a67fb0a..fef9ad24a 100644 --- a/src/detection/terminalfont/terminalfont_android.c +++ b/src/detection/terminalfont/terminalfont_android.c @@ -53,9 +53,9 @@ exit: #endif } -void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFontResult* terminalFont) { - if(ffStrbufCompS(&terminalShell->terminalProcessName, "com.termux") != 0) + if(ffStrbufCompS(&terminal->processName, "com.termux") != 0) { ffStrbufSetS(&terminalFont->error, "Unsupported terminal"); return; diff --git a/src/detection/terminalfont/terminalfont_apple.m b/src/detection/terminalfont/terminalfont_apple.m index 871462109..7d6aca282 100644 --- a/src/detection/terminalfont/terminalfont_apple.m +++ b/src/detection/terminalfont/terminalfont_apple.m @@ -91,13 +91,13 @@ static void detectWarpTerminal(FFTerminalFontResult* terminalFont) ffFontInitValues(&terminalFont->font, fontName.UTF8String, fontSize.UTF8String); } -void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFontResult* terminalFont) { - if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "iterm.app") == 0 || - ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "iTermServer-")) + if(ffStrbufIgnCaseEqualS(&terminal->processName, "iterm.app") || + ffStrbufStartsWithIgnCaseS(&terminal->processName, "iTermServer-")) detectIterm2(terminalFont); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Apple_Terminal") == 0) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "Apple_Terminal")) detectAppleTerminal(terminalFont); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "WarpTerminal") == 0) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal")) detectWarpTerminal(terminalFont); } diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index a8fc901bb..8f163418e 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -303,32 +303,32 @@ static void detectSt(FFTerminalFontResult* terminalFont, uint32_t pid) ffFontInitValues(&terminalFont->font, font.chars, size.chars); } -void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFontResult* terminalFont) { - if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "konsole")) + if(ffStrbufIgnCaseEqualS(&terminal->processName, "konsole")) detectKonsole(terminalFont, "konsolerc"); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "yakuake")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "yakuake")) detectKonsole(terminalFont, "yakuakerc"); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "xfce4-terminal")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "xfce4-terminal")) detectXFCETerminal(terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "lxterminal")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "lxterminal")) detectFromConfigFile("lxterminal/lxterminal.conf", "fontname =", terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "tilix")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "tilix")) detectFromGSettings("/com/gexperts/Tilix/profiles/", "com.gexperts.Tilix.ProfilesList", "com.gexperts.Tilix.Profile", "default", terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "gnome-terminal-")) + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "gnome-terminal-")) detectFromGSettings("/org/gnome/terminal/legacy/profiles:/:", "org.gnome.Terminal.ProfilesList", "org.gnome.Terminal.Legacy.Profile", "default", terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "kgx")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "kgx")) detectKgx(terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "mate-terminal")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "mate-terminal")) detectFromGSettings("/org/mate/terminal/profiles/", "org.mate.terminal.global", "org.mate.terminal.profile", "default-profile", terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "deepin-terminal")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "deepin-terminal")) detectDeepinTerminal(terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "foot")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "foot")) detectFootTerminal(terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "qterminal")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "qterminal")) detectQTerminal(terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "xterm")) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "xterm")) detectXterm(terminalFont); - else if(ffStrbufIgnCaseEqualS(&terminalShell->terminalProcessName, "st")) - detectSt(terminalFont, terminalShell->terminalPid); + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "st")) + detectSt(terminalFont, terminal->pid); } diff --git a/src/detection/terminalfont/terminalfont_windows.c b/src/detection/terminalfont/terminalfont_windows.c index e461986dd..633781ad7 100644 --- a/src/detection/terminalfont/terminalfont_windows.c +++ b/src/detection/terminalfont/terminalfont_windows.c @@ -85,12 +85,12 @@ static void detectConEmu(FFTerminalFontResult* terminalFont) ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); } -void ffDetectTerminalFontPlatform(const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) +void ffDetectTerminalFontPlatform(const FFTerminalResult* terminal, FFTerminalFontResult* terminalFont) { - if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "mintty") == 0) + if(ffStrbufIgnCaseEqualS(&terminal->processName, "mintty")) detectMintty(terminalFont); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "conhost.exe") == 0) + else if(ffStrbufIgnCaseEqualS(&terminal->processName, "conhost.exe")) detectConhost(terminalFont); - else if(ffStrbufStartsWithIgnCaseS(&terminalShell->terminalProcessName, "ConEmu")) + else if(ffStrbufStartsWithIgnCaseS(&terminal->processName, "ConEmu")) detectConEmu(terminalFont); } diff --git a/src/detection/terminalshell/terminalshell.c b/src/detection/terminalshell/terminalshell.c index 68b963e07..118b4c70f 100644 --- a/src/detection/terminalshell/terminalshell.c +++ b/src/detection/terminalshell/terminalshell.c @@ -152,6 +152,22 @@ static bool getShellVersionNushell(FFstrbuf* exe, FFstrbuf* version) return getExeVersionRaw(exe, version); //0.73.0 } +static bool getShellVersionAsh(FFstrbuf* exe, FFstrbuf* version) +{ + if(ffProcessAppendStdErr(version, (char* const[]) { + exe->chars, + "--help", + NULL + }) != NULL) + return false; + + // BusyBox v1.36.1 (2023-11-07 18:53:09 UTC) multi-call binary... + ffStrbufSubstrAfterFirstC(version, ' '); + ffStrbufSubstrBeforeFirstC(version, ' '); + ffStrbufTrimLeft(version, 'v'); + return true; +} + #ifdef _WIN32 static bool getShellVersionWinPowerShell(FFstrbuf* exe, FFstrbuf* version) { @@ -210,6 +226,8 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) return getShellVersionOils(exe, version); if(strcasecmp(exeName, "elvish") == 0) return getExeVersionRaw(exe, version); + if(strcasecmp(exeName, "ash") == 0) + return getShellVersionAsh(exe, version); if(strcasecmp(exeName, "python") == 0 && getenv("XONSH_VERSION")) { ffStrbufSetS(version, getenv("XONSH_VERSION")); diff --git a/src/detection/terminalshell/terminalshell.h b/src/detection/terminalshell/terminalshell.h index 3dbb0633f..921fb7098 100644 --- a/src/detection/terminalshell/terminalshell.h +++ b/src/detection/terminalshell/terminalshell.h @@ -2,21 +2,27 @@ #include "fastfetch.h" -typedef struct FFTerminalShellResult +typedef struct FFShellResult { - FFstrbuf shellProcessName; - FFstrbuf shellExe; - const char* shellExeName; //pointer to a char in shellExe - FFstrbuf shellPrettyName; - FFstrbuf shellVersion; - uint32_t shellPid; + FFstrbuf processName; + FFstrbuf exe; + const char* exeName; //pointer to a char in exe + FFstrbuf prettyName; + FFstrbuf version; + uint32_t pid; + uint32_t ppid; +} FFShellResult; - FFstrbuf terminalProcessName; - FFstrbuf terminalExe; - FFstrbuf terminalPrettyName; - const char* terminalExeName; //pointer to a char in terminalExe - FFstrbuf terminalVersion; - uint32_t terminalPid; -} FFTerminalShellResult; +typedef struct FFTerminalResult +{ + FFstrbuf processName; + FFstrbuf exe; + FFstrbuf prettyName; + const char* exeName; //pointer to a char in exe + FFstrbuf version; + uint32_t pid; + uint32_t ppid; +} FFTerminalResult; -const FFTerminalShellResult* ffDetectTerminalShell(); +const FFShellResult* ffDetectShell(); +const FFTerminalResult* ffDetectTerminal(); diff --git a/src/detection/terminalshell/terminalshell_linux.c b/src/detection/terminalshell/terminalshell_linux.c index 327ba9772..6bbbec717 100644 --- a/src/detection/terminalshell/terminalshell_linux.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -75,7 +75,7 @@ static const char* getProcessNameAndPpid(pid_t pid, char* name, pid_t* ppid) char buf[PROC_FILE_BUFFSIZ]; ssize_t nRead = ffReadFileData(statFilePath, sizeof(buf) - 1, buf); if(nRead < 0) - return "ffReadFileData(statFilePath, sizeof(buf)-1, buf)"; + return "ffReadFileData(statFilePath, sizeof(buf)-1, buf) failed"; buf[nRead] = '\0'; *ppid = 0; @@ -118,95 +118,114 @@ static const char* getProcessNameAndPpid(pid_t pid, char* name, pid_t* ppid) return NULL; } -static void getTerminalShell(FFTerminalShellResult* result, pid_t pid) +static pid_t getShellInfo(FFShellResult* result, pid_t pid) { char name[256]; name[0] = '\0'; pid_t ppid = 0; - if(getProcessNameAndPpid(pid, name, &ppid)) - return; - - //Common programs that are between terminal and own process, but are not the shell - if( - strcasecmp(name, "sh") == 0 || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell - strcasecmp(name, "sudo") == 0 || - strcasecmp(name, "su") == 0 || - strcasecmp(name, "strace") == 0 || - strcasecmp(name, "sshd") == 0 || - strcasecmp(name, "gdb") == 0 || - strcasecmp(name, "lldb") == 0 || - strcasecmp(name, "login") == 0 || - strcasecmp(name, "ltrace") == 0 || - strcasecmp(name, "perf") == 0 || - strcasecmp(name, "guake-wrapped") == 0 || - strcasestr(name, "debug") != NULL || - strcasestr(name, "command-not-found") != NULL || - ffStrEndsWith(name, ".sh") - ) { - getTerminalShell(result, ppid); - return; - } - - //Known shells - if ( - strcasecmp(name, "ash") == 0 || - strcasecmp(name, "bash") == 0 || - strcasecmp(name, "zsh") == 0 || - strcasecmp(name, "ksh") == 0 || - strcasecmp(name, "mksh") == 0 || - strcasecmp(name, "oksh") == 0 || - strcasecmp(name, "csh") == 0 || - strcasecmp(name, "tcsh") == 0 || - strcasecmp(name, "fish") == 0 || - strcasecmp(name, "dash") == 0 || - strcasecmp(name, "pwsh") == 0 || - strcasecmp(name, "nu") == 0 || - strcasecmp(name, "git-shell") == 0 || - strcasecmp(name, "elvish") == 0 || - strcasecmp(name, "oil.ovm") == 0 || - (strcasecmp(name, "python") == 0 && getenv("XONSH_VERSION")) - ) { - if (result->shellProcessName.length == 0) + while (getProcessNameAndPpid(pid, name, &ppid) == NULL) + { + //Common programs that are between terminal and own process, but are not the shell + if( + ffStrEquals(name, "sh") || //This prevents us from detecting things like pipes and redirects, i hope nobody uses plain `sh` as shell + ffStrEquals(name, "sudo") || + ffStrEquals(name, "su") || + ffStrEquals(name, "strace") || + ffStrEquals(name, "sshd") || + ffStrEquals(name, "gdb") || + ffStrEquals(name, "lldb") || + ffStrEquals(name, "lldb-mi") || + ffStrEquals(name, "login") || + ffStrEquals(name, "ltrace") || + ffStrEquals(name, "perf") || + ffStrEquals(name, "guake-wrapped") || + ffStrContainsIgnCase(name, "debug") || + ffStrContainsIgnCase(name, "not-found") || + ffStrEndsWith(name, ".sh") + ) { - result->shellPid = (uint32_t) pid; - ffStrbufSetS(&result->shellProcessName, name); - getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName); + pid = ppid; + continue; } - getTerminalShell(result, ppid); - return; + result->pid = (uint32_t) pid; + result->ppid = (uint32_t) ppid; + ffStrbufSetS(&result->processName, name); + getProcessInformation(pid, &result->processName, &result->exe, &result->exeName); + break; } - - #ifdef __APPLE__ - // https://github.com/fastfetch-cli/fastfetch/discussions/501 - if (ffStrEndsWith(name, " (figterm)") || ffStrEndsWith(name, " (cwterm)")) - getProcessNameAndPpid(ppid, name, &ppid); - #endif - - result->terminalPid = (uint32_t) pid; - ffStrbufSetS(&result->terminalProcessName, name); - getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName); + return ppid; } -static void getTerminalFromEnv(FFTerminalShellResult* result) +static pid_t getTerminalInfo(FFTerminalResult* result, pid_t pid) { - if( - result->terminalProcessName.length > 0 && - !ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") && - !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "(login)") && + char name[256]; + name[0] = '\0'; + + pid_t ppid = 0; + + while (getProcessNameAndPpid(pid, name, &ppid) == NULL) + { + //Known shells + if ( + ffStrEquals(name, "ash") || + ffStrEquals(name, "bash") || + ffStrEquals(name, "zsh") || + ffStrEquals(name, "ksh") || + ffStrEquals(name, "mksh") || + ffStrEquals(name, "oksh") || + ffStrEquals(name, "csh") || + ffStrEquals(name, "tcsh") || + ffStrEquals(name, "fish") || + ffStrEquals(name, "dash") || + ffStrEquals(name, "pwsh") || + ffStrEquals(name, "nu") || + ffStrEquals(name, "git-shell") || + ffStrEquals(name, "elvish") || + ffStrEquals(name, "oil.ovm") || + (ffStrEquals(name, "python") && getenv("XONSH_VERSION")) + ) + { + pid = ppid; + continue; + } #ifdef __APPLE__ - !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "launchd") && - !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "stable") && //for WarpTerminal - #else - !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "systemd") && - !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "init") && - !ffStrbufIgnCaseEqualS(&result->terminalProcessName, "(init)") && + // https://github.com/fastfetch-cli/fastfetch/discussions/501 + if (ffStrEndsWith(name, " (figterm)") || ffStrEndsWith(name, " (cwterm)")) + { + if (__builtin_expect(getProcessNameAndPpid(ppid, name, &ppid) != NULL, false)) + return 0; + } #endif - ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0 + result->pid = (uint32_t) pid; + ffStrbufSetS(&result->processName, name); + getProcessInformation(pid, &result->processName, &result->exe, &result->exeName); + break; + } + return ppid; +} + +static void getTerminalFromEnv(FFTerminalResult* result) +{ + if( + result->processName.length > 0 && + !ffStrbufStartsWithS(&result->processName, "login") && + !ffStrbufEqualS(&result->processName, "(login)") && + + #ifdef __APPLE__ + !ffStrbufEqualS(&result->processName, "launchd") && + !ffStrbufEqualS(&result->processName, "stable") && //for WarpTerminal + #else + !ffStrbufEqualS(&result->processName, "systemd") && + !ffStrbufEqualS(&result->processName, "init") && + !ffStrbufEqualS(&result->processName, "(init)") && + #endif + + !ffStrbufEqualS(&result->processName, "0") ) return; const char* term = NULL; @@ -262,25 +281,25 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) term = getenv("TERM"); //TTY - if(!ffStrSet(term) || strcasecmp(term, "linux") == 0) + if(!ffStrSet(term) || ffStrEquals(term, "linux")) term = ttyname(STDIN_FILENO); if(ffStrSet(term)) { - ffStrbufSetS(&result->terminalProcessName, term); - ffStrbufSetS(&result->terminalExe, term); - setExeName(&result->terminalExe, &result->terminalExeName); + ffStrbufSetS(&result->processName, term); + ffStrbufSetS(&result->exe, term); + setExeName(&result->exe, &result->exeName); } } -static void getUserShellFromEnv(FFTerminalShellResult* result) +static void getUserShellFromEnv(FFShellResult* result) { //If shell detection via processes failed - if(result->shellProcessName.length == 0 && instance.state.platform.userShell.length > 0) + if(result->processName.length == 0 && instance.state.platform.userShell.length > 0) { - ffStrbufSet(&result->shellExe, &instance.state.platform.userShell); - setExeName(&result->shellExe, &result->shellExeName); - ffStrbufAppendS(&result->shellProcessName, result->shellExeName); + ffStrbufSet(&result->exe, &instance.state.platform.userShell); + setExeName(&result->exe, &result->exeName); + ffStrbufAppendS(&result->processName, result->exeName); } } @@ -288,107 +307,133 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); -const FFTerminalShellResult* ffDetectTerminalShell() +static void setShellInfoDetails(FFShellResult* result) { - static FFTerminalShellResult result; + ffStrbufClear(&result->version); + fftsGetShellVersion(&result->exe, result->exeName, &result->version); + + if(ffStrbufEqualS(&result->processName, "pwsh")) + ffStrbufInitStatic(&result->prettyName, "PowerShell"); + else if(ffStrbufEqualS(&result->processName, "nu")) + ffStrbufInitStatic(&result->prettyName, "nushell"); + else if(ffStrbufEqualS(&result->processName, "python") && getenv("XONSH_VERSION")) + ffStrbufInitStatic(&result->prettyName, "xonsh"); + else if(ffStrbufEqualS(&result->processName, "oil.ovm")) + ffStrbufInitStatic(&result->prettyName, "Oils"); + else + { + // https://github.com/fastfetch-cli/fastfetch/discussions/280#discussioncomment-3831734 + ffStrbufInitS(&result->prettyName, result->exeName); + } +} + +static void setTerminalInfoDetails(FFTerminalResult* result) +{ + if(result->exeName[0] == '.' && ffStrEndsWith(result->exeName, "-wrapped")) + { + // For NixOS. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428 + // We use processName when detecting version and font, overriding it for simplification + ffStrbufSetNS( + &result->processName, + (uint32_t) (strlen(result->exeName) - strlen(".-wrapped")), + result->exeName + 1); + } + + if(ffStrbufEqualS(&result->processName, "wezterm-gui")) + ffStrbufInitStatic(&result->prettyName, "WezTerm"); + else if(ffStrbufStartsWithS(&result->processName, "tmux:")) + ffStrbufInitStatic(&result->prettyName, "tmux"); + + #if defined(__ANDROID__) + + else if(ffStrbufEqualS(&result->processName, "com.termux")) + ffStrbufInitStatic(&result->prettyName, "Termux"); + + #elif defined(__linux__) || defined(__FreeBSD__) + + else if(ffStrbufStartsWithS(&result->processName, "gnome-terminal-")) + ffStrbufInitStatic(&result->prettyName, "GNOME Terminal"); + else if(ffStrbufStartsWithS(&result->processName, "kgx")) + ffStrbufInitStatic(&result->prettyName, "GNOME Console"); + else if(ffStrbufEqualS(&result->processName, "urxvt") || + ffStrbufEqualS(&result->processName, "urxvtd") || + ffStrbufEqualS(&result->processName, "rxvt") + ) + ffStrbufInitStatic(&result->prettyName, "rxvt-unicode"); + + #elif defined(__APPLE__) + + else if(ffStrbufEqualS(&result->processName, "iTerm.app") || ffStrbufStartsWithS(&result->processName, "iTermServer-")) + ffStrbufInitStatic(&result->prettyName, "iTerm"); + else if(ffStrbufEqualS(&result->processName, "Apple_Terminal")) + ffStrbufInitStatic(&result->prettyName, "Apple Terminal"); + else if(ffStrbufEqualS(&result->processName, "WarpTerminal")) + ffStrbufInitStatic(&result->prettyName, "Warp"); + + #endif + + else if(strncmp(result->exeName, result->processName.chars, result->processName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName + ffStrbufInitS(&result->prettyName, result->exeName); + else + ffStrbufInitCopy(&result->prettyName, &result->processName); + + ffStrbufInit(&result->version); + fftsGetTerminalVersion(&result->processName, &result->exe, &result->version); +} + +#ifdef __APPLE__ +#define FF_EXE_PATH_LEN PROC_PIDPATHINFO_MAXSIZE +#elif defined(MAXPATH) +#define FF_EXE_PATH_LEN MAXPATH +#elif defined(PATH_MAX) +#define FF_EXE_PATH_LEN PATH_MAX +#else +#define FF_EXE_PATH_LEN 260 +#endif + +const FFShellResult* ffDetectShell() +{ + static FFShellResult result; static bool init = false; if(init) return &result; init = true; - #ifdef __APPLE__ - const uint32_t exePathLen = PROC_PIDPATHINFO_MAXSIZE; - #elif defined(MAXPATH) - const uint32_t exePathLen = MAXPATH; - #elif defined(PATH_MAX) - const uint32_t exePathLen = PATH_MAX; - #else - const uint32_t exePathLen = 260; - #endif + ffStrbufInit(&result.processName); + ffStrbufInitA(&result.exe, FF_EXE_PATH_LEN); + result.exeName = result.exe.chars; + ffStrbufInit(&result.version); + result.pid = 0; + result.ppid = 0; - ffStrbufInit(&result.shellProcessName); - ffStrbufInitA(&result.shellExe, exePathLen); - result.shellExeName = result.shellExe.chars; - ffStrbufInit(&result.shellVersion); - result.shellPid = 0; - - ffStrbufInit(&result.terminalProcessName); - ffStrbufInitA(&result.terminalExe, exePathLen); - result.terminalExeName = result.terminalExe.chars; - result.terminalPid = 0; - - getTerminalShell(&result, getppid()); - - getTerminalFromEnv(&result); + pid_t ppid = getppid(); + ppid = getShellInfo(&result, ppid); getUserShellFromEnv(&result); - - ffStrbufClear(&result.shellVersion); - fftsGetShellVersion(&result.shellExe, result.shellExeName, &result.shellVersion); - - if(ffStrbufEqualS(&result.shellProcessName, "pwsh")) - ffStrbufInitStatic(&result.shellPrettyName, "PowerShell"); - else if(ffStrbufEqualS(&result.shellProcessName, "nu")) - ffStrbufInitStatic(&result.shellPrettyName, "nushell"); - else if(ffStrbufIgnCaseEqualS(&result.shellProcessName, "python") && getenv("XONSH_VERSION")) - ffStrbufInitStatic(&result.shellPrettyName, "xonsh"); - else if(ffStrbufIgnCaseEqualS(&result.shellProcessName, "oil.ovm")) - ffStrbufInitStatic(&result.shellPrettyName, "Oils"); - else - { - // https://github.com/fastfetch-cli/fastfetch/discussions/280#discussioncomment-3831734 - ffStrbufInitS(&result.shellPrettyName, result.shellExeName); - } - - if(result.terminalExeName[0] == '.' && ffStrEndsWith(result.terminalExeName, "-wrapped")) - { - // For NixOS. Ref: #510 and https://github.com/NixOS/nixpkgs/pull/249428 - // We use terminalProcessName when detecting version and font, overriding it for simplification - ffStrbufSetNS( - &result.terminalProcessName, - (uint32_t) (strlen(result.terminalExeName) - strlen(".-wrapped")), - result.terminalExeName + 1); - } - - if(ffStrbufEqualS(&result.terminalProcessName, "wezterm-gui")) - ffStrbufInitStatic(&result.terminalPrettyName, "WezTerm"); - if(ffStrbufStartsWithS(&result.terminalProcessName, "tmux:")) - ffStrbufInitStatic(&result.terminalPrettyName, "tmux"); - - #if defined(__ANDROID__) - - else if(ffStrbufEqualS(&result.terminalProcessName, "com.termux")) - ffStrbufInitStatic(&result.terminalPrettyName, "Termux"); - - #elif defined(__linux__) || defined(__FreeBSD__) - - else if(ffStrbufStartsWithS(&result.terminalProcessName, "gnome-terminal-")) - ffStrbufInitStatic(&result.terminalPrettyName, "GNOME Terminal"); - else if(ffStrbufStartsWithS(&result.terminalProcessName, "kgx")) - ffStrbufInitStatic(&result.terminalPrettyName, "GNOME Console"); - else if(ffStrbufEqualS(&result.terminalProcessName, "urxvt") || - ffStrbufEqualS(&result.terminalProcessName, "urxvtd") || - ffStrbufEqualS(&result.terminalProcessName, "rxvt") - ) - ffStrbufInitStatic(&result.terminalPrettyName, "rxvt-unicode"); - - #elif defined(__APPLE__) - - else if(ffStrbufEqualS(&result.terminalProcessName, "iTerm.app") || ffStrbufStartsWithS(&result.terminalProcessName, "iTermServer-")) - ffStrbufInitStatic(&result.terminalPrettyName, "iTerm"); - else if(ffStrbufEqualS(&result.terminalProcessName, "Apple_Terminal")) - ffStrbufInitStatic(&result.terminalPrettyName, "Apple Terminal"); - else if(ffStrbufEqualS(&result.terminalProcessName, "WarpTerminal")) - ffStrbufInitStatic(&result.terminalPrettyName, "Warp"); - - #endif - - else if(strncmp(result.terminalExeName, result.terminalProcessName.chars, result.terminalProcessName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName - ffStrbufInitS(&result.terminalPrettyName, result.terminalExeName); - else - ffStrbufInitCopy(&result.terminalPrettyName, &result.terminalProcessName); - - ffStrbufInit(&result.terminalVersion); - fftsGetTerminalVersion(&result.terminalProcessName, &result.terminalExe, &result.terminalVersion); + setShellInfoDetails(&result); + + return &result; +} + +const FFTerminalResult* ffDetectTerminal() +{ + static FFTerminalResult result; + static bool init = false; + if(init) + return &result; + init = true; + + ffStrbufInit(&result.processName); + ffStrbufInitA(&result.exe, FF_EXE_PATH_LEN); + result.exeName = result.exe.chars; + result.pid = 0; + result.ppid = 0; + + pid_t ppid = (pid_t) ffDetectShell()->ppid; + + if (ppid) + ppid = getTerminalInfo(&result, ppid); + getTerminalFromEnv(&result); + setTerminalInfoDetails(&result); return &result; } diff --git a/src/detection/terminalshell/terminalshell_windows.c b/src/detection/terminalshell/terminalshell_windows.c index 0f2f23c81..a462ab566 100644 --- a/src/detection/terminalshell/terminalshell_windows.c +++ b/src/detection/terminalshell/terminalshell_windows.c @@ -83,55 +83,71 @@ static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrb bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version); -static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) +static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) { uint32_t ppid; - if(pid == 0 || !getProcessInfo(pid, &ppid, &result->shellProcessName, &result->shellExe, &result->shellExeName)) - return 0; - - ffStrbufSet(&result->shellPrettyName, &result->shellProcessName); - if(ffStrbufEndsWithIgnCaseS(&result->shellPrettyName, ".exe")) - ffStrbufSubstrBefore(&result->shellPrettyName, result->shellPrettyName.length - 4); - - //Common programs that are between terminal and own process, but are not the shell - if( - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "sudo") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "su") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "doas") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "strace") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "sshd") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "gdb") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "lldb") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "guake-wrapped") || - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "fastfetch") || //scoop warps the real binaries with a "shim" exe - ffStrbufIgnCaseEqualS(&result->shellPrettyName, "flashfetch") || - ffStrbufContainIgnCaseS(&result->shellPrettyName, "debug") || - ffStrbufStartsWithIgnCaseS(&result->shellPrettyName, "ConEmu") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014 - ) { - ffStrbufClear(&result->shellProcessName); - ffStrbufClear(&result->shellPrettyName); - ffStrbufClear(&result->shellExe); - result->shellExeName = NULL; - return getShellInfo(result, ppid); - } - - ffStrbufClear(&result->shellVersion); - fftsGetShellVersion(&result->shellExe, result->shellPrettyName.chars, &result->shellVersion); - - result->shellPid = pid; - if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "pwsh")) - ffStrbufSetS(&result->shellPrettyName, "PowerShell"); - else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell")) - ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell"); - else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell_ise")) - ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell ISE"); - else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "cmd")) + while (pid != 0 && getProcessInfo(pid, &ppid, &result->processName, &result->exe, &result->exeName)) { - ffStrbufClear(&result->shellPrettyName); + ffStrbufSet(&result->prettyName, &result->processName); + if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe")) + ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4); + + //Common programs that are between terminal and own process, but are not the shell + if( + ffStrbufIgnCaseEqualS(&result->prettyName, "sudo") || + ffStrbufIgnCaseEqualS(&result->prettyName, "su") || + ffStrbufIgnCaseEqualS(&result->prettyName, "doas") || + ffStrbufIgnCaseEqualS(&result->prettyName, "strace") || + ffStrbufIgnCaseEqualS(&result->prettyName, "sshd") || + ffStrbufIgnCaseEqualS(&result->prettyName, "gdb") || + ffStrbufIgnCaseEqualS(&result->prettyName, "lldb") || + ffStrbufIgnCaseEqualS(&result->prettyName, "guake-wrapped") || + ffStrbufIgnCaseEqualS(&result->prettyName, "fastfetch") || //scoop warps the real binaries with a "shim" exe + ffStrbufIgnCaseEqualS(&result->prettyName, "flashfetch") || + ffStrbufContainIgnCaseS(&result->prettyName, "debug") || + ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmu") // https://github.com/fastfetch-cli/fastfetch/issues/488#issuecomment-1619982014 + ) { + ffStrbufClear(&result->processName); + ffStrbufClear(&result->prettyName); + ffStrbufClear(&result->exe); + result->exeName = NULL; + pid = ppid; + continue; + } + + ffStrbufClear(&result->version); + fftsGetShellVersion(&result->exe, result->prettyName.chars, &result->version); + + result->pid = pid; + result->ppid = ppid; + + if(ffStrbufIgnCaseEqualS(&result->prettyName, "explorer")) + { + ffStrbufSetS(&result->prettyName, "Windows Explorer"); // Started without shell + // In this case, terminal process will be created by fastfetch itself. + ppid = 0; + } + + break; + } + return ppid; +} + +static void setShellInfoDetails(FFShellResult* result) +{ + if(ffStrbufIgnCaseEqualS(&result->prettyName, "pwsh")) + ffStrbufSetS(&result->prettyName, "PowerShell"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "powershell")) + ffStrbufSetS(&result->prettyName, "Windows PowerShell"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "powershell_ise")) + ffStrbufSetS(&result->prettyName, "Windows PowerShell ISE"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "cmd")) + { + ffStrbufClear(&result->prettyName); FF_AUTO_CLOSE_FD HANDLE snapshot = NULL; - while(!(snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)) && GetLastError() == ERROR_BAD_LENGTH) {} + while(!(snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, result->pid)) && GetLastError() == ERROR_BAD_LENGTH) {} if(snapshot) { @@ -141,33 +157,27 @@ static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) { if(wcsncmp(module.szModule, L"clink_dll_", strlen("clink_dll_")) == 0) { - ffStrbufAppendS(&result->shellPrettyName, "CMD (with Clink "); - getProductVersion(module.szExePath, &result->shellPrettyName); - ffStrbufAppendC(&result->shellPrettyName, ')'); + ffStrbufAppendS(&result->prettyName, "CMD (with Clink "); + getProductVersion(module.szExePath, &result->prettyName); + ffStrbufAppendC(&result->prettyName, ')'); break; } } } - if(result->shellPrettyName.length == 0) - ffStrbufAppendS(&result->shellPrettyName, "Command Prompt"); + if(result->prettyName.length == 0) + ffStrbufAppendS(&result->prettyName, "Command Prompt"); } - else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "nu")) - ffStrbufSetS(&result->shellPrettyName, "nushell"); - else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "explorer")) - { - ffStrbufSetS(&result->shellPrettyName, "Windows Explorer"); // Started without shell - // In this case, terminal process will be created by fastfetch itself. - return 0; - } - - return ppid; + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "nu")) + ffStrbufSetS(&result->prettyName, "nushell"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "explorer")) + ffStrbufSetS(&result->prettyName, "Windows Explorer"); } -static bool getTerminalFromEnv(FFTerminalShellResult* result) +static bool getTerminalFromEnv(FFTerminalResult* result) { if( - result->terminalProcessName.length > 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "explorer") != 0 + result->processName.length > 0 && + ffStrbufIgnCaseCompS(&result->processName, "explorer") != 0 ) return false; const char* term = getenv("ConEmuPID"); @@ -176,12 +186,12 @@ static bool getTerminalFromEnv(FFTerminalShellResult* result) { //ConEmu uint32_t pid = (uint32_t) strtoul(term, NULL, 10); - result->terminalPid = pid; - if(getProcessInfo(pid, NULL, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName)) + result->pid = pid; + if(getProcessInfo(pid, NULL, &result->processName, &result->exe, &result->exeName)) { - ffStrbufSet(&result->terminalPrettyName, &result->terminalProcessName); - if(ffStrbufEndsWithIgnCaseS(&result->terminalPrettyName, ".exe")) - ffStrbufSubstrBefore(&result->terminalPrettyName, result->terminalPrettyName.length - 4); + ffStrbufSet(&result->prettyName, &result->processName); + if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe")) + ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4); return true; } else @@ -198,7 +208,7 @@ static bool getTerminalFromEnv(FFTerminalShellResult* result) if(!term && ( getenv("WT_SESSION") != NULL || getenv("WT_PROFILE_ID") != NULL - )) term = "Windows Terminal"; + )) term = "WindowsTerminal"; //Alacritty if(!term && ( @@ -216,17 +226,17 @@ static bool getTerminalFromEnv(FFTerminalShellResult* result) if(term) { - ffStrbufSetS(&result->terminalProcessName, term); - ffStrbufSetS(&result->terminalPrettyName, term); - ffStrbufSetS(&result->terminalExe, term); - result->terminalExeName = ""; + ffStrbufSetS(&result->processName, term); + ffStrbufSetS(&result->prettyName, term); + ffStrbufSetS(&result->exe, term); + result->exeName = ""; return true; } return false; } -static bool detectDefaultTerminal(FFTerminalShellResult* result) +static bool detectDefaultTerminal(FFTerminalResult* result) { wchar_t regPath[128] = L"SOFTWARE\\Classes\\PackagedCom\\ClassIndex\\"; wchar_t* uuid = regPath + strlen("SOFTWARE\\Classes\\PackagedCom\\ClassIndex\\"); @@ -251,18 +261,18 @@ static bool detectDefaultTerminal(FFTerminalShellResult* result) { if (ffStrbufStartsWithS(&path, "Microsoft.WindowsTerminal")) { - ffStrbufSetS(&result->terminalProcessName, "WindowsTerminal.exe"); - ffStrbufSetS(&result->terminalPrettyName, "WindowsTerminal"); - ffStrbufSetF(&result->terminalExe, "%s\\WindowsApps\\%s\\WindowsTerminal.exe", getenv("ProgramFiles"), path.chars); - if(ffPathExists(result->terminalExe.chars, FF_PATHTYPE_FILE)) + ffStrbufSetS(&result->processName, "WindowsTerminal.exe"); + ffStrbufSetS(&result->prettyName, "WindowsTerminal"); + ffStrbufSetF(&result->exe, "%s\\WindowsApps\\%s\\WindowsTerminal.exe", getenv("ProgramFiles"), path.chars); + if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE)) { - result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\') + 1; + result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1; } else { - ffStrbufDestroy(&result->terminalExe); - ffStrbufInitMove(&result->terminalExe, &path); - result->terminalExeName = ""; + ffStrbufDestroy(&result->exe); + ffStrbufInitMove(&result->exe, &path); + result->exeName = ""; } return true; } @@ -271,122 +281,148 @@ static bool detectDefaultTerminal(FFTerminalShellResult* result) } conhost: - ffStrbufSetF(&result->terminalExe, "%s\\System32\\conhost.exe", getenv("SystemRoot")); - if(ffPathExists(result->terminalExe.chars, FF_PATHTYPE_FILE)) + ffStrbufSetF(&result->exe, "%s\\System32\\conhost.exe", getenv("SystemRoot")); + if(ffPathExists(result->exe.chars, FF_PATHTYPE_FILE)) { - ffStrbufSetS(&result->terminalProcessName, "conhost.exe"); - ffStrbufSetS(&result->terminalPrettyName, "conhost"); - result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\') + 1; + ffStrbufSetS(&result->processName, "conhost.exe"); + ffStrbufSetS(&result->prettyName, "conhost"); + result->exeName = result->exe.chars + ffStrbufLastIndexC(&result->exe, '\\') + 1; return true; } - ffStrbufClear(&result->terminalExe); + ffStrbufClear(&result->exe); return false; } -static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) +static uint32_t getTerminalInfo(FFTerminalResult* result, uint32_t pid) { uint32_t ppid; - if(pid == 0 || !getProcessInfo(pid, &ppid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName)) - return 0; + while (pid != 0 && getProcessInfo(pid, &ppid, &result->processName, &result->exe, &result->exeName)) + { + ffStrbufSet(&result->prettyName, &result->processName); + if(ffStrbufEndsWithIgnCaseS(&result->prettyName, ".exe")) + ffStrbufSubstrBefore(&result->prettyName, result->prettyName.length - 4); - ffStrbufSet(&result->terminalPrettyName, &result->terminalProcessName); - if(ffStrbufEndsWithIgnCaseS(&result->terminalPrettyName, ".exe")) - ffStrbufSubstrBefore(&result->terminalPrettyName, result->terminalPrettyName.length - 4); - - if( - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "pwsh") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "cmd") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "bash") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "zsh") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "fish") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "nu") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "powershell") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "powershell_ise") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "wsl") || // running inside wsl - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "servercoreshell") || // ServerCore Shell Launcher - ffStrbufStartsWithIgnCaseS(&result->terminalPrettyName, "ConEmuC") // wrapper process of ConEmu - ) { - //We are nested shell - ffStrbufClear(&result->terminalProcessName); - ffStrbufClear(&result->terminalPrettyName); - ffStrbufClear(&result->terminalExe); - result->terminalExeName = ""; - return getTerminalInfo(result, ppid); - } - - if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "sihost") || - ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "explorer") - ) { - // A CUI program created by Windows Explorer will spawn a conhost as its child. - // However the conhost process is just a placeholder; - // The true terminal can be Windows Terminal or others. - if (!getTerminalFromEnv(result) && !detectDefaultTerminal(result)) - { - ffStrbufClear(&result->terminalProcessName); - ffStrbufClear(&result->terminalPrettyName); - ffStrbufClear(&result->terminalExe); - result->terminalExeName = ""; - return 0; + if( + ffStrbufIgnCaseEqualS(&result->prettyName, "pwsh") || + ffStrbufIgnCaseEqualS(&result->prettyName, "cmd") || + ffStrbufIgnCaseEqualS(&result->prettyName, "bash") || + ffStrbufIgnCaseEqualS(&result->prettyName, "zsh") || + ffStrbufIgnCaseEqualS(&result->prettyName, "fish") || + ffStrbufIgnCaseEqualS(&result->prettyName, "nu") || + ffStrbufIgnCaseEqualS(&result->prettyName, "powershell") || + ffStrbufIgnCaseEqualS(&result->prettyName, "powershell_ise") || + ffStrbufIgnCaseEqualS(&result->prettyName, "wsl") || // running inside wsl + ffStrbufIgnCaseEqualS(&result->prettyName, "servercoreshell") || // ServerCore Shell Launcher + ffStrbufStartsWithIgnCaseS(&result->prettyName, "ConEmuC") // wrapper process of ConEmu + ) { + //We are nested shell + ffStrbufClear(&result->processName); + ffStrbufClear(&result->prettyName); + ffStrbufClear(&result->exe); + result->exeName = ""; + pid = ppid; + continue; } - } - else - result->terminalPid = pid; - if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "WindowsTerminal")) - ffStrbufSetStatic(&result->terminalPrettyName, ffStrbufContainIgnCaseS(&result->terminalExe, ".WindowsTerminalPreview_") + if(ffStrbufIgnCaseEqualS(&result->prettyName, "sihost") || + ffStrbufIgnCaseEqualS(&result->prettyName, "explorer") + ) { + // A CUI program created by Windows Explorer will spawn a conhost as its child. + // However the conhost process is just a placeholder; + // The true terminal can be Windows Terminal or others. + if (!getTerminalFromEnv(result) && !detectDefaultTerminal(result)) + { + ffStrbufClear(&result->processName); + ffStrbufClear(&result->prettyName); + ffStrbufClear(&result->exe); + result->exeName = ""; + return 0; + } + } + else + { + result->pid = pid; + result->ppid = ppid; + } + + break; + } + return ppid; +} + +static void setTerminalInfoDetails(FFTerminalResult* result) +{ + if(ffStrbufIgnCaseEqualS(&result->prettyName, "WindowsTerminal")) + ffStrbufSetStatic(&result->prettyName, ffStrbufContainIgnCaseS(&result->exe, ".WindowsTerminalPreview_") ? "Windows Terminal Preview" : "Windows Terminal" ); - else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "conhost")) - ffStrbufSetStatic(&result->terminalPrettyName, "Console Window Host"); - else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "Code")) - ffStrbufSetStatic(&result->terminalPrettyName, "Visual Studio Code"); - else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "explorer")) - ffStrbufSetStatic(&result->terminalPrettyName, "Windows Explorer"); - else if(ffStrbufEqualS(&result->terminalPrettyName, "wezterm-gui")) - ffStrbufSetStatic(&result->terminalPrettyName, "WezTerm"); - - return ppid; + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "conhost")) + ffStrbufSetStatic(&result->prettyName, "Console Window Host"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "Code")) + ffStrbufSetStatic(&result->prettyName, "Visual Studio Code"); + else if(ffStrbufIgnCaseEqualS(&result->prettyName, "explorer")) + ffStrbufSetStatic(&result->prettyName, "Windows Explorer"); + else if(ffStrbufEqualS(&result->prettyName, "wezterm-gui")) + ffStrbufSetStatic(&result->prettyName, "WezTerm"); } bool fftsGetTerminalVersion(FFstrbuf* processName, FFstrbuf* exe, FFstrbuf* version); -const FFTerminalShellResult* ffDetectTerminalShell(void) +const FFShellResult* ffDetectShell(void) { - static FFTerminalShellResult result; + static FFShellResult result; static bool init = false; if(init) return &result; init = true; - ffStrbufInit(&result.shellProcessName); - ffStrbufInitA(&result.shellExe, 128); - result.shellExeName = ""; - ffStrbufInit(&result.shellPrettyName); - ffStrbufInit(&result.shellVersion); - result.shellPid = 0; - - ffStrbufInit(&result.terminalProcessName); - ffStrbufInitA(&result.terminalExe, 128); - result.terminalExeName = ""; - ffStrbufInit(&result.terminalPrettyName); - result.terminalPid = 0; + ffStrbufInit(&result.processName); + ffStrbufInitA(&result.exe, 128); + result.exeName = ""; + ffStrbufInit(&result.prettyName); + ffStrbufInit(&result.version); + result.pid = 0; + result.ppid = 0; uint32_t ppid; if(!getProcessInfo(0, &ppid, NULL, NULL, NULL)) return &result; ppid = getShellInfo(&result, ppid); - if(ppid) - getTerminalInfo(&result, ppid); - - if(result.terminalProcessName.length == 0) - getTerminalFromEnv(&result); - - ffStrbufInit(&result.terminalVersion); - fftsGetTerminalVersion(&result.terminalProcessName, &result.terminalExe, &result.terminalVersion); + setShellInfoDetails(&result); + + return &result; +} + +const FFTerminalResult* ffDetectTerminal(void) +{ + static FFTerminalResult result; + static bool init = false; + if(init) + return &result; + init = true; + + ffStrbufInit(&result.processName); + ffStrbufInitA(&result.exe, 128); + result.exeName = ""; + ffStrbufInit(&result.prettyName); + ffStrbufInit(&result.version); + result.pid = 0; + result.ppid = 0; + + uint32_t ppid = ffDetectShell()->ppid; + if(ppid) + getTerminalInfo(&result, ppid); + + if(result.processName.length == 0) + getTerminalFromEnv(&result); + + setTerminalInfoDetails(&result); + + fftsGetTerminalVersion(&result.processName, &result.exe, &result.version); return &result; } diff --git a/src/detection/vulkan/vulkan.c b/src/detection/vulkan/vulkan.c index 70d6c5590..5492882c7 100644 --- a/src/detection/vulkan/vulkan.c +++ b/src/detection/vulkan/vulkan.c @@ -1,5 +1,4 @@ #include "fastfetch.h" -#include "common/thread.h" #include "detection/gpu/gpu.h" #include "detection/vulkan/vulkan.h" @@ -8,7 +7,7 @@ #include "common/io/io.h" #include "common/parsing.h" #include "util/stringUtils.h" -#include "util/mallocHelper.h" + #include #include @@ -211,14 +210,8 @@ static const char* detectVulkan(FFVulkanResult* result) continue; } - //If the highest device version is lower than the instance version, use it as our vulkan version - //otherwise the instance version is the vulkan version - if(ffVersionCompare(&instanceVersion, &maxDeviceApiVersion) > 0) - ffVersionToPretty(&maxDeviceApiVersion, &result->apiVersion); - else - ffVersionToPretty(&instanceVersion, &result->apiVersion); - - //Use the highest device conformace version as our conformance version + ffVersionToPretty(&instanceVersion, &result->instanceVersion); + ffVersionToPretty(&maxDeviceApiVersion, &result->apiVersion); ffVersionToPretty(&maxDeviceConformanceVersion, &result->conformanceVersion); ffvkDestroyInstance(vkInstance, NULL); @@ -236,6 +229,7 @@ FFVulkanResult* ffDetectVulkan(void) ffStrbufInit(&result.driver); ffStrbufInit(&result.apiVersion); ffStrbufInit(&result.conformanceVersion); + ffStrbufInit(&result.instanceVersion); ffListInit(&result.gpus, sizeof(FFGPUResult)); #ifdef FF_HAVE_VULKAN diff --git a/src/detection/vulkan/vulkan.h b/src/detection/vulkan/vulkan.h index 9ac051cbc..5b6d7832e 100644 --- a/src/detection/vulkan/vulkan.h +++ b/src/detection/vulkan/vulkan.h @@ -7,6 +7,7 @@ typedef struct FFVulkanResult FFstrbuf driver; FFstrbuf apiVersion; FFstrbuf conformanceVersion; + FFstrbuf instanceVersion; FFlist gpus; //List of FFGPUResult, see detection/gpu/gpu.h const char* error; } FFVulkanResult; diff --git a/src/detection/wallpaper/wallpaper_apple.c b/src/detection/wallpaper/wallpaper_apple.c deleted file mode 100644 index 87691abb4..000000000 --- a/src/detection/wallpaper/wallpaper_apple.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "wallpaper.h" -#include "common/settings.h" -#include "util/apple/osascript.h" - -const char* ffDetectWallpaper(FFstrbuf* result) -{ - // https://stackoverflow.com/questions/301215/getting-desktop-background-on-mac - - #ifdef FF_HAVE_SQLITE3 - - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&instance.state.platform.homeDir); - ffStrbufAppendS(&path, "Library/Application Support/Dock/desktoppicture.db"); - if (ffSettingsGetSQLite3String(path.chars, - "SELECT value\n" - "FROM preferences\n" - "JOIN data ON preferences.data_id=data.ROWID\n" - "JOIN pictures ON preferences.picture_id=pictures.ROWID\n" - "JOIN displays ON pictures.display_id=displays.ROWID\n" - "JOIN spaces ON pictures.space_id=spaces.ROWID\n" - "WHERE display_id=1 AND space_id=1 AND key=1", result) - ) - return NULL; - - #endif - - if (!ffOsascript("tell application \"Finder\" to get POSIX path of (get desktop picture as alias)", result)) - return "ffOsascript(get desktop picture) failed"; - - return NULL; -} diff --git a/src/detection/wallpaper/wallpaper_apple.m b/src/detection/wallpaper/wallpaper_apple.m new file mode 100644 index 000000000..eb330dda1 --- /dev/null +++ b/src/detection/wallpaper/wallpaper_apple.m @@ -0,0 +1,72 @@ +#include "wallpaper.h" +#include "common/settings.h" +#include "util/apple/osascript.h" + +#import + +const char* ffDetectWallpaper(FFstrbuf* result) +{ + { + // For Sonoma + // https://github.com/JohnCoates/Aerial/issues/1332 + NSError* error; + NSString* fileName = [NSString stringWithFormat:@"file://%s/Library/Application Support/com.apple.wallpaper/Store/Index.plist", instance.state.platform.homeDir.chars]; + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:fileName] + error:&error]; + if (!error) + { + NSArray* choices = [dict valueForKeyPath:@"SystemDefault.Desktop.Content.Choices"]; + if (choices.count > 0) + { + NSDictionary* choice = [choices objectAtIndex:0]; + NSArray* files = [choice valueForKey:@"Files"]; + if (files.count > 0) + { + NSString* file = [[files objectAtIndex: 0] valueForKey: @"relative"]; + ffStrbufAppendS(result, [NSURL URLWithString:file].path.UTF8String); + } + else + { + NSString* provider = [choice valueForKey:@"Provider"]; + NSString* builtinPrefix = @"com.apple.wallpaper.choice."; + if ([provider hasPrefix:builtinPrefix]) + provider = [provider substringFromIndex:builtinPrefix.length]; + if ([provider isEqualToString:@"sonoma"]) + ffStrbufSetStatic(result, "macOS Sonoma"); + else if ([provider isEqualToString:@"aerials"]) // Most builtin aerial wallpapers are private + ffStrbufSetStatic(result, "Built-in aerial photography"); + else + ffStrbufAppendF(result, "Built-in %s wallpaper", provider.UTF8String); + } + } + if (result->length > 0) + return NULL; + } + } + + #ifdef FF_HAVE_SQLITE3 + + { + // For Ventura + // https://stackoverflow.com/questions/301215/getting-desktop-background-on-mac + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateCopy(&instance.state.platform.homeDir); + ffStrbufAppendS(&path, "Library/Application Support/Dock/desktoppicture.db"); + if (ffSettingsGetSQLite3String(path.chars, + "SELECT value\n" + "FROM preferences\n" + "JOIN data ON preferences.data_id=data.ROWID\n" + "JOIN pictures ON preferences.picture_id=pictures.ROWID\n" + "JOIN displays ON pictures.display_id=displays.ROWID\n" + "JOIN spaces ON pictures.space_id=spaces.ROWID\n" + "WHERE display_id=1 AND space_id=1 AND key=1", result) + ) + return NULL; + } + + #endif + + if (ffOsascript("tell application \"Finder\" to get POSIX path of (get desktop picture as alias)", result)) + return NULL; + + return "All detection methods failed"; +} diff --git a/src/detection/wifi/wifi_apple.m b/src/detection/wifi/wifi_apple.m index 4402b0170..64d90ccb2 100644 --- a/src/detection/wifi/wifi_apple.m +++ b/src/detection/wifi/wifi_apple.m @@ -17,7 +17,7 @@ static NSDictionary* getWifiInfoByApple80211(NSString* ifName) struct Apple80211* handle = NULL; if (Apple80211Open(&handle) < 0) return NULL; - if (Apple80211BindToInterface(handle, (CFStringRef)ifName) < 0) return NULL; + if (Apple80211BindToInterface(handle, (__bridge CFStringRef)ifName) < 0) return NULL; CFDictionaryRef result; if (Apple80211GetInfoCopy(handle, &result) < 0) return NULL; Apple80211Close(handle); diff --git a/src/detection/wifi/wifi_linux.c b/src/detection/wifi/wifi_linux.c index 5c500f50d..34af04fd4 100644 --- a/src/detection/wifi/wifi_linux.c +++ b/src/detection/wifi/wifi_linux.c @@ -87,43 +87,43 @@ static const char* detectWifiWithLibnm(FFlist* result) switch(state) { case NM_DEVICE_STATE_UNKNOWN: - ffStrbufAppendS(&item->inf.status, "Unknown"); + ffStrbufSetStatic(&item->inf.status, "Unknown"); break; case NM_DEVICE_STATE_UNMANAGED: - ffStrbufAppendS(&item->inf.status, "Unmanaged"); + ffStrbufSetStatic(&item->inf.status, "Unmanaged"); break; case NM_DEVICE_STATE_UNAVAILABLE: - ffStrbufAppendS(&item->inf.status, "Unavailable"); + ffStrbufSetStatic(&item->inf.status, "Unavailable"); break; case NM_DEVICE_STATE_DISCONNECTED: - ffStrbufAppendS(&item->inf.status, "Disconnected"); + ffStrbufSetStatic(&item->inf.status, "Disconnected"); break; case NM_DEVICE_STATE_PREPARE: - ffStrbufAppendS(&item->inf.status, "Prepare"); + ffStrbufSetStatic(&item->inf.status, "Prepare"); break; case NM_DEVICE_STATE_CONFIG: - ffStrbufAppendS(&item->inf.status, "Config"); + ffStrbufSetStatic(&item->inf.status, "Config"); break; case NM_DEVICE_STATE_NEED_AUTH: - ffStrbufAppendS(&item->inf.status, "Need auth"); + ffStrbufSetStatic(&item->inf.status, "Need auth"); break; case NM_DEVICE_STATE_IP_CONFIG: - ffStrbufAppendS(&item->inf.status, "IP config"); + ffStrbufSetStatic(&item->inf.status, "IP config"); break; case NM_DEVICE_STATE_IP_CHECK: - ffStrbufAppendS(&item->inf.status, "IP check"); + ffStrbufSetStatic(&item->inf.status, "IP check"); break; case NM_DEVICE_STATE_SECONDARIES: - ffStrbufAppendS(&item->inf.status, "Secondaries"); + ffStrbufSetStatic(&item->inf.status, "Secondaries"); break; case NM_DEVICE_STATE_ACTIVATED: - ffStrbufAppendS(&item->inf.status, "Activated"); + ffStrbufSetStatic(&item->inf.status, "Activated"); break; case NM_DEVICE_STATE_DEACTIVATING: - ffStrbufAppendS(&item->inf.status, "Deactivating"); + ffStrbufSetStatic(&item->inf.status, "Deactivating"); break; case NM_DEVICE_STATE_FAILED: - ffStrbufAppendS(&item->inf.status, "Failed"); + ffStrbufSetStatic(&item->inf.status, "Failed"); break; } if(state != NM_DEVICE_STATE_ACTIVATED) @@ -133,6 +133,8 @@ static const char* detectWifiWithLibnm(FFlist* result) if (!ap) continue; + ffStrbufSetStatic(&item->conn.status, "Connected"); + GBytes* activeSsid = ffnm_access_point_get_ssid(ap); if (activeSsid) { @@ -161,13 +163,13 @@ static const char* detectWifiWithLibnm(FFlist* result) if(ffParsePropLines(output.chars, "tx bitrate: ", &item->conn.protocol)) { if(ffStrbufContainS(&item->conn.protocol, " HE-MCS ")) - ffStrbufSetS(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); + ffStrbufSetStatic(&item->conn.protocol, "802.11ax (Wi-Fi 6)"); else if(ffStrbufContainS(&item->conn.protocol, " VHT-MCS ")) - ffStrbufSetS(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); + ffStrbufSetStatic(&item->conn.protocol, "802.11ac (Wi-Fi 5)"); else if(ffStrbufContainS(&item->conn.protocol, " MCS ")) - ffStrbufSetS(&item->conn.protocol, "802.11n (Wi-Fi 4)"); + ffStrbufSetStatic(&item->conn.protocol, "802.11n (Wi-Fi 4)"); else - ffStrbufSetS(&item->conn.protocol, "802.11a/b/g"); + ffStrbufClear(&item->conn.protocol); sscanf(item->conn.protocol.chars, "%lf", &item->conn.txRate); } diff --git a/src/detection/wm/wm_windows.cpp b/src/detection/wm/wm_windows.cpp index 941052830..583cf2149 100644 --- a/src/detection/wm/wm_windows.cpp +++ b/src/detection/wm/wm_windows.cpp @@ -25,7 +25,7 @@ private: extern "C" const char* ffDetectWMPlugin(FFstrbuf* pluginName) { - DWORD pid = ffDetectTerminalShell()->terminalPid; // Whatever GUI program + DWORD pid = ffDetectTerminal()->pid; // Whatever GUI program if (pid == 0) return "Unable to find a GUI program"; FF_AUTO_CLOSE_FD HANDLE snapshot = NULL; diff --git a/src/logo/logo.c b/src/logo/logo.c index 6fa0610a8..708bc6c7f 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -498,14 +498,14 @@ void ffLogoPrint(void) //Make sure the logo path is set correctly. updateLogoPath(); - const FFTerminalShellResult* terminalShell = ffDetectTerminalShell(); + const FFTerminalResult* terminal = ffDetectTerminal(); //Terminal emulators that support kitty graphics protocol. bool supportsKitty = - ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "kitty") == 0 || - ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0 || - ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wezterm") == 0 || - ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wayst") == 0; + ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") || + ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") || + ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") || + ffStrbufIgnCaseEqualS(&terminal->processName, "wayst"); //Try to load the logo as an image. If it succeeds, print it and return. if(logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false)) diff --git a/src/modules/battery/battery.c b/src/modules/battery/battery.c index 0946ad0c0..0a2bd8afd 100644 --- a/src/modules/battery/battery.c +++ b/src/modules/battery/battery.c @@ -6,7 +6,7 @@ #include "modules/battery/battery.h" #include "util/stringUtils.h" -#define FF_BATTERY_NUM_FORMAT_ARGS 7 +#define FF_BATTERY_NUM_FORMAT_ARGS 8 static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uint8_t index) { @@ -71,6 +71,8 @@ static void printBattery(FFBatteryOptions* options, FFBatteryResult* result, uin {FF_FORMAT_ARG_TYPE_STRBUF, &result->status}, {FF_FORMAT_ARG_TYPE_DOUBLE, &result->temperature}, {FF_FORMAT_ARG_TYPE_UINT, &result->cycleCount}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->serial}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->manufactureDate}, }); } } @@ -96,6 +98,8 @@ void ffPrintBattery(FFBatteryOptions* options) ffStrbufDestroy(&result->modelName); ffStrbufDestroy(&result->technology); ffStrbufDestroy(&result->status); + ffStrbufDestroy(&result->serial); + ffStrbufDestroy(&result->manufactureDate); } if(results.length == 0) ffPrintError(FF_BATTERY_MODULE_NAME, 0, &options->moduleArgs, "No batteries found"); @@ -115,14 +119,6 @@ bool ffParseBatteryCommandOptions(FFBatteryOptions* options, const char* key, co return true; } - #ifdef __linux__ - if (ffStrEqualsIgnCase(subKey, "dir")) - { - ffOptionParseString(key, value, &options->dir); - return true; - } - #endif - #ifdef _WIN32 if (ffStrEqualsIgnCase(subKey, "use-setup-api")) { @@ -147,14 +143,6 @@ void ffParseBatteryJsonObject(FFBatteryOptions* options, yyjson_val* module) if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) continue; - #ifdef __linux__ - if (ffStrEqualsIgnCase(key, "dir")) - { - ffStrbufSetS(&options->dir, yyjson_get_str(val)); - continue; - } - #endif - #ifdef _WIN32 if (ffStrEqualsIgnCase(key, "useSetupApi")) { @@ -180,11 +168,6 @@ void ffGenerateBatteryJsonConfig(FFBatteryOptions* options, yyjson_mut_doc* doc, ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); - #ifdef __linux__ - if (!ffStrbufEqual(&defaultOptions.dir, &options->dir)) - yyjson_mut_obj_add_strbuf(doc, module, "dir", &options->dir); - #endif - #ifdef _WIN32 if (defaultOptions.useSetupApi != options->useSetupApi) yyjson_mut_obj_add_bool(doc, module, "useSetupApi", options->useSetupApi); @@ -212,9 +195,11 @@ void ffGenerateBatteryJsonResult(FFBatteryOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); yyjson_mut_obj_add_real(doc, obj, "capacity", battery->capacity); yyjson_mut_obj_add_strbuf(doc, obj, "manufacturer", &battery->manufacturer); + yyjson_mut_obj_add_strbuf(doc, obj, "manufactureDate", &battery->manufactureDate); yyjson_mut_obj_add_strbuf(doc, obj, "modelName", &battery->modelName); yyjson_mut_obj_add_strbuf(doc, obj, "status", &battery->status); yyjson_mut_obj_add_strbuf(doc, obj, "technology", &battery->technology); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &battery->serial); yyjson_mut_obj_add_real(doc, obj, "temperature", battery->temperature); yyjson_mut_obj_add_uint(doc, obj, "cycleCount", battery->cycleCount); } @@ -222,9 +207,11 @@ void ffGenerateBatteryJsonResult(FFBatteryOptions* options, yyjson_mut_doc* doc, FF_LIST_FOR_EACH(FFBatteryResult, battery, results) { ffStrbufDestroy(&battery->manufacturer); + ffStrbufDestroy(&battery->manufactureDate); ffStrbufDestroy(&battery->modelName); ffStrbufDestroy(&battery->technology); ffStrbufDestroy(&battery->status); + ffStrbufDestroy(&battery->serial); } } @@ -238,6 +225,8 @@ void ffPrintBatteryHelpFormat(void) "Battery status", "Battery temperature", "Battery cycle count", + "Battery serial number", + "Battery manufactor date", }); } @@ -257,9 +246,7 @@ void ffInitBatteryOptions(FFBatteryOptions* options) ffOptionInitModuleArg(&options->moduleArgs); options->temp = false; - #ifdef __linux__ - ffStrbufInitStatic(&options->dir, "/sys/class/power_supply/"); - #elif defined(_WIN32) + #ifdef _WIN32 options->useSetupApi = false; #endif } @@ -267,8 +254,4 @@ void ffInitBatteryOptions(FFBatteryOptions* options) void ffDestroyBatteryOptions(FFBatteryOptions* options) { ffOptionDestroyModuleArg(&options->moduleArgs); - - #ifdef __linux__ - ffStrbufDestroy(&options->dir); - #endif } diff --git a/src/modules/battery/option.h b/src/modules/battery/option.h index 89f05bab4..401816ae7 100644 --- a/src/modules/battery/option.h +++ b/src/modules/battery/option.h @@ -11,9 +11,7 @@ typedef struct FFBatteryOptions bool temp; - #ifdef __linux__ - FFstrbuf dir; - #elif defined(_WIN32) + #ifdef _WIN32 bool useSetupApi; #endif } FFBatteryOptions; diff --git a/src/modules/board/board.c b/src/modules/board/board.c index 0cb3bcfaa..81a663609 100644 --- a/src/modules/board/board.c +++ b/src/modules/board/board.c @@ -12,8 +12,9 @@ void ffPrintBoard(FFBoardOptions* options) ffStrbufInit(&result.name); ffStrbufInit(&result.vendor); ffStrbufInit(&result.version); - const char* error = ffDetectBoard(&result); + ffStrbufInit(&result.serial); + const char* error = ffDetectBoard(&result); if(error) { ffPrintError(FF_BOARD_MODULE_NAME, 0, &options->moduleArgs, "%s", error); @@ -40,6 +41,7 @@ void ffPrintBoard(FFBoardOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &result.name}, {FF_FORMAT_ARG_TYPE_STRBUF, &result.vendor}, {FF_FORMAT_ARG_TYPE_STRBUF, &result.version}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.serial}, }); } @@ -47,6 +49,7 @@ exit: ffStrbufDestroy(&result.name); ffStrbufDestroy(&result.vendor); ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.serial); } bool ffParseBoardCommandOptions(FFBoardOptions* options, const char* key, const char* value) @@ -90,6 +93,7 @@ void ffGenerateBoardJsonResult(FF_MAYBE_UNUSED FFBoardOptions* options, yyjson_m ffStrbufInit(&board.name); ffStrbufInit(&board.vendor); ffStrbufInit(&board.version); + ffStrbufInit(&board.serial); const char* error = ffDetectBoard(&board); @@ -109,11 +113,13 @@ void ffGenerateBoardJsonResult(FF_MAYBE_UNUSED FFBoardOptions* options, yyjson_m yyjson_mut_obj_add_strbuf(doc, obj, "name", &board.name); yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &board.vendor); yyjson_mut_obj_add_strbuf(doc, obj, "version", &board.version); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &board.serial); exit: ffStrbufDestroy(&board.name); ffStrbufDestroy(&board.vendor); ffStrbufDestroy(&board.version); + ffStrbufDestroy(&board.serial); } void ffPrintBoardHelpFormat(void) @@ -121,7 +127,8 @@ void ffPrintBoardHelpFormat(void) ffPrintModuleFormatHelp(FF_BOARD_MODULE_NAME, "{1} ({3})", FF_BOARD_NUM_FORMAT_ARGS, (const char* []) { "board name", "board vendor", - "board version" + "board version", + "board serial number", }); } diff --git a/src/modules/chassis/chassis.c b/src/modules/chassis/chassis.c index ad0b19570..4966053a1 100644 --- a/src/modules/chassis/chassis.c +++ b/src/modules/chassis/chassis.c @@ -12,8 +12,9 @@ void ffPrintChassis(FFChassisOptions* options) ffStrbufInit(&result.type); ffStrbufInit(&result.vendor); ffStrbufInit(&result.version); + ffStrbufInit(&result.serial); - const char* error = ffDetectChassis(&result, options); + const char* error = ffDetectChassis(&result); if(error) { @@ -41,6 +42,7 @@ void ffPrintChassis(FFChassisOptions* options) {FF_FORMAT_ARG_TYPE_STRBUF, &result.type}, {FF_FORMAT_ARG_TYPE_STRBUF, &result.vendor}, {FF_FORMAT_ARG_TYPE_STRBUF, &result.version}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.serial}, }); } @@ -48,6 +50,7 @@ exit: ffStrbufDestroy(&result.type); ffStrbufDestroy(&result.vendor); ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.serial); } bool ffParseChassisCommandOptions(FFChassisOptions* options, const char* key, const char* value) @@ -57,14 +60,6 @@ bool ffParseChassisCommandOptions(FFChassisOptions* options, const char* key, co if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) return true; - #ifdef _WIN32 - if (ffStrEqualsIgnCase(subKey, "use-wmi")) - { - options->useWmi = ffOptionParseBoolean(value); - return true; - } - #endif - return false; } @@ -81,14 +76,6 @@ void ffParseChassisJsonObject(FFChassisOptions* options, yyjson_val* module) if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) continue; - #ifdef _WIN32 - if (ffStrEqualsIgnCase(key, "useWmi")) - { - options->useWmi = yyjson_get_bool(val); - continue; - } - #endif - ffPrintError(FF_CHASSIS_MODULE_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); } } @@ -99,11 +86,6 @@ void ffGenerateChassisJsonConfig(FFChassisOptions* options, yyjson_mut_doc* doc, ffInitChassisOptions(&defaultOptions); ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); - - #ifdef _WIN32 - if (options->useWmi != defaultOptions.useWmi) - yyjson_mut_obj_add_bool(doc, module, "useWmi", options->useWmi); - #endif } void ffGenerateChassisJsonResult(FF_MAYBE_UNUSED FFChassisOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) @@ -112,8 +94,9 @@ void ffGenerateChassisJsonResult(FF_MAYBE_UNUSED FFChassisOptions* options, yyjs ffStrbufInit(&result.type); ffStrbufInit(&result.vendor); ffStrbufInit(&result.version); + ffStrbufInit(&result.serial); - const char* error = ffDetectChassis(&result, options); + const char* error = ffDetectChassis(&result); if (error) { @@ -131,11 +114,13 @@ void ffGenerateChassisJsonResult(FF_MAYBE_UNUSED FFChassisOptions* options, yyjs yyjson_mut_obj_add_strbuf(doc, obj, "type", &result.type); yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &result.vendor); yyjson_mut_obj_add_strbuf(doc, obj, "version", &result.version); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &result.serial); exit: ffStrbufDestroy(&result.type); ffStrbufDestroy(&result.vendor); ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.serial); } void ffPrintChassisHelpFormat(void) @@ -143,7 +128,8 @@ void ffPrintChassisHelpFormat(void) ffPrintModuleFormatHelp(FF_CHASSIS_MODULE_NAME, "{1}", FF_CHASSIS_NUM_FORMAT_ARGS, (const char* []) { "chassis type", "chassis vendor", - "chassis version" + "chassis version", + "chassis serial number", }); } @@ -161,10 +147,6 @@ void ffInitChassisOptions(FFChassisOptions* options) ffGenerateChassisJsonConfig ); ffOptionInitModuleArg(&options->moduleArgs); - - #ifdef _WIN32 - options->useWmi = false; - #endif } void ffDestroyChassisOptions(FFChassisOptions* options) diff --git a/src/modules/chassis/option.h b/src/modules/chassis/option.h index 25d7cee65..de6338cd5 100644 --- a/src/modules/chassis/option.h +++ b/src/modules/chassis/option.h @@ -8,8 +8,4 @@ typedef struct FFChassisOptions { FFModuleBaseInfo moduleInfo; FFModuleArgs moduleArgs; - - #ifdef _WIN32 - bool useWmi; - #endif } FFChassisOptions; diff --git a/src/modules/gamepad/gamepad.c b/src/modules/gamepad/gamepad.c index f48be7bf9..a74d603cc 100644 --- a/src/modules/gamepad/gamepad.c +++ b/src/modules/gamepad/gamepad.c @@ -1,3 +1,4 @@ +#include "common/bar.h" #include "common/printing.h" #include "common/jsonconfig.h" #include "detection/gamepad/gamepad.h" @@ -10,14 +11,21 @@ static void printDevice(FFGamepadOptions* options, const FFGamepadDevice* device { if(options->moduleArgs.outputFormat.length == 0) { + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateCopy(&device->name); ffPrintLogoAndKey(FF_GAMEPAD_MODULE_NAME, index, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - ffStrbufPutTo(&device->name, stdout); + if (device->battery > 0 && device->battery <= 100) + { + ffStrbufAppendC(&buffer, ' '); + ffAppendPercentNum(&buffer, device->battery, 51, 21, true); + } + ffStrbufPutTo(&buffer, stdout); } else { ffPrintFormat(FF_GAMEPAD_MODULE_NAME, index, &options->moduleArgs, FF_GAMEPAD_NUM_FORMAT_ARGS, (FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &device->name}, - {FF_FORMAT_ARG_TYPE_STRBUF, &device->identifier}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->serial}, + {FF_FORMAT_ARG_TYPE_UINT8, &device->battery}, }); } } @@ -44,7 +52,7 @@ void ffPrintGamepad(FFGamepadOptions* options) FF_LIST_FOR_EACH(FFGamepadDevice, device, result) { printDevice(options, device, result.length > 1 ? ++index : 0); - ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->serial); ffStrbufDestroy(&device->name); } } @@ -106,13 +114,13 @@ void ffGenerateGamepadJsonResult(FF_MAYBE_UNUSED FFGamepadOptions* options, yyjs FF_LIST_FOR_EACH(FFGamepadDevice, device, result) { yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); - yyjson_mut_obj_add_strbuf(doc, obj, "identifier", &device->identifier); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &device->serial); yyjson_mut_obj_add_strbuf(doc, obj, "name", &device->name); } FF_LIST_FOR_EACH(FFGamepadDevice, device, result) { - ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->serial); ffStrbufDestroy(&device->name); } } @@ -121,7 +129,8 @@ void ffPrintGamepadHelpFormat(void) { ffPrintModuleFormatHelp(FF_GAMEPAD_MODULE_NAME, "{1}", FF_GAMEPAD_NUM_FORMAT_ARGS, (const char* []) { "Name", - "Identifier" + "Serial number", + "Battery", }); } diff --git a/src/modules/host/host.c b/src/modules/host/host.c index aeafd7118..08f11c8e7 100644 --- a/src/modules/host/host.c +++ b/src/modules/host/host.c @@ -4,25 +4,27 @@ #include "modules/host/host.h" #include "util/stringUtils.h" -#define FF_HOST_NUM_FORMAT_ARGS 5 +#define FF_HOST_NUM_FORMAT_ARGS 7 void ffPrintHost(FFHostOptions* options) { FFHostResult host; - ffStrbufInit(&host.productFamily); - ffStrbufInit(&host.productName); - ffStrbufInit(&host.productVersion); - ffStrbufInit(&host.productSku); - ffStrbufInit(&host.sysVendor); - const char* error = ffDetectHost(&host); + ffStrbufInit(&host.family); + ffStrbufInit(&host.name); + ffStrbufInit(&host.version); + ffStrbufInit(&host.sku); + ffStrbufInit(&host.serial); + ffStrbufInit(&host.uuid); + ffStrbufInit(&host.vendor); + const char* error = ffDetectHost(&host); if(error) { ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, "%s", error); goto exit; } - if(host.productFamily.length == 0 && host.productName.length == 0) + if(host.family.length == 0 && host.name.length == 0) { ffPrintError(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, "neither product_family nor product_name is set by O.E.M."); goto exit; @@ -34,33 +36,37 @@ void ffPrintHost(FFHostOptions* options) FF_STRBUF_AUTO_DESTROY output = ffStrbufCreate(); - if(host.productName.length > 0) - ffStrbufAppend(&output, &host.productName); + if(host.name.length > 0) + ffStrbufAppend(&output, &host.name); else - ffStrbufAppend(&output, &host.productFamily); + ffStrbufAppend(&output, &host.family); - if(host.productVersion.length > 0) - ffStrbufAppendF(&output, " (%s)", host.productVersion.chars); + if(host.version.length > 0) + ffStrbufAppendF(&output, " (%s)", host.version.chars); ffStrbufPutTo(&output, stdout); } else { ffPrintFormat(FF_HOST_MODULE_NAME, 0, &options->moduleArgs, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &host.productFamily}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host.productName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host.productVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host.productSku}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host.sysVendor} + {FF_FORMAT_ARG_TYPE_STRBUF, &host.family}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host.name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host.version}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host.sku}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host.vendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host.serial}, + {FF_FORMAT_ARG_TYPE_STRBUF, &host.uuid}, }); } exit: - ffStrbufDestroy(&host.productFamily); - ffStrbufDestroy(&host.productName); - ffStrbufDestroy(&host.productVersion); - ffStrbufDestroy(&host.productSku); - ffStrbufDestroy(&host.sysVendor); + ffStrbufDestroy(&host.family); + ffStrbufDestroy(&host.name); + ffStrbufDestroy(&host.version); + ffStrbufDestroy(&host.sku); + ffStrbufDestroy(&host.serial); + ffStrbufDestroy(&host.uuid); + ffStrbufDestroy(&host.vendor); } bool ffParseHostCommandOptions(FFHostOptions* options, const char* key, const char* value) @@ -101,38 +107,44 @@ void ffGenerateHostJsonConfig(FFHostOptions* options, yyjson_mut_doc* doc, yyjso void ffGenerateHostJsonResult(FF_MAYBE_UNUSED FFHostOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { FFHostResult host; - ffStrbufInit(&host.productFamily); - ffStrbufInit(&host.productName); - ffStrbufInit(&host.productVersion); - ffStrbufInit(&host.productSku); - ffStrbufInit(&host.sysVendor); - const char* error = ffDetectHost(&host); + ffStrbufInit(&host.family); + ffStrbufInit(&host.name); + ffStrbufInit(&host.version); + ffStrbufInit(&host.sku); + ffStrbufInit(&host.serial); + ffStrbufInit(&host.uuid); + ffStrbufInit(&host.vendor); + const char* error = ffDetectHost(&host); if (error) { yyjson_mut_obj_add_str(doc, module, "error", error); goto exit; } - if (host.productFamily.length == 0 && host.productName.length == 0) + if (host.family.length == 0 && host.name.length == 0) { yyjson_mut_obj_add_str(doc, module, "error", "neither product_family nor product_name is set by O.E.M."); goto exit; } yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); - yyjson_mut_obj_add_strbuf(doc, obj, "family", &host.productFamily); - yyjson_mut_obj_add_strbuf(doc, obj, "name", &host.productName); - yyjson_mut_obj_add_strbuf(doc, obj, "version", &host.productVersion); - yyjson_mut_obj_add_strbuf(doc, obj, "sku", &host.productSku); - yyjson_mut_obj_add_strbuf(doc, obj, "sysVender", &host.sysVendor); + yyjson_mut_obj_add_strbuf(doc, obj, "family", &host.family); + yyjson_mut_obj_add_strbuf(doc, obj, "name", &host.name); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &host.version); + yyjson_mut_obj_add_strbuf(doc, obj, "sku", &host.sku); + yyjson_mut_obj_add_strbuf(doc, obj, "vendor", &host.vendor); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &host.serial); + yyjson_mut_obj_add_strbuf(doc, obj, "uuid", &host.uuid); exit: - ffStrbufDestroy(&host.productFamily); - ffStrbufDestroy(&host.productName); - ffStrbufDestroy(&host.productVersion); - ffStrbufDestroy(&host.productSku); - ffStrbufDestroy(&host.sysVendor); + ffStrbufDestroy(&host.family); + ffStrbufDestroy(&host.name); + ffStrbufDestroy(&host.version); + ffStrbufDestroy(&host.sku); + ffStrbufDestroy(&host.serial); + ffStrbufDestroy(&host.uuid); + ffStrbufDestroy(&host.vendor); } void ffPrintHostHelpFormat(void) @@ -142,7 +154,9 @@ void ffPrintHostHelpFormat(void) "product name", "product version", "product sku", - "sys vendor" + "product vendor", + "product serial number", + "product uuid", }); } diff --git a/src/modules/monitor/monitor.c b/src/modules/monitor/monitor.c index 35ad8a6c4..776d4fd18 100644 --- a/src/modules/monitor/monitor.c +++ b/src/modules/monitor/monitor.c @@ -59,6 +59,15 @@ void ffPrintMonitor(FFMonitorOptions* options) } else { + char buf[32]; + if (display->serial) + { + const uint8_t* nums = (uint8_t*) &display->serial; + snprintf(buf, sizeof(buf), "%2X-%2X-%2X-%2X", nums[0], nums[1], nums[2], nums[3]); + } + else + buf[0] = '\0'; + ffPrintFormatString(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, FF_MONITOR_NUM_FORMAT_ARGS, (FFformatarg[]) { {FF_FORMAT_ARG_TYPE_STRBUF, &display->name}, {FF_FORMAT_ARG_TYPE_UINT, &display->width}, @@ -67,6 +76,9 @@ void ffPrintMonitor(FFMonitorOptions* options) {FF_FORMAT_ARG_TYPE_UINT, &display->physicalHeight}, {FF_FORMAT_ARG_TYPE_DOUBLE, &inch}, {FF_FORMAT_ARG_TYPE_DOUBLE, &ppi}, + {FF_FORMAT_ARG_TYPE_UINT16, &display->manufactureYear}, + {FF_FORMAT_ARG_TYPE_UINT16, &display->manufactureWeek}, + {FF_FORMAT_ARG_TYPE_STRING, buf}, }); } @@ -131,11 +143,31 @@ void ffGenerateMonitorJsonResult(FF_MAYBE_UNUSED FFMonitorOptions* options, yyjs { yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr); yyjson_mut_obj_add_bool(doc, obj, "hdrCompatible", item->hdrCompatible); - yyjson_mut_obj_add_uint(doc, obj, "width", item->width); - yyjson_mut_obj_add_uint(doc, obj, "height", item->height); yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); - yyjson_mut_obj_add_uint(doc, obj, "physicalHeight", item->physicalHeight); - yyjson_mut_obj_add_uint(doc, obj, "physicalWidth", item->physicalWidth); + + yyjson_mut_val* resolution = yyjson_mut_obj_add_obj(doc, obj, "resolution"); + yyjson_mut_obj_add_uint(doc, resolution, "width", item->width); + yyjson_mut_obj_add_uint(doc, resolution, "height", item->height); + + yyjson_mut_val* physical = yyjson_mut_obj_add_obj(doc, obj, "physical"); + yyjson_mut_obj_add_uint(doc, physical, "height", item->physicalHeight); + yyjson_mut_obj_add_uint(doc, physical, "width", item->physicalWidth); + + if (item->manufactureYear) + { + yyjson_mut_val* manufactureDate = yyjson_mut_obj_add_obj(doc, obj, "manufactureDate"); + yyjson_mut_obj_add_uint(doc, manufactureDate, "year", item->manufactureYear); + yyjson_mut_obj_add_uint(doc, manufactureDate, "week", item->manufactureWeek); + } + else + { + yyjson_mut_obj_add_null(doc, obj, "manufactureDate"); + } + + if (item->serial) + yyjson_mut_obj_add_uint(doc, obj, "serial", item->serial); + else + yyjson_mut_obj_add_null(doc, obj, "serial"); } } @@ -154,7 +186,9 @@ void ffPrintMonitorHelpFormat(void) "Display physical width in millimeters", "Display physical height in millimeters", "Display physical diagonal length in inches", - "Display physical pixels per inch (PPI)" + "Year of manufacturing", + "Nth week of manufacturing in the year", + "Serial number", }); } diff --git a/src/modules/poweradapter/poweradapter.c b/src/modules/poweradapter/poweradapter.c index 3ce00a771..23cd10732 100644 --- a/src/modules/poweradapter/poweradapter.c +++ b/src/modules/poweradapter/poweradapter.c @@ -5,13 +5,13 @@ #include "util/stringUtils.h" #define FF_POWERADAPTER_DISPLAY_NAME "Power Adapter" -#define FF_POWERADAPTER_NUM_FORMAT_ARGS 5 +#define FF_POWERADAPTER_NUM_FORMAT_ARGS 6 void ffPrintPowerAdapter(FFPowerAdapterOptions* options) { FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFPowerAdapterResult)); - const char* error = ffDetectPowerAdapterImpl(&results); + const char* error = ffDetectPowerAdapter(&results); if (error) { @@ -27,35 +27,34 @@ void ffPrintPowerAdapter(FFPowerAdapterOptions* options) { FFPowerAdapterResult* result = ffListGet(&results, i); - if(result->watts != FF_POWERADAPTER_UNSET) + if(options->moduleArgs.outputFormat.length == 0) { - if(options->moduleArgs.outputFormat.length == 0) - { - ffPrintLogoAndKey(FF_POWERADAPTER_DISPLAY_NAME, i, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + ffPrintLogoAndKey(FF_POWERADAPTER_DISPLAY_NAME, i, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - if(result->name.length > 0) - puts(result->name.chars); - else if(result->watts == FF_POWERADAPTER_NOT_CONNECTED) - puts("not connected"); - else - printf("%dW\n", result->watts); - } + if(result->name.length > 0) + puts(result->name.chars); + else if(result->watts == FF_POWERADAPTER_NOT_CONNECTED) + puts("not connected"); else - { - ffPrintFormat(FF_POWERADAPTER_DISPLAY_NAME, i, &options->moduleArgs, FF_POWERADAPTER_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_INT, &result->watts}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->name}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->description}, - }); - } + printf("%dW\n", result->watts); + } + else + { + ffPrintFormat(FF_POWERADAPTER_DISPLAY_NAME, i, &options->moduleArgs, FF_POWERADAPTER_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_INT, &result->watts}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->manufacturer}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->modelName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->description}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->serial}, + }); } ffStrbufDestroy(&result->manufacturer); ffStrbufDestroy(&result->description); ffStrbufDestroy(&result->modelName); ffStrbufDestroy(&result->name); + ffStrbufDestroy(&result->serial); } } } @@ -99,7 +98,7 @@ void ffGeneratePowerAdapterJsonResult(FF_MAYBE_UNUSED FFPowerAdapterOptions* opt { FF_LIST_AUTO_DESTROY results = ffListCreate(sizeof(FFPowerAdapterResult)); - const char* error = ffDetectPowerAdapterImpl(&results); + const char* error = ffDetectPowerAdapter(&results); if (error) { @@ -119,6 +118,7 @@ void ffGeneratePowerAdapterJsonResult(FF_MAYBE_UNUSED FFPowerAdapterOptions* opt yyjson_mut_obj_add_strbuf(doc, obj, "manufacturer", &item->manufacturer); yyjson_mut_obj_add_strbuf(doc, obj, "modelName", &item->modelName); yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name); + yyjson_mut_obj_add_strbuf(doc, obj, "serial", &item->serial); yyjson_mut_obj_add_int(doc, obj, "watts", item->watts); } } @@ -132,6 +132,7 @@ void ffPrintPowerAdapterHelpFormat(void) "PowerAdapter manufacturer", "PowerAdapter model", "PowerAdapter description" + "PowerAdapter serial number" }); } diff --git a/src/modules/shell/shell.c b/src/modules/shell/shell.c index cc8cb6170..9bb590eea 100644 --- a/src/modules/shell/shell.c +++ b/src/modules/shell/shell.c @@ -8,9 +8,9 @@ void ffPrintShell(FFShellOptions* options) { - const FFTerminalShellResult* result = ffDetectTerminalShell(); + const FFShellResult* result = ffDetectShell(); - if(result->shellProcessName.length == 0) + if(result->processName.length == 0) { ffPrintError(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, "Couldn't detect shell"); return; @@ -19,12 +19,12 @@ void ffPrintShell(FFShellOptions* options) if(options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - ffStrbufWriteTo(&result->shellPrettyName, stdout); + ffStrbufWriteTo(&result->prettyName, stdout); - if(result->shellVersion.length > 0) + if(result->version.length > 0) { putchar(' '); - ffStrbufWriteTo(&result->shellVersion, stdout); + ffStrbufWriteTo(&result->version, stdout); } putchar('\n'); @@ -32,12 +32,12 @@ void ffPrintShell(FFShellOptions* options) else { ffPrintFormat(FF_SHELL_MODULE_NAME, 0, &options->moduleArgs, FF_SHELL_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellProcessName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->shellExeName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellVersion}, - {FF_FORMAT_ARG_TYPE_UINT, &result->shellPid}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->shellPrettyName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->processName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->exe}, + {FF_FORMAT_ARG_TYPE_STRING, result->exeName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->version}, + {FF_FORMAT_ARG_TYPE_UINT, &result->pid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->prettyName}, }); } } @@ -79,20 +79,21 @@ void ffGenerateShellJsonConfig(FFShellOptions* options, yyjson_mut_doc* doc, yyj void ffGenerateShellJsonResult(FF_MAYBE_UNUSED FFShellOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { - const FFTerminalShellResult* result = ffDetectTerminalShell(); + const FFShellResult* result = ffDetectShell(); - if(result->shellProcessName.length == 0) + if(result->processName.length == 0) { yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect shell"); return; } yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); - yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->shellExe); - yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->shellExeName); - yyjson_mut_obj_add_uint(doc, obj, "pid", result->shellPid); - yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->shellProcessName); - yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->shellVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->exe); + yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->exeName); + yyjson_mut_obj_add_uint(doc, obj, "pid", result->pid); + yyjson_mut_obj_add_uint(doc, obj, "ppid", result->ppid); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->processName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->version); } void ffPrintShellHelpFormat(void) diff --git a/src/modules/terminal/terminal.c b/src/modules/terminal/terminal.c index 02347593f..d456ea771 100644 --- a/src/modules/terminal/terminal.c +++ b/src/modules/terminal/terminal.c @@ -10,9 +10,9 @@ void ffPrintTerminal(FFTerminalOptions* options) { - const FFTerminalShellResult* result = ffDetectTerminalShell(); + const FFTerminalResult* result = ffDetectTerminal(); - if(result->terminalProcessName.length == 0) + if(result->processName.length == 0) { ffPrintError(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, "Couldn't detect terminal"); return; @@ -22,20 +22,20 @@ void ffPrintTerminal(FFTerminalOptions* options) { ffPrintLogoAndKey(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - if(result->terminalVersion.length) - printf("%s %s\n", result->terminalPrettyName.chars, result->terminalVersion.chars); + if(result->version.length) + printf("%s %s\n", result->prettyName.chars, result->version.chars); else - ffStrbufPutTo(&result->terminalPrettyName, stdout); + ffStrbufPutTo(&result->prettyName, stdout); } else { ffPrintFormat(FF_TERMINAL_MODULE_NAME, 0, &options->moduleArgs, FF_TERMINAL_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalProcessName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalExe}, - {FF_FORMAT_ARG_TYPE_STRING, result->terminalExeName}, - {FF_FORMAT_ARG_TYPE_UINT, &result->terminalPid}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalPrettyName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &result->terminalVersion}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->processName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->exe}, + {FF_FORMAT_ARG_TYPE_STRING, result->exeName}, + {FF_FORMAT_ARG_TYPE_UINT, &result->pid}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->prettyName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result->version}, }); } } @@ -77,21 +77,22 @@ void ffGenerateTerminalJsonConfig(FFTerminalOptions* options, yyjson_mut_doc* do void ffGenerateTerminalJsonResult(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { - const FFTerminalShellResult* result = ffDetectTerminalShell(); + const FFTerminalResult* result = ffDetectTerminal(); - if(result->terminalProcessName.length == 0) + if(result->processName.length == 0) { yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect terminal"); return; } yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); - yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->terminalProcessName); - yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->terminalExe); - yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->terminalExeName); - yyjson_mut_obj_add_uint(doc, obj, "pid", result->terminalPid); - yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->terminalPrettyName); - yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->terminalVersion); + yyjson_mut_obj_add_strbuf(doc, obj, "processName", &result->processName); + yyjson_mut_obj_add_strbuf(doc, obj, "exe", &result->exe); + yyjson_mut_obj_add_strcpy(doc, obj, "exeName", result->exeName); + yyjson_mut_obj_add_uint(doc, obj, "pid", result->pid); + yyjson_mut_obj_add_uint(doc, obj, "ppid", result->ppid); + yyjson_mut_obj_add_strbuf(doc, obj, "prettyName", &result->prettyName); + yyjson_mut_obj_add_strbuf(doc, obj, "version", &result->version); } void ffPrintTerminalHelpFormat(void) diff --git a/src/modules/vulkan/vulkan.c b/src/modules/vulkan/vulkan.c index 94a0c32dd..24b5497da 100644 --- a/src/modules/vulkan/vulkan.c +++ b/src/modules/vulkan/vulkan.c @@ -21,6 +21,13 @@ void ffPrintVulkan(FFVulkanOptions* options) { ffPrintLogoAndKey(FF_VULKAN_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + if (vulkan->apiVersion.length == 0 && vulkan->driver.length == 0) + { + ffStrbufWriteTo(&vulkan->instanceVersion, stdout); + puts(" [Software only]"); + return; + } + if(vulkan->apiVersion.length > 0) { ffStrbufWriteTo(&vulkan->apiVersion, stdout); @@ -101,6 +108,7 @@ void ffGenerateVulkanJsonResult(FF_MAYBE_UNUSED FFVulkanOptions* options, yyjson yyjson_mut_obj_add_strbuf(doc, gpuObj, "vendor", &vulkanGpu->vendor); yyjson_mut_obj_add_strbuf(doc, gpuObj, "name", &vulkanGpu->name); yyjson_mut_obj_add_strbuf(doc, gpuObj, "driver", &vulkanGpu->driver); + yyjson_mut_obj_add_strbuf(doc, gpuObj, "platformApi", &vulkanGpu->platformApi); yyjson_mut_val* memoryObj = yyjson_mut_obj_add_obj(doc, gpuObj, "memory"); @@ -139,7 +147,8 @@ void ffPrintVulkanHelpFormat(void) ffPrintModuleFormatHelp(FF_VULKAN_MODULE_NAME, "{2} - {1}", FF_VULKAN_NUM_FORMAT_ARGS, (const char* []) { "Driver name", "API version", - "Conformance version" + "Conformance version", + "Instance version", }); } diff --git a/src/util/FFstrbuf.c b/src/util/FFstrbuf.c index 703b4055d..d3c8bb8ec 100644 --- a/src/util/FFstrbuf.c +++ b/src/util/FFstrbuf.c @@ -249,13 +249,17 @@ void ffStrbufTrimLeft(FFstrbuf* strbuf, char c) void ffStrbufTrimRight(FFstrbuf* strbuf, char c) { - if(strbuf->length == 0) + if (strbuf->length == 0) return; - while(ffStrbufEndsWithC(strbuf, c)) - --strbuf->length; + if (!ffStrbufEndsWithC(strbuf, c)) + return; - if(strbuf->allocated == 0) + do + --strbuf->length; + while (ffStrbufEndsWithC(strbuf, c)); + + if (strbuf->allocated == 0) { //static string ffStrbufInitNS(strbuf, strbuf->length, strbuf->chars); @@ -267,13 +271,17 @@ void ffStrbufTrimRight(FFstrbuf* strbuf, char c) void ffStrbufTrimRightSpace(FFstrbuf* strbuf) { - if(strbuf->length == 0) + if (strbuf->length == 0) return; - while(ffStrbufEndsWithFn(strbuf, isspace)) - --strbuf->length; + if (!ffStrbufEndsWithFn(strbuf, isspace)) + return; - if(strbuf->allocated == 0) + do + --strbuf->length; + while (ffStrbufEndsWithFn(strbuf, isspace)); + + if (strbuf->allocated == 0) { //static string ffStrbufInitNS(strbuf, strbuf->length, strbuf->chars); @@ -470,6 +478,13 @@ uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue) return str_end == strbuf->chars ? defaultValue : (uint64_t)result; } +int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue) +{ + char* str_end; + long long result = strtoll(strbuf->chars, &str_end, 10); + return str_end == strbuf->chars ? defaultValue : (int64_t)result; +} + void ffStrbufUpperCase(FFstrbuf* strbuf) { for (uint32_t i = 0; i < strbuf->length; ++i) diff --git a/src/util/FFstrbuf.h b/src/util/FFstrbuf.h index df4a70951..1eb6a3edd 100644 --- a/src/util/FFstrbuf.h +++ b/src/util/FFstrbuf.h @@ -83,6 +83,7 @@ void ffStrbufWriteTo(const FFstrbuf* strbuf, FILE* file); void ffStrbufPutTo(const FFstrbuf* strbuf, FILE* file); FF_C_NODISCARD double ffStrbufToDouble(const FFstrbuf* strbuf); +FF_C_NODISCARD int64_t ffStrbufToSInt(const FFstrbuf* strbuf, int64_t defaultValue); FF_C_NODISCARD uint64_t ffStrbufToUInt(const FFstrbuf* strbuf, uint64_t defaultValue); void ffStrbufUpperCase(FFstrbuf* strbuf); @@ -222,6 +223,8 @@ FF_C_NODISCARD static inline FFstrbuf ffStrbufCreate() static inline void ffStrbufInitStatic(FFstrbuf* strbuf, const char* str) { ffStrbufInit(strbuf); + if (!str) return; + strbuf->allocated = 0; strbuf->length = (uint32_t) strlen(str); strbuf->chars = (char*) str; diff --git a/src/util/apple/cf_helpers.c b/src/util/apple/cf_helpers.c index 962334ed5..a0bc534c7 100644 --- a/src/util/apple/cf_helpers.c +++ b/src/util/apple/cf_helpers.c @@ -19,26 +19,61 @@ const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result) return "TypeID is neither 'CFNumber' nor 'CFData'"; } -const char* ffCfStrGetString(CFStringRef str, FFstrbuf* result) +const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) { - if (!str) + if(CFGetTypeID(cf) == CFNumberGetTypeID()) + { + if(!CFNumberGetValue((CFNumberRef)cf, kCFNumberSInt32Type, result)) + return "Number type is not SInt32"; + return NULL; + } + else if(CFGetTypeID(cf) == CFDataGetTypeID()) + { + if(CFDataGetLength((CFDataRef)cf) != sizeof(int)) + return "Data length is not sizeof(int)"; + CFDataGetBytes((CFDataRef)cf, CFRangeMake(0, sizeof(int)), (uint8_t*)result); + return NULL; + } + + return "TypeID is neither 'CFNumber' nor 'CFData'"; +} + +const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) +{ + if (!cf) { ffStrbufClear(result); return NULL; } - uint32_t length = (uint32_t)CFStringGetLength(str); - //CFString stores UTF16 characters, therefore may require larger buffer to convert to UTF8 string - ffStrbufEnsureFree(result, length * 2); - if(!CFStringGetCString(str, result->chars, result->allocated, kCFStringEncodingUTF8)) + if (CFGetTypeID(cf) == CFStringGetTypeID()) { - ffStrbufEnsureFree(result, length * 4); - if(!CFStringGetCString(str, result->chars, result->allocated, kCFStringEncodingUTF8)) - return "CFStringGetCString() failed"; + CFStringRef cfStr = (CFStringRef)cf; + uint32_t length = (uint32_t)CFStringGetLength(cfStr); + //CFString stores UTF16 characters, therefore may require larger buffer to convert to UTF8 string + ffStrbufEnsureFree(result, length * 2); + if (!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) + { + ffStrbufEnsureFree(result, length * 4); + if(!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) + return "CFStringGetCString() failed"; + } + // CFStringGetCString ensures the buffer is NUL terminated + // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring + result->length = (uint32_t) strnlen(result->chars, (uint32_t)result->allocated); } - // CFStringGetCString ensures the buffer is NUL terminated - // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring - result->length = (uint32_t) strnlen(result->chars, (uint32_t)result->allocated); + else if (CFGetTypeID(cf) == CFDataGetTypeID()) + { + CFDataRef cfData = (CFDataRef)cf; + uint32_t length = (uint32_t)CFDataGetLength(cfData); + ffStrbufEnsureFree(result, length + 1); + CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars); + result->length = (uint32_t)strnlen(result->chars, length); + result->chars[result->length] = '\0'; + } + else + return "TypeID is neither 'CFString' nor 'CFData'"; + return NULL; } @@ -48,22 +83,7 @@ const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* r if(cf == NULL) return "CFDictionaryGetValue() failed"; - if(CFGetTypeID(cf) == CFStringGetTypeID()) - { - return ffCfStrGetString((CFStringRef)cf, result); - } - else if(CFGetTypeID(cf) == CFDataGetTypeID()) - { - CFDataRef cfData = (CFDataRef)cf; - uint32_t length = (uint32_t)CFDataGetLength(cfData); - ffStrbufEnsureFree(result, length + 1); - CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*)result->chars); - result->length = (uint32_t)strnlen(result->chars, length); - result->chars[result->length] = '\0'; - return NULL; - } - - return "TypeID is neither 'CFString' nor 'CFData'"; + return ffCfStrGetString(cf, result); } const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) @@ -85,21 +105,7 @@ const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) if(cf == NULL) return "CFDictionaryGetValue() failed"; - if(CFGetTypeID(cf) == CFNumberGetTypeID()) - { - if(!CFNumberGetValue((CFNumberRef)cf, kCFNumberSInt32Type, result)) - return "Number type is not SInt32"; - return NULL; - } - else if(CFGetTypeID(cf) == CFDataGetTypeID()) - { - if(CFDataGetLength((CFDataRef)cf) != sizeof(int)) - return "Data length is not sizeof(int)"; - CFDataGetBytes((CFDataRef)cf, CFRangeMake(0, sizeof(int)), (uint8_t*)result); - return NULL; - } - - return "TypeID is neither 'CFNumber' nor 'CFData'"; + return ffCfNumGetInt(cf, result); } const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result) diff --git a/src/util/apple/cf_helpers.h b/src/util/apple/cf_helpers.h index eba56c127..fbfa47383 100644 --- a/src/util/apple/cf_helpers.h +++ b/src/util/apple/cf_helpers.h @@ -5,9 +5,11 @@ #include "fastfetch.h" #include +#include //Return error info if failed, NULL otherwise -const char* ffCfStrGetString(CFStringRef str, FFstrbuf* result); +const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result); +const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result); const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result); const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result); const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result); @@ -29,4 +31,12 @@ static inline void cfReleaseWrapper(void* type) #define FF_CFTYPE_AUTO_RELEASE __attribute__((__cleanup__(cfReleaseWrapper))) +static inline void wrapIoObjectRelease(io_service_t* service) +{ + assert(service); + if (*service) + IOObjectRelease(*service); +} +#define FF_IOOBJECT_AUTO_RELEASE __attribute__((__cleanup__(wrapIoObjectRelease))) + #endif diff --git a/src/util/edidHelper.c b/src/util/edidHelper.c index 5a375dfcf..5fa2c662b 100644 --- a/src/util/edidHelper.c +++ b/src/util/edidHelper.c @@ -50,6 +50,20 @@ void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* h *height = (((uint32_t) edid[68] & 0x0F) << 8) + edid[67]; } +void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week) +{ + if (edid[17] > 0 && edid[17] < 0xFF) + { + *year = edid[17] + 1990; + *week = edid[16]; + if (*week == 0xFF) *week = 0; + } + else + *year = *week = 0; + + *serial = *(uint32_t*) &edid[12]; +} + bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length) { if (length <= 128) return false; diff --git a/src/util/edidHelper.h b/src/util/edidHelper.h index 2fc8d591d..6e768ddf9 100644 --- a/src/util/edidHelper.h +++ b/src/util/edidHelper.h @@ -7,4 +7,5 @@ void ffEdidGetVendorAndModel(const uint8_t edid[128], FFstrbuf* result); bool ffEdidGetName(const uint8_t edid[128], FFstrbuf* name); void ffEdidGetPhysicalResolution(const uint8_t edid[128], uint32_t* width, uint32_t* height); void ffEdidGetPhysicalSize(const uint8_t edid[128], uint32_t* width, uint32_t* height); // in mm +void ffEdidGetSerialAndManufactureDate(const uint8_t edid[128], uint32_t* serial, uint16_t* year, uint16_t* week); bool ffEdidGetHdrCompatible(const uint8_t* edid, uint32_t length); diff --git a/src/util/smbiosHelper.c b/src/util/smbiosHelper.c index 5f958fc27..b9418cab9 100644 --- a/src/util/smbiosHelper.c +++ b/src/util/smbiosHelper.c @@ -1,8 +1,10 @@ #include "smbiosHelper.h" #include "common/io/io.h" +#include "util/unused.h" bool ffIsSmbiosValueSet(FFstrbuf* value) { + ffStrbufTrimRightSpace(value); return value->length > 0 && !ffStrbufStartsWithIgnCaseS(value, "To be filled") && @@ -23,28 +25,84 @@ bool ffIsSmbiosValueSet(FFstrbuf* value) !ffStrbufIgnCaseEqualS(value, "Not Available") && !ffStrbufIgnCaseEqualS(value, "INVALID") && !ffStrbufIgnCaseEqualS(value, "Type1ProductConfigId") && + !ffStrbufIgnCaseEqualS(value, "No Enclosure") && + !ffStrbufIgnCaseEqualS(value, "Chassis Version") && !ffStrbufIgnCaseEqualS(value, "All Series") && !ffStrbufIgnCaseEqualS(value, "N/A") ; } #ifdef __linux__ -void ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) +bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) { if (ffReadFileBuffer(devicesPath, buffer)) { ffStrbufTrimRightSpace(buffer); if(ffIsSmbiosValueSet(buffer)) - return; + return true; } if (ffReadFileBuffer(classPath, buffer)) { ffStrbufTrimRightSpace(buffer); if(ffIsSmbiosValueSet(buffer)) - return; + return true; } ffStrbufClear(buffer); + return false; +} +#elif defined(_WIN32) +#include + +#pragma GCC diagnostic ignored "-Wmultichar" + +const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header) +{ + const char* p = ((const char*) header) + header->Length; + if (*p) + { + do + p += strlen(p) + 1; + while (*p); + } + else // The terminator is always double 0 even if there is no string + p ++; + + return (const FFSmbiosHeader*) (p + 1); +} + +const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable() +{ + static FFRawSmbiosData* buffer; + static FFSmbiosHeaderTable table; + + if (!buffer) + { + const DWORD signature = 'RSMB'; + uint32_t bufSize = GetSystemFirmwareTable(signature, 0, NULL, 0); + assert(bufSize > sizeof(FFRawSmbiosData)); + buffer = (FFRawSmbiosData*) malloc(bufSize); + assert(buffer); + FF_MAYBE_UNUSED uint32_t resultSize = GetSystemFirmwareTable(signature, 0, buffer, bufSize); + assert(resultSize == bufSize); + + for ( + const FFSmbiosHeader* header = (const FFSmbiosHeader*) buffer->SMBIOSTableData; + (const uint8_t*) header < buffer->SMBIOSTableData + buffer->Length; + header = ffSmbiosNextEntry(header) + ) + { + if (header->Type < FF_SMBIOS_TYPE_END_OF_TABLE) + { + if (!table[header->Type]) + table[header->Type] = header; + } + else if (header->Type == FF_SMBIOS_TYPE_END_OF_TABLE) + break; + } + } + + return &table; } #endif diff --git a/src/util/smbiosHelper.h b/src/util/smbiosHelper.h index f129af5ac..6a80b3ab5 100644 --- a/src/util/smbiosHelper.h +++ b/src/util/smbiosHelper.h @@ -13,7 +13,97 @@ static inline void ffCleanUpSmbiosValue(FFstrbuf* value) } #ifdef __linux__ -void ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer); +bool ffGetSmbiosValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer); +#elif defined(_WIN32) +// https://github.com/KunYi/DumpSMBIOS +// https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.7.0.pdf + +typedef enum __attribute__((__packed__)) FFSmbiosType // : uint8_t +{ + FF_SMBIOS_TYPE_BIOS = 0, + FF_SMBIOS_TYPE_SYSTEM_INFO = 1, + FF_SMBIOS_TYPE_BASEBOARD_INFO = 2, + FF_SMBIOS_TYPE_SYSTEM_ENCLOSURE = 3, + FF_SMBIOS_TYPE_PROCESSOR_INFO = 4, + FF_SMBIOS_TYPE_MEMORY_CONTROLLER_INFO = 5, // obsolete + FF_SMBIOS_TYPE_MEMORY_MODULE_INFO = 6, // obsolete + FF_SMBIOS_TYPE_CACHE_INFO = 7, + FF_SMBIOS_TYPE_PORT_CONNECTOR_INFO = 8, + FF_SMBIOS_TYPE_SYSTEM_SLOTS = 9, + FF_SMBIOS_TYPE_ON_BOARD_DEVICES_INFO = 10, // obsolete + FF_SMBIOS_TYPE_OEM_STRING = 11, + FF_SMBIOS_TYPE_SYSTEM_CONFIGURATION_OPTIONS = 12, + FF_SMBIOS_TYPE_BIOS_LANGUAGE_INFO = 13, + FF_SMBIOS_TYPE_GROUP_ASSOCIATIONS = 14, + FF_SMBIOS_TYPE_SYSTEM_EVENT_LOG = 15, + FF_SMBIOS_TYPE_PHYSICAL_MEMORY_ARRAY = 16, + FF_SMBIOS_TYPE_MEMORY_DEVICE = 17, + FF_SMBIOS_TYPE_32BIT_MEMORY_ERROR_INFO = 18, + FF_SMBIOS_TYPE_MEMORY_ARRAY_MAPPED_ADDRESS = 19, + FF_SMBIOS_TYPE_MEMORY_DEVICE_MAPPED_ADDRESS = 20, + FF_SMBIOS_TYPE_BUILTIN_POINTING_DEVICE = 21, + FF_SMBIOS_TYPE_PORTABLE_BATTERY = 22, + FF_SMBIOS_TYPE_SYSTEM_RESET = 23, + FF_SMBIOS_TYPE_HARDWARE_SECURITY = 24, + FF_SMBIOS_TYPE_SYSTEM_POWER_CONTROLS = 25, + FF_SMBIOS_TYPE_VOLTAGE_PROBE = 26, + FF_SMBIOS_TYPE_COOLING_DEVICE = 27, + FF_SMBIOS_TYPE_TEMPERATURE_PROBE = 28, + FF_SMBIOS_TYPE_ELECTRICAL_CURRENT_PROBE = 29, + FF_SMBIOS_TYPE_OUT_OF_BAND_REMOTE_ACCESS = 30, + FF_SMBIOS_TYPE_BOOT_INTEGRITY_SERVICES_ENTRY_POINT = 31, // reserved + FF_SMBIOS_TYPE_SYSTEM_BOOT_INFO = 32, + FF_SMBIOS_TYPE_64BIT_MEMORY_ERROR_INFO = 33, + FF_SMBIOS_TYPE_MANAGEMENT_DEVICE = 34, + FF_SMBIOS_TYPE_MANAGEMENT_DEVICE_COMPONENT = 35, + FF_SMBIOS_TYPE_MANAGEMENT_DEVICE_THRESHOLD_DATA = 36, + FF_SMBIOS_TYPE_MEMORY_CHANNEL = 37, + FF_SMBIOS_TYPE_IPMI_DEVICE_INFO = 38, + FF_SMBIOS_TYPE_SYSTEM_POWER_SUPPLY = 39, + FF_SMBIOS_TYPE_ADDITIONAL_INFO = 40, + FF_SMBIOS_TYPE_ONBOARD_DEVICE_EXTENDED_INFO = 41, + FF_SMBIOS_TYPE_MANAGEMENT_CONTROLLER_HOST_INTERFACE = 42, + FF_SMBIOS_TYPE_TPM_DEVICE = 43, + FF_SMBIOS_TYPE_PROCESSOR_ADDITIONAL_INFO = 44, + FF_SMBIOS_TYPE_FIRMWARE_INVENTORY_INFO = 45, + FF_SMBIOS_TYPE_STRING_PROPERTY = 46, + FF_SMBIOS_TYPE_INACTIVE = 126, + FF_SMBIOS_TYPE_END_OF_TABLE = 127, + // system- and OEM-specific information 128~256 +} FFSmbiosType; +static_assert(sizeof(FFSmbiosType) == 1, "FFSmbiosType should be 1 byte"); + +typedef struct FFSmbiosHeader +{ + FFSmbiosType Type; + uint8_t Length; + uint16_t Handle; +} FFSmbiosHeader; +static_assert(sizeof(FFSmbiosHeader) == 4, "FFSmbiosHeader should be 4 bytes"); + +typedef struct FFRawSmbiosData +{ + uint8_t Used20CallingMethod; + uint8_t SMBIOSMajorVersion; + uint8_t SMBIOSMinorVersion; + uint8_t DmiRevision; + uint32_t Length; + uint8_t SMBIOSTableData[]; +} FFRawSmbiosData; + +static inline const char* ffSmbiosLocateString(const char* start, uint8_t index /* start from 1 */) +{ + if (index == 0 || *start == '\0') + return NULL; + while (--index) + start += strlen(start) + 1; + return start; +} + +typedef const FFSmbiosHeader* FFSmbiosHeaderTable[FF_SMBIOS_TYPE_END_OF_TABLE]; + +const FFSmbiosHeader* ffSmbiosNextEntry(const FFSmbiosHeader* header); +const FFSmbiosHeaderTable* ffGetSmbiosHeaderTable(); #endif #endif diff --git a/src/util/stringUtils.c b/src/util/stringUtils.c deleted file mode 100644 index 07a3bd101..000000000 --- a/src/util/stringUtils.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "stringUtils.h" - -#include - -bool ffStrSet(const char* str) -{ - if(str == NULL) - return false; - - while(isspace(*str)) - str++; - - return *str != '\0'; -} - -bool ffStrHasNChars(const char* str, char c, uint32_t n) -{ - for(uint32_t i = 0; i < n; i++) - { - char* current = strchr(str, c); - if(current == NULL) - return false; - - str = current + 1; - } - - return true; -} diff --git a/src/util/stringUtils.h b/src/util/stringUtils.h index e67298224..4de957841 100644 --- a/src/util/stringUtils.h +++ b/src/util/stringUtils.h @@ -3,9 +3,19 @@ #include #include #include +#include + +static inline bool ffStrSet(const char* str) +{ + if(str == NULL) + return false; + + while(isspace(*str)) + str++; + + return *str != '\0'; +} -bool ffStrSet(const char* str); -bool ffStrHasNChars(const char* str, char c, uint32_t n); static inline bool ffStrStartsWithIgnCase(const char* str, const char* compareTo) { @@ -44,3 +54,13 @@ static inline bool ffStrEquals(const char* str, const char* compareTo) { return strcmp(str, compareTo) == 0; } + +static inline bool ffStrContains(const char* str, const char* compareTo) +{ + return strstr(str, compareTo) != NULL; +} + +static inline bool ffStrContainsIgnCase(const char* str, const char* compareTo) +{ + return strcasestr(str, compareTo) != NULL; +} diff --git a/tests/strbuf.c b/tests/strbuf.c index e27bc6c2e..0a35012d2 100644 --- a/tests/strbuf.c +++ b/tests/strbuf.c @@ -181,6 +181,12 @@ int main(void) ffStrbufEnsureEndsWithC(&strbuf, '$'); VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG$")); + //trimRight + ffStrbufTrimRight(&strbuf, '$'); + VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG")); + ffStrbufTrimRight(&strbuf, '$'); + VERIFY(ffStrbufEqualS(&strbuf, "AbCdEfG")); + //clear ffStrbufClear(&strbuf); VERIFY(strbuf.allocated > 0); @@ -296,6 +302,9 @@ int main(void) //ffStrbufCreateStatic / TrimR ffStrbufInitStatic(&strbuf, "_TEST_"); + ffStrbufTrimRight(&strbuf, ' '); + VERIFY(strbuf.allocated == 0); + VERIFY(ffStrbufEqualS(&strbuf, "_TEST_")); ffStrbufTrimRight(&strbuf, '_'); VERIFY(ffStrbufEqualS(&strbuf, "_TEST")); VERIFY(strbuf.length == 5);