diff --git a/CMakeLists.txt b/CMakeLists.txt index 01dec2e8e..6716badc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,7 +66,8 @@ cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD OR WIN32" cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX OR WIN32" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND AND NOT ANDROID" OFF) -cmake_dependent_option(USE_WIN_FAST_PPID_DETECTION "Use internal NTAPI instead of querying WMI to get PPID" ON "WIN32" OFF) +cmake_dependent_option(USE_WIN_NTAPI "Allow using internal NTAPI" ON "WIN32" OFF) +cmake_dependent_option(USE_WIN_GPU_DXGI "Use DXGI to detect GPUs instead of WMI. Faster, but may ignore GPUs that only support DX9" ON "WIN32" OFF) 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 @@ -300,6 +301,7 @@ if(LINUX) src/detection/board/board_linux.c src/detection/cpu/cpu_linux.c src/detection/cpuUsage/cpuUsage_linux.c + src/detection/cursor/cursor_linux.c src/detection/disk/disk_linux.c src/detection/displayserver/linux/displayserver_linux.c src/detection/displayserver/linux/wayland.c @@ -335,12 +337,12 @@ elseif(ANDROID) src/detection/bios/bios_nosupport.c src/detection/board/board_nosupport.c src/detection/cpu/cpu_linux.c + src/detection/cursor/cursor_nosupport.c src/detection/cpuUsage/cpuUsage_linux.c src/detection/disk/disk_linux.c src/detection/displayserver/displayserver_nosupport.c src/detection/font/font_nosupport.c src/detection/gpu/gpu_nosupport.c - src/detection/gtk.c src/detection/host/host_android.c src/detection/localip/localip_linux.c src/detection/media/media_nosupport.c @@ -350,7 +352,6 @@ elseif(ANDROID) src/detection/packages/packages_linux.c src/detection/poweradapter/poweradapter_nosupport.c src/detection/processes/processes_linux.c - src/detection/qt.c src/detection/swap/swap_linux.c src/detection/temps/temps_linux.c src/detection/terminalfont/terminalfont_android.c @@ -369,19 +370,33 @@ elseif(BSD) src/detection/board/board_nosupport.c src/detection/cpu/cpu_bsd.c src/detection/cpuUsage/cpuUsage_bsd.c + src/detection/cursor/cursor_linux.c src/detection/disk/disk_bsd.c + src/detection/displayserver/linux/displayserver_linux.c + src/detection/displayserver/linux/wayland.c + src/detection/displayserver/linux/wmde.c + src/detection/displayserver/linux/xcb.c + src/detection/displayserver/linux/xlib.c + src/detection/font/font_linux.c + src/detection/gpu/gpu_linux.c + src/detection/gtk.c src/detection/host/host_bsd.c src/detection/localip/localip_linux.c + src/detection/media/media_linux.c src/detection/memory/memory_bsd.c 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/processes/processes_bsd.c + src/detection/qt.c src/detection/swap/swap_bsd.c src/detection/temps/temps_linux.c + src/detection/terminalfont/terminalfont_linux.c src/detection/terminalshell/terminalshell_linux.c src/detection/uptime/uptime_bsd.c src/detection/users/users_linux.c + src/detection/wmtheme/wmtheme_linux.c ) elseif(APPLE) list(APPEND LIBFASTFETCH_SRC @@ -393,6 +408,7 @@ elseif(APPLE) src/detection/board/board_nosupport.c src/detection/cpu/cpu_apple.c src/detection/cpuUsage/cpuUsage_apple.c + src/detection/cursor/cursor_nosupport.c src/detection/disk/disk_apple.m src/detection/disk/disk_bsd.c src/detection/displayserver/displayserver_apple.c @@ -421,33 +437,35 @@ elseif(WIN32) list(APPEND LIBFASTFETCH_SRC src/common/networking_windows.c src/common/processing_windows.c - src/detection/battery/battery_windows.cpp - src/detection/bios/bios_windows.cpp - src/detection/board/board_windows.cpp - src/detection/cpu/cpu_windows.cpp - src/detection/cpuUsage/cpuUsage_nowait_windows.cpp + src/detection/battery/battery_windows.c + src/detection/bios/bios_windows.c + src/detection/board/board_windows.c + src/detection/cpu/cpu_windows.c src/detection/cpuUsage/cpuUsage_windows.c + src/detection/cursor/cursor_windows.c src/detection/disk/disk_windows.c src/detection/displayserver/displayserver_windows.c - src/detection/font/font_windows.cpp + src/detection/font/font_windows.c src/detection/gpu/gpu_windows.cpp - src/detection/host/host_windows.cpp + src/detection/host/host_windows.c src/detection/localip/localip_windows.c src/detection/media/media_nosupport.c - src/detection/memory/memory_windows.cpp + src/detection/memory/memory_windows.c src/detection/opengl/opengl_windows.c src/detection/os/os_windows.cpp src/detection/packages/packages_windows.c src/detection/poweradapter/poweradapter_nosupport.c src/detection/processes/processes_windows.cpp - src/detection/swap/swap_windows.cpp + src/detection/swap/swap_windows.c src/detection/terminalfont/terminalfont_windows.c src/detection/terminalshell/terminalshell_windows.cpp src/detection/uptime/uptime_windows.c - src/detection/users/users_windows.cpp + src/detection/users/users_windows.c src/detection/wmtheme/wmtheme_windows.c src/util/windows/getline.c src/util/windows/pwd.c + src/util/windows/register.c + src/util/windows/unicode.c src/util/windows/utsname.c src/util/windows/wmi.cpp ) @@ -545,9 +563,15 @@ elseif(WIN32) PRIVATE "ws2_32" PRIVATE "ntdll" PRIVATE "version" + PRIVATE "setupapi" + PRIVATE "dxgi" + PRIVATE "wtsapi32" ) - if(USE_WIN_FAST_PPID_DETECTION) - target_compile_definitions(libfastfetch PRIVATE FF_USE_WIN_FAST_PPID_DETECTION) + if(USE_WIN_NTAPI) + target_compile_definitions(libfastfetch PRIVATE FF_USE_WIN_NTAPI) + endif() + if(USE_WIN_GPU_DXGI) + target_compile_definitions(libfastfetch PRIVATE FF_USE_WIN_GPU_DXGI) endif() endif() diff --git a/src/detection/battery/battery_windows.c b/src/detection/battery/battery_windows.c new file mode 100644 index 000000000..ed4a1a813 --- /dev/null +++ b/src/detection/battery/battery_windows.c @@ -0,0 +1,133 @@ +#include "battery.h" +#include "util/windows/unicode.h" +#include "util/mallocHelper.h" + +#include +#include +#include + +static inline void wrapCloseHandle(HANDLE* handle) +{ + if(*handle) + CloseHandle(*handle); +} +static inline void wrapSetupDiDestroyDeviceInfoList(HDEVINFO* hdev) +{ + if(*hdev) + SetupDiDestroyDeviceInfoList(*hdev); +} + +const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) +{ + FF_UNUSED(instance); + + //https://learn.microsoft.com/en-us/windows/win32/power/enumerating-battery-devices + HDEVINFO hdev __attribute__((__cleanup__(wrapSetupDiDestroyDeviceInfoList))) = + SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if(hdev == INVALID_HANDLE_VALUE) + return "SetupDiGetClassDevs(&GUID_DEVCLASS_BATTERY) failed"; + + for(DWORD idev = 0;; idev++) + { + SP_DEVICE_INTERFACE_DATA did = { .cbSize = sizeof(did) }; + if(!SetupDiEnumDeviceInterfaces(hdev, NULL, &GUID_DEVCLASS_BATTERY, idev, &did)) + break; + + DWORD cbRequired = 0; + SetupDiGetDeviceInterfaceDetailW(hdev, &did, NULL, 0, &cbRequired, NULL); //Fail with not enough buffer + SP_DEVICE_INTERFACE_DETAIL_DATA_W* FF_AUTO_FREE pdidd = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)malloc(cbRequired); + if(!pdidd) + break; //Out of memory + + pdidd->cbSize = sizeof(*pdidd); + if(!SetupDiGetDeviceInterfaceDetailW(hdev, &did, pdidd, cbRequired, &cbRequired, NULL)) + continue; + + HANDLE __attribute__((__cleanup__(wrapCloseHandle))) hBattery = + CreateFileW(pdidd->DevicePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if(hBattery == INVALID_HANDLE_VALUE) + continue; + + BATTERY_QUERY_INFORMATION bqi = { .InformationLevel = BatteryInformation }; + + DWORD dwWait = 0; + DWORD dwOut; + + if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_TAG, &dwWait, sizeof(dwWait), &bqi.BatteryTag, sizeof(bqi.BatteryTag), &dwOut, NULL) && bqi.BatteryTag) + continue; + + BATTERY_INFORMATION bi = {0}; + if(!DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &bi, sizeof(bi), &dwOut, NULL)) + continue; + + if(!(bi.Capabilities & BATTERY_SYSTEM_BATTERY)) + continue; + + BatteryResult* battery = (BatteryResult*)ffListAdd(results); + + if(memcmp(bi.Chemistry, "PbAc", 4) == 0) + ffStrbufInitS(&battery->technology, "Lead Acid"); + else if(memcmp(bi.Chemistry, "LION", 4) == 0 || memcmp(bi.Chemistry, "Li-I", 4) == 0) + ffStrbufInitS(&battery->technology, "Lithium Ion"); + else if(memcmp(bi.Chemistry, "NiCd", 4) == 0) + ffStrbufInitS(&battery->technology, "Nickel Cadmium"); + else if(memcmp(bi.Chemistry, "NiMH", 4) == 0) + ffStrbufInitS(&battery->technology, "Nickel Metal Hydride"); + else if(memcmp(bi.Chemistry, "NiZn", 4) == 0) + ffStrbufInitS(&battery->technology, "Nickel Zinc"); + else if(memcmp(bi.Chemistry, "RAM\0", 4) == 0) + ffStrbufInitS(&battery->technology, "Rechargeable Alkaline-Manganese"); + else + ffStrbufInitS(&battery->technology, "Unknown"); + + { + ffStrbufInit(&battery->modelName); + bqi.InformationLevel = BatteryDeviceName; + wchar_t name[64]; + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL)) + ffWcharToUtf8(name, &battery->modelName); + } + + { + ffStrbufInit(&battery->manufacturer); + bqi.InformationLevel = BatteryManufactureName; + wchar_t name[64]; + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), name, sizeof(name), &dwOut, NULL)) + ffWcharToUtf8(name, &battery->manufacturer); + } + + battery->temperature = 0.0/0.0; + if(instance->config.batteryTemp) + { + bqi.InformationLevel = BatteryTemperature; + ULONG temp; + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_INFORMATION, &bqi, sizeof(bqi), &temp, sizeof(temp), &dwOut, NULL)) + battery->temperature = temp; + } + + { + BATTERY_STATUS bs; + BATTERY_WAIT_STATUS bws = { .BatteryTag = bqi.BatteryTag }; + if(DeviceIoControl(hBattery, IOCTL_BATTERY_QUERY_STATUS, &bws, sizeof(bws), &bs, sizeof(bs), &dwOut, NULL) && bs.Capacity != BATTERY_UNKNOWN_CAPACITY) + battery->capacity = bs.Capacity * 100.0 / bi.FullChargedCapacity; + else + battery->capacity = 0; + + ffStrbufInit(&battery->status); + if(bs.PowerState & BATTERY_POWER_ON_LINE) + ffStrbufAppendS(&battery->status, "AC Connected, "); + if(bs.PowerState & BATTERY_DISCHARGING) + ffStrbufAppendS(&battery->status, "Discharging, "); + if(bs.PowerState & BATTERY_CRITICAL) + ffStrbufAppendS(&battery->status, "Critical, "); + if(bs.PowerState & BATTERY_CHARGING) + ffStrbufAppendS(&battery->status, "Charging"); + ffStrbufTrimRight(&battery->status, ' '); + ffStrbufTrimRight(&battery->status, ','); + } + + } + + return NULL; +} diff --git a/src/detection/battery/battery_windows.cpp b/src/detection/battery/battery_windows.cpp deleted file mode 100644 index 9cf53ee28..000000000 --- a/src/detection/battery/battery_windows.cpp +++ /dev/null @@ -1,65 +0,0 @@ -extern "C" { -#include "battery.h" -} -#include "util/windows/wmi.hpp" - -const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) -{ - FF_UNUSED(instance); - - //https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-battery - FFWmiQuery query(L"SELECT SystemName, Name, Chemistry, EstimatedChargeRemaining, BatteryStatus FROM Win32_Battery"); - - if(!query) - return "Query WMI service failed"; - - while(FFWmiRecord record = query.next()) - { - BatteryResult* battery = (BatteryResult*)ffListAdd(results); - - ffStrbufInit(&battery->manufacturer); - record.getString(L"SystemName", &battery->manufacturer); - - ffStrbufInit(&battery->modelName); - record.getString(L"Name", &battery->modelName); - - uint64_t chemistry = 0; - record.getUnsigned(L"Chemistry", &chemistry); - switch(chemistry) - { - case 1: ffStrbufInitS(&battery->technology, "Other"); break; - case 2: ffStrbufInitS(&battery->technology, "Unknown"); break; - case 3: ffStrbufInitS(&battery->technology, "Lead Acid"); break; - case 4: ffStrbufInitS(&battery->technology, "Nickel Cadmium"); break; - case 5: ffStrbufInitS(&battery->technology, "Nickel Metal Hydride"); break; - case 6: ffStrbufInitS(&battery->technology, "Lithium-ion"); break; - case 7: ffStrbufInitS(&battery->technology, "Zinc air"); break; - case 8: ffStrbufInitS(&battery->technology, "Lithium Polymer"); break; - default: ffStrbufInit(&battery->technology); break; - } - - record.getReal(L"EstimatedChargeRemaining", &battery->capacity); - - uint64_t batteryStatus; - record.getUnsigned(L"BatteryStatus", &batteryStatus); - switch(batteryStatus) - { - case 1: ffStrbufInitS(&battery->status, "Discharging"); break; - case 2: ffStrbufInitS(&battery->status, "AC Connected"); break; - case 3: ffStrbufInitS(&battery->status, "Fully Charged"); break; - case 4: ffStrbufInitS(&battery->status, "Low"); break; - case 5: ffStrbufInitS(&battery->status, "Critical"); break; - case 6: ffStrbufInitS(&battery->status, "Charging"); break; - case 7: ffStrbufInitS(&battery->status, "Charging and High"); break; - case 8: ffStrbufInitS(&battery->status, "Charging and Low"); break; - case 9: ffStrbufInitS(&battery->status, "Charging and Critical"); break; - case 10: ffStrbufInitS(&battery->status, "Undefined"); break; - case 11: ffStrbufInitS(&battery->status, "Partially Charged"); break; - default: ffStrbufInit(&battery->status); break; - } - - battery->temperature = FF_BATTERY_TEMP_UNSET; - } - - return nullptr; -} diff --git a/src/detection/bios/bios_windows.c b/src/detection/bios/bios_windows.c new file mode 100644 index 000000000..e29b1cc8c --- /dev/null +++ b/src/detection/bios/bios_windows.c @@ -0,0 +1,28 @@ +#include "bios.h" +#include "util/windows/register.h" + +void ffDetectBios(FFBiosResult* bios) +{ + ffStrbufInit(&bios->error); + + ffStrbufInit(&bios->biosDate); + ffStrbufInit(&bios->biosRelease); + ffStrbufInit(&bios->biosVendor); + ffStrbufInit(&bios->biosVersion); + + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, &bios->error)) + return; + + if(!ffRegReadStrbuf(hKey, L"BIOSVersion", &bios->biosRelease, &bios->error)) + return; + ffRegReadStrbuf(hKey, L"BIOSVendor", &bios->biosVendor, NULL); + ffRegReadStrbuf(hKey, L"BIOSReleaseDate", &bios->biosDate, NULL); + + uint32_t major, minor; + if( + ffRegReadUint(hKey, L"BiosMajorRelease", &major, NULL) && + ffRegReadUint(hKey, L"BiosMinorRelease", &minor, NULL) + ) + ffStrbufAppendF(&bios->biosVersion, "%u.%u", (unsigned)major, (unsigned)minor); +} diff --git a/src/detection/bios/bios_windows.cpp b/src/detection/bios/bios_windows.cpp deleted file mode 100644 index a48d5d538..000000000 --- a/src/detection/bios/bios_windows.cpp +++ /dev/null @@ -1,28 +0,0 @@ -extern "C" { -#include "bios.h" -} -#include "util/windows/wmi.hpp" - -extern "C" void ffDetectBios(FFBiosResult* bios) -{ - ffStrbufInit(&bios->error); - - ffStrbufInit(&bios->biosDate); - ffStrbufInit(&bios->biosRelease); - ffStrbufInit(&bios->biosVendor); - ffStrbufInit(&bios->biosVersion); - - FFWmiQuery query(L"SELECT Name, ReleaseDate, Version, Manufacturer FROM Win32_BIOS", &bios->error); - if(!query) - return; - - if(FFWmiRecord record = query.next()) - { - record.getString(L"Name", &bios->biosRelease); - record.getString(L"ReleaseDate", &bios->biosDate); - record.getString(L"Version", &bios->biosVersion); - record.getString(L"Manufacturer", &bios->biosVendor); - } - else - ffStrbufInitS(&bios->error, "No Wmi result returned"); -} diff --git a/src/detection/board/board_windows.c b/src/detection/board/board_windows.c new file mode 100644 index 000000000..adeae2251 --- /dev/null +++ b/src/detection/board/board_windows.c @@ -0,0 +1,21 @@ +#include "board.h" +#include "util/windows/register.h" + +void ffDetectBoard(FFBoardResult* board) +{ + ffStrbufInit(&board->error); + + ffStrbufInit(&board->boardName); + ffStrbufInit(&board->boardVendor); + ffStrbufInit(&board->boardVersion); + + FF_HKEY_AUTO_DESTROY hKey = NULL; + + if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, &board->error)) + return; + + if(!ffRegReadStrbuf(hKey, L"BaseBoardProduct", &board->boardName, &board->error)) + return; + ffRegReadStrbuf(hKey, L"BaseBoardManufacturer", &board->boardVendor, NULL); + ffRegReadStrbuf(hKey, L"BaseBoardVersion", &board->boardVersion, NULL); +} diff --git a/src/detection/board/board_windows.cpp b/src/detection/board/board_windows.cpp deleted file mode 100644 index e0f92bcf5..000000000 --- a/src/detection/board/board_windows.cpp +++ /dev/null @@ -1,26 +0,0 @@ -extern "C" { -#include "board.h" -} -#include "util/windows/wmi.hpp" - -extern "C" void ffDetectBoard(FFBoardResult* board) -{ - ffStrbufInit(&board->error); - - ffStrbufInit(&board->boardName); - ffStrbufInit(&board->boardVendor); - ffStrbufInit(&board->boardVersion); - - FFWmiQuery query(L"SELECT Product, Version, Manufacturer FROM Win32_BaseBoard", &board->error); - if(!query) - return; - - if(FFWmiRecord record = query.next()) - { - record.getString(L"Product", &board->boardName); - record.getString(L"Manufacturer", &board->boardVendor); - record.getString(L"Version", &board->boardVersion); - } - else - ffStrbufInitS(&board->error, "No Wmi result returned"); -} diff --git a/src/detection/cpu/cpu_windows.c b/src/detection/cpu/cpu_windows.c new file mode 100644 index 000000000..a0a86d169 --- /dev/null +++ b/src/detection/cpu/cpu_windows.c @@ -0,0 +1,53 @@ +#include "cpu.h" +#include "util/windows/register.h" +#include "util/mallocHelper.h" + +void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu, bool cached) +{ + FF_UNUSED(instance); + + cpu->temperature = FF_CPU_TEMP_UNSET; + + if(cached) + return; + + cpu->coresPhysical = cpu->coresLogical = cpu->coresOnline = 0; + cpu->frequencyMax = cpu->frequencyMin = 0; + ffStrbufInit(&cpu->name); + ffStrbufInit(&cpu->vendor); + + { + DWORD length = 0; + GetLogicalProcessorInformationEx(RelationProcessorCore, NULL, &length); + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* FF_AUTO_FREE + pLogicalInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(length); + + if(pLogicalInfo && GetLogicalProcessorInformationEx(RelationProcessorCore, pLogicalInfo, &length)) + { + for( + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX* ptr = pLogicalInfo; + (uint8_t*)ptr < ((uint8_t*)pLogicalInfo) + length; + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((uint8_t*)ptr) + ptr->Size) + ) + { + if(ptr->Relationship == RelationProcessorCore) + ++cpu->coresPhysical; + } + } + } + cpu->coresOnline = (uint16_t)GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + cpu->coresLogical = (uint16_t)GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS); + + FF_HKEY_AUTO_DESTROY hKey; + if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL)) + return; + + { + uint32_t mhz; + if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL)) + cpu->frequencyMax = mhz / 1000.0; + } + + ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL); + ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL); +} diff --git a/src/detection/cpu/cpu_windows.cpp b/src/detection/cpu/cpu_windows.cpp deleted file mode 100644 index e5939cd60..000000000 --- a/src/detection/cpu/cpu_windows.cpp +++ /dev/null @@ -1,51 +0,0 @@ -extern "C" { -#include "cpu.h" -} -#include "util/windows/wmi.hpp" - -extern "C" -void ffDetectCPUImpl(const FFinstance* instance, FFCPUResult* cpu, bool cached) -{ - FF_UNUSED(instance); - - cpu->temperature = FF_CPU_TEMP_UNSET; - - if(cached) - return; - - cpu->coresPhysical = cpu->coresLogical = cpu->coresOnline = 0; - ffStrbufInit(&cpu->name); - ffStrbufInit(&cpu->vendor); - - FFWmiQuery query(L"SELECT Name, Manufacturer, NumberOfCores, NumberOfLogicalProcessors, NumberOfEnabledCore, CurrentClockSpeed, MaxClockSpeed FROM Win32_Processor WHERE ProcessorType = 3"); - if(!query) - return; - - FFWmiRecord record = query.next(); - if(!record) - { - //NumberOfEnabledCore is not supported on Windows 10- - query = FFWmiQuery(L"SELECT Name, Manufacturer, NumberOfCores, NumberOfLogicalProcessors, CurrentClockSpeed, MaxClockSpeed FROM Win32_Processor WHERE ProcessorType = 3"); - if(!query) - return; - record = query.next(); - } - - if(record) - { - record.getString(L"Name", &cpu->name); - record.getString(L"Manufacturer", &cpu->vendor); - - uint64_t value; - - record.getUnsigned(L"NumberOfCores", &value); - cpu->coresPhysical = (uint16_t)value; - record.getUnsigned(L"NumberOfLogicalProcessors", &value); - cpu->coresLogical = (uint16_t)value; - cpu->coresOnline = record.getUnsigned(L"NumberOfEnabledCore", &value) ? (uint16_t)value : cpu->coresPhysical; - record.getUnsigned(L"CurrentClockSpeed", &value); //There's no MinClockSpeed in Win32_Processor - cpu->frequencyMin = (double)value / 1000.0; - record.getUnsigned(L"MaxClockSpeed", &value); - cpu->frequencyMax = (double)value / 1000.0; - } -} diff --git a/src/detection/cpuUsage/cpuUsage.c b/src/detection/cpuUsage/cpuUsage.c index 82bf1f7d4..1dbdb4bfb 100644 --- a/src/detection/cpuUsage/cpuUsage.c +++ b/src/detection/cpuUsage/cpuUsage.c @@ -1,18 +1,6 @@ #include "fastfetch.h" #include "cpuUsage.h" -#if FF_DETECTION_CPUUSAGE_NOWAIT - -const char* ffGetCpuUsageResultNoWait(double* result); - -void ffPrepareCPUUsage() {} - -const char* ffGetCpuUsageResult(double* result) { - return ffGetCpuUsageResultNoWait(result); -} - -#else //FF_DETECTION_CPUUSAGE_NOWAIT - #include "common/time.h" #include @@ -61,5 +49,3 @@ const char* ffGetCpuUsageResult(double* result) ffTimeSleep(250); } } - -#endif //FF_DETECTION_CPUUSAGE_NOWAIT diff --git a/src/detection/cpuUsage/cpuUsage.h b/src/detection/cpuUsage/cpuUsage.h index 126fc1a8e..250b30570 100644 --- a/src/detection/cpuUsage/cpuUsage.h +++ b/src/detection/cpuUsage/cpuUsage.h @@ -3,11 +3,6 @@ #ifndef FF_INCLUDED_detection_cpu_cpuUsage #define FF_INCLUDED_detection_cpu_cpuUsage -#ifdef _WIN32 - // Disabled by default because the result does need some time to generate - #define FF_DETECTION_CPUUSAGE_NOWAIT 0 -#endif - const char* ffGetCpuUsageResult(double* result); #endif diff --git a/src/detection/cpuUsage/cpuUsage_nowait_windows.cpp b/src/detection/cpuUsage/cpuUsage_nowait_windows.cpp deleted file mode 100644 index 7ef1aa855..000000000 --- a/src/detection/cpuUsage/cpuUsage_nowait_windows.cpp +++ /dev/null @@ -1,18 +0,0 @@ -extern "C" { -#include "cpuUsage.h" -} -#include "util/windows/wmi.hpp" - -extern "C" const char* ffGetCpuUsageResultNoWait(double* result) -{ - FFWmiQuery query(L"SELECT LoadPercentage FROM Win32_Processor"); - if(!query) - return "Query WMI service failed"; - - if(FFWmiRecord record = query.next()) - record.getReal(L"LoadPercentage", result); - else - return "No WMI result returned"; - - return nullptr; -} diff --git a/src/detection/cursor/cursor.h b/src/detection/cursor/cursor.h new file mode 100644 index 000000000..268631240 --- /dev/null +++ b/src/detection/cursor/cursor.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_cursor_cursor +#define FF_INCLUDED_detection_cursor_cursor + +#include "fastfetch.h" + +typedef struct FFCursorResult +{ + FFstrbuf theme; + FFstrbuf size; + FFstrbuf error; +} FFCursorResult; + +void ffDetectCursor(const FFinstance* instance, FFCursorResult* result); + +#endif diff --git a/src/detection/cursor/cursor_linux.c b/src/detection/cursor/cursor_linux.c new file mode 100644 index 000000000..a8dfb8810 --- /dev/null +++ b/src/detection/cursor/cursor_linux.c @@ -0,0 +1,131 @@ +#include "cursor.h" + +#include "common/properties.h" +#include "common/parsing.h" +#include "common/settings.h" +#include "detection/gtk.h" +#include "detection/displayserver/displayserver.h" + +#include + +static void detectCursorGTK(const FFinstance* instance, FFCursorResult* result) +{ + const FFGTKResult* gtk = ffDetectGTK4(instance); + + if(gtk->cursor.length == 0) + gtk = ffDetectGTK3(instance); + + if(gtk->cursor.length == 0) + gtk = ffDetectGTK2(instance); + + if(gtk->cursor.length == 0) + { + ffStrbufAppendS(&result->error, "Couldn't detect GTK Cursor"); + return; + } + + ffStrbufAppend(&result->theme, >k->cursor); + ffStrbufAppend(&result->size, >k->cursorSize); +} + +static void detectCursorXFCE(const FFinstance* instance, FFCursorResult* result) +{ + ffStrbufAppendS(&result->theme, ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue); + + if(result->theme.length == 0) + ffStrbufAppendS(&result->error, "Couldn't find xfce cursor in xfconf (xsettings::/Gtk/CursorThemeName)"); + + int cursorSizeVal = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue; + if(cursorSizeVal > 0) + ffStrbufAppendF(&result->size, "%i", cursorSizeVal); +} + +static void detectCursorFromConfigFile(const FFinstance* instance, const char* relativeFilePath, const char* themeStart, const char* themeDefault, const char* sizeStart, const char* sizeDefault, FFCursorResult* result) +{ + if(ffParsePropFileConfigValues(instance, relativeFilePath, 2, (FFpropquery[]) { + {themeStart, &result->theme}, + {sizeStart, &result->size} + })) { + + if(result->theme.length == 0) + ffStrbufAppendS(&result->theme, themeDefault); + + if(result->size.length == 0) + ffStrbufAppendS(&result->size, sizeDefault); + } + + if(result->theme.length == 0) + ffStrbufAppendF(&result->error, "Couldn't find cursor in %s", relativeFilePath); +} + +static bool detectCursorFromXResources(const FFinstance* instance, FFCursorResult* result) +{ + ffParsePropFileHomeValues(instance, ".Xresources", 2, (FFpropquery[]) { + {"Xcursor.theme :", &result->theme}, + {"Xcursor.size :", &result->size} + }); + + return result->theme.length > 0; +} + +static bool detectCursorFromXDG(const FFinstance* instance, bool user, FFCursorResult* result) +{ + if(user) + ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &result->theme); + else + ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/icons/default/index.theme", "Inherits =", &result->theme); + + return result->theme.length > 0; +} + +static bool detectCursorFromEnv(const FFinstance* instance, FFCursorResult* result) +{ + FF_UNUSED(instance); + const char* xcursor_theme = getenv("XCURSOR_THEME"); + + if(!ffStrSet(xcursor_theme)) + return false; + + ffStrbufAppendS(&result->theme, xcursor_theme); + ffStrbufAppendS(&result->size, getenv("XCURSOR_SIZE")); + + return true; +} + +void ffDetectCursor(const FFinstance* instance, FFCursorResult* result) +{ + const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance); + + if(ffStrbufCompS(&wmde->wmPrettyName, "WSLg") == 0) + { + ffStrbufAppendS(&result->error, "WSLg uses native windows cursor"); + return; + } + + if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0) + { + ffStrbufAppendS(&result->error, "Cursor isn't supported in TTY"); + return; + } + + if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0) + return detectCursorFromConfigFile(instance, "kcminputrc", "cursorTheme =", "Breeze", "cursorSize =", "24", result); + + if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "XFCE")) + return detectCursorXFCE(instance, result); + + if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "LXQt")) + return detectCursorFromConfigFile(instance, "lxqt/session.conf", "cursor_theme =", "Adwaita", "cursor_size =", "24", result); + + if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0) + return detectCursorGTK(instance, result); + + if( + detectCursorFromEnv(instance, result) || + detectCursorFromXDG(instance, true, result) || + detectCursorFromXResources(instance, result) || + detectCursorFromXDG(instance, false, result) + ) return; + + detectCursorGTK(instance, result); +} diff --git a/src/detection/cursor/cursor_nosupport.c b/src/detection/cursor/cursor_nosupport.c new file mode 100644 index 000000000..c548b2f6c --- /dev/null +++ b/src/detection/cursor/cursor_nosupport.c @@ -0,0 +1,6 @@ +#include "cursor.h" + +void ffDetectCursor(const FFinstance* instance, FFCursorResult* result) +{ + ffStrbufInitS(&result->error, "Not supported on this platform"); +} diff --git a/src/detection/cursor/cursor_windows.c b/src/detection/cursor/cursor_windows.c new file mode 100644 index 000000000..c820a33e7 --- /dev/null +++ b/src/detection/cursor/cursor_windows.c @@ -0,0 +1,12 @@ +#include "cursor.h" + +#include "util/windows/register.h" + +void ffDetectCursor(const FFinstance* instance, FFCursorResult* result) +{ + FF_UNUSED(instance); + + FF_HKEY_AUTO_DESTROY hKey; + if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Cursors", &hKey, &result->error)) + ffRegReadStrbuf(hKey, NULL, &result->theme, &result->error); +} diff --git a/src/detection/font/font.h b/src/detection/font/font.h index 80bed7a39..822815d2a 100644 --- a/src/detection/font/font.h +++ b/src/detection/font/font.h @@ -14,7 +14,7 @@ typedef struct FFFontResult /** * Linux / BSD: QT, GTK2, GTK3, GTK4 * MacOS: System, User, System Mono, User Mono - * Windows: Desktop, Unset, Unset, Unset + * Windows: Caption, Menu, Message, Status * Other: Unset, Unset, Unset, Unset */ FFstrbuf fonts[FF_DETECT_FONT_NUM_FONTS]; diff --git a/src/detection/font/font_windows.c b/src/detection/font/font_windows.c new file mode 100644 index 000000000..b2595d04d --- /dev/null +++ b/src/detection/font/font_windows.c @@ -0,0 +1,22 @@ +#include "font.h" +#include "util/windows/unicode.h" + +#include + +void ffDetectFontImpl(const FFinstance* instance, FFFontResult* result) +{ + FF_UNUSED(instance); + + NONCLIENTMETRICSW info = { .cbSize = sizeof(info) }; + if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0)) + ffStrbufAppendS(&result->error, "SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed"); + + LOGFONTW* fonts[4] = { &info.lfCaptionFont, &info.lfMenuFont, &info.lfMessageFont, &info.lfStatusFont }; + + for(uint32_t i = 0; i < sizeof(fonts) / sizeof(fonts[0]); ++i) + { + ffWcharToUtf8(fonts[i]->lfFaceName, &result->fonts[i]); + if(fonts[i]->lfHeight < 0) + ffStrbufAppendF(&result->fonts[i], " (%dpt)", (int)-fonts[i]->lfHeight); + } +} diff --git a/src/detection/font/font_windows.cpp b/src/detection/font/font_windows.cpp deleted file mode 100644 index fd400fb71..000000000 --- a/src/detection/font/font_windows.cpp +++ /dev/null @@ -1,32 +0,0 @@ -extern "C" { -#include "font.h" -#include "common/font.h" -} -#include "util/windows/wmi.hpp" - -#include - -extern "C" -void ffDetectFontImpl(const FFinstance* instance, FFFontResult* result) -{ - wchar_t sql[256] = {}; - swprintf(sql, 256, L"SELECT IconTitleFaceName, IconTitleSize FROM Win32_Desktop WHERE Name LIKE '%%\\\\%s'", instance->state.passwd->pw_name); - - FFWmiQuery query(sql, &result->error); - if(!query) - return; - - if(FFWmiRecord record = query.next()) - { - FF_STRBUF_AUTO_DESTROY fontName; - ffStrbufInit(&fontName); - record.getString(L"IconTitleFaceName", &fontName); - - uint64_t fontSize; - record.getUnsigned(L"IconTitleSize", &fontSize); - - ffStrbufAppendF(&result->fonts[0], "%*s (%upt)", fontName.length, fontName.chars, (unsigned)fontSize); - } - else - ffStrbufInitS(&result->error, "No WMI result returned"); -} diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h index 08b90d3be..16bd2ff53 100644 --- a/src/detection/gpu/gpu.h +++ b/src/detection/gpu/gpu.h @@ -8,6 +8,10 @@ #define FF_GPU_TEMP_UNSET (0/0.0) #define FF_GPU_CORE_COUNT_UNSET -1 +#define FF_GPU_VENDOR_NAME_AMD "AMD" +#define FF_GPU_VENDOR_NAME_INTEL "Intel" +#define FF_GPU_VENDOR_NAME_NVIDIA "NVIDIA" + typedef struct FFGPUResult { FFstrbuf vendor; diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 8266357ed..17b40d28a 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -1,10 +1,6 @@ #include "gpu.h" #include "detection/vulkan.h" -#define FF_GPU_VENDOR_NAME_AMD "AMD" -#define FF_GPU_VENDOR_NAME_INTEL "Intel" -#define FF_GPU_VENDOR_NAME_NVIDIA "NVIDIA" - #ifdef FF_HAVE_LIBPCI #include "common/library.h" #include "common/properties.h" diff --git a/src/detection/gpu/gpu_windows.cpp b/src/detection/gpu/gpu_windows.cpp index 374c8e035..7254bc2b4 100644 --- a/src/detection/gpu/gpu_windows.cpp +++ b/src/detection/gpu/gpu_windows.cpp @@ -1,13 +1,62 @@ extern "C" { #include "gpu.h" +#include "util/windows/unicode.h" } + +#ifdef FF_USE_WIN_GPU_DXGI + +#include +#include + +static const char* detectWithDxgi(FFlist* gpus) +{ + IDXGIFactory1* pFactory; + if(FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&pFactory)))) + return "CreateDXGIFactory1() failed"; + + for(unsigned iAdapter = 0;; ++iAdapter) + { + IDXGIAdapter1* adapter; + if(FAILED(pFactory->EnumAdapters1(iAdapter, &adapter))) + break; + + DXGI_ADAPTER_DESC1 desc; + if(FAILED(adapter->GetDesc1(&desc)) || (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) + continue; + + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + + if(wmemchr((const wchar_t[]) {0x1002, 0x1022}, (wchar_t)desc.VendorId, 2)) + ffStrbufInitS(&gpu->vendor, FF_GPU_VENDOR_NAME_AMD); + else if(wmemchr((const wchar_t[]) {0x03e7, 0x8086, 0x8087}, (wchar_t)desc.VendorId, 3)) + ffStrbufInitS(&gpu->vendor, FF_GPU_VENDOR_NAME_INTEL); + else if(wmemchr((const wchar_t[]) {0x0955, 0x10de, 0x12d2}, (wchar_t)desc.VendorId, 3)) + ffStrbufInitS(&gpu->vendor, FF_GPU_VENDOR_NAME_NVIDIA); + else + ffStrbufInit(&gpu->vendor); + + ffStrbufInit(&gpu->name); + ffWcharToUtf8(desc.Description, &gpu->name); + + ffStrbufInit(&gpu->driver); + + adapter->Release(); + + gpu->temperature = FF_GPU_TEMP_UNSET; + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + } + + pFactory->Release(); + + return NULL; +} + +#else + #include "util/windows/wmi.hpp" -extern "C" -const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) +static const char* detectWithWmi(FFlist* gpus) { - FF_UNUSED(instance); - FFWmiQuery query(L"SELECT Name, AdapterCompatibility, DriverVersion FROM Win32_VideoController", nullptr); if(!query) return "Query WMI service failed"; @@ -36,3 +85,17 @@ const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) return nullptr; } + +#endif + +extern "C" +const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) +{ + FF_UNUSED(instance); + + #ifdef FF_USE_WIN_GPU_DXGI + return detectWithDxgi(gpus); + #else + return detectWithWmi(gpus); + #endif +} diff --git a/src/detection/host/host_windows.c b/src/detection/host/host_windows.c new file mode 100644 index 000000000..8c59eed11 --- /dev/null +++ b/src/detection/host/host_windows.c @@ -0,0 +1,27 @@ +#include "host.h" +#include "util/windows/register.h" + +void ffDetectHostImpl(FFHostResult* host) +{ + ffStrbufInit(&host->error); + + ffStrbufInit(&host->productName); + ffStrbufInit(&host->productFamily); + ffStrbufInit(&host->productVersion); + ffStrbufInit(&host->productSku); + ffStrbufInit(&host->sysVendor); + ffStrbufInit(&host->chassisType); + ffStrbufInit(&host->chassisVendor); + ffStrbufInit(&host->chassisVersion); + + FF_HKEY_AUTO_DESTROY hKey = NULL; + + if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\BIOS", &hKey, &host->error)) + return; + + ffRegReadStrbuf(hKey, L"SystemProductName", &host->productName, NULL); + ffRegReadStrbuf(hKey, L"SystemFamily", &host->productFamily, NULL); + ffRegReadStrbuf(hKey, L"SystemVersion", &host->productVersion, NULL); + ffRegReadStrbuf(hKey, L"SystemSKU", &host->productSku, NULL); + ffRegReadStrbuf(hKey, L"SystemManufacturer", &host->sysVendor, NULL); +} diff --git a/src/detection/host/host_windows.cpp b/src/detection/host/host_windows.cpp deleted file mode 100644 index cbdd440ad..000000000 --- a/src/detection/host/host_windows.cpp +++ /dev/null @@ -1,32 +0,0 @@ -extern "C" { -#include "host.h" -} -#include "util/windows/wmi.hpp" - -extern "C" void ffDetectHostImpl(FFHostResult* host) -{ - ffStrbufInit(&host->error); - - ffStrbufInit(&host->productName); - ffStrbufInit(&host->productFamily); - ffStrbufInit(&host->productVersion); - ffStrbufInit(&host->productSku); - ffStrbufInit(&host->sysVendor); - ffStrbufInit(&host->chassisType); - ffStrbufInit(&host->chassisVendor); - ffStrbufInit(&host->chassisVersion); - - FFWmiQuery query(L"SELECT Name, Version, SKUNumber, Vendor FROM Win32_ComputerSystemProduct", &host->error); - if(!query) - return; - - if(FFWmiRecord record = query.next()) - { - record.getString(L"Name", &host->productName); - record.getString(L"Version", &host->productVersion); - record.getString(L"SKUNumber", &host->productSku); - record.getString(L"Vendor", &host->sysVendor); - } - else - ffStrbufAppendS(&host->error, "No Wmi result returned"); -} diff --git a/src/detection/memory/memory_windows.c b/src/detection/memory/memory_windows.c new file mode 100644 index 000000000..2072d7232 --- /dev/null +++ b/src/detection/memory/memory_windows.c @@ -0,0 +1,11 @@ +#include "memory.h" + +void ffDetectMemoryImpl(FFMemoryStorage* ram) +{ + MEMORYSTATUSEX statex = { + .dwLength = sizeof(statex), + }; + GlobalMemoryStatusEx(&statex); + ram->bytesTotal = statex.ullTotalPhys; + ram->bytesUsed = statex.ullTotalPhys - statex.ullAvailPhys; +} diff --git a/src/detection/memory/memory_windows.cpp b/src/detection/memory/memory_windows.cpp deleted file mode 100644 index 122e28d26..000000000 --- a/src/detection/memory/memory_windows.cpp +++ /dev/null @@ -1,25 +0,0 @@ -extern "C" { -#include "memory.h" -} -#include "util/windows/wmi.hpp" - -extern "C" -void ffDetectMemoryImpl(FFMemoryStorage* ram) -{ - FFWmiQuery query(L"SELECT TotalVisibleMemorySize, FreePhysicalMemory FROM Win32_OperatingSystem", &ram->error); - if(!query) - return; - - if(FFWmiRecord record = query.next()) - { - //KB - record.getUnsigned(L"TotalVisibleMemorySize", &ram->bytesTotal); - uint64_t bytesFree; - record.getUnsigned(L"FreePhysicalMemory", &bytesFree); - ram->bytesUsed = ram->bytesTotal - bytesFree; - ram->bytesTotal *= 1024; - ram->bytesUsed *= 1024; - } - else - ffStrbufInitS(&ram->error, "No Wmi result returned"); -} diff --git a/src/detection/os/os_windows.cpp b/src/detection/os/os_windows.cpp index 5bd9d11c5..9f8c2570c 100644 --- a/src/detection/os/os_windows.cpp +++ b/src/detection/os/os_windows.cpp @@ -1,8 +1,49 @@ extern "C" { #include "os.h" +#include "util/windows/unicode.h" } #include "util/windows/wmi.hpp" +static const char* getOsNameByWmi(FFstrbuf* osName) +{ + FFWmiQuery query(L"SELECT Caption FROM Win32_OperatingSystem"); + if(!query) + return "Query WMI service failed"; + + if(FFWmiRecord record = query.next()) + { + record.getString(L"Caption", osName); + ffStrbufTrimRight(osName, ' '); + return NULL; + } + + return "No WMI result returned"; +} + +static inline void wrapFreeLibrary(HMODULE* module) +{ + if(*module) + FreeLibrary(*module); +} + +static const char* getOsNameByWinbrand(FFstrbuf* osName) +{ + //https://dennisbabkin.com/blog/?t=how-to-tell-the-real-version-of-windows-your-app-is-running-on#ver_string + if(HMODULE __attribute__((__cleanup__(wrapFreeLibrary))) hWinbrand = LoadLibraryW(L"winbrand.dll")) + { + PWSTR(WINAPI* BrandingFormatString)(PCWSTR); + (FARPROC&)BrandingFormatString = GetProcAddress(hWinbrand, "BrandingFormatString"); + if(!BrandingFormatString) + return "GetProcAddress(BrandingFormatString) failed"; + + const wchar_t* rawName = BrandingFormatString(L"%WINDOWS_LONG%"); + ffWcharToUtf8(rawName, osName); + GlobalFree((HGLOBAL)rawName); + return NULL; + } + return "LoadLibraryW(winbrand.dll) failed"; +} + extern "C" void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) { @@ -19,56 +60,54 @@ void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) ffStrbufInit(&os->systemName); ffStrbufInit(&os->architecture); - FFWmiQuery query(L"SELECT Caption, Version, BuildNumber, OSArchitecture FROM Win32_OperatingSystem"); - if(!query) + if(getOsNameByWinbrand(&os->variant) && getOsNameByWmi(&os->variant)) return; - if(FFWmiRecord record = query.next()) + ffStrbufTrimRight(&os->variant, ' '); + + //WMI returns the "Microsoft" prefix while BrandingFormatString doesn't. Make them consistant. + if(ffStrbufStartsWithS(&os->variant, "Microsoft ")) + ffStrbufSubstrAfter(&os->variant, strlen("Microsoft ") - 1); + + if(ffStrbufStartsWithS(&os->variant, "Windows ")) { - record.getString(L"Caption", &os->variant); - ffStrbufTrimRight(&os->variant, ' '); - if(ffStrbufStartsWithS(&os->variant, "Microsoft Windows ")) + ffStrbufAppendS(&os->name, "Windows"); + ffStrbufAppendS(&os->prettyName, "Windows"); + + ffStrbufSubstrAfter(&os->variant, strlen("Windows ") - 1); + + if(ffStrbufStartsWithS(&os->variant, "Server ")) { - ffStrbufAppendS(&os->name, "Microsoft Windows"); - ffStrbufAppendS(&os->prettyName, "Windows"); - - ffStrbufSubstrAfter(&os->variant, strlen("Microsoft Windows ") - 1); - - if(ffStrbufStartsWithS(&os->variant, "Server ")) - { - ffStrbufAppendS(&os->name, " Server"); - ffStrbufAppendS(&os->prettyName, " Server"); - ffStrbufSubstrAfter(&os->variant, strlen(" Server") - 1); - } - - uint32_t index = ffStrbufFirstIndexC(&os->variant, ' '); - ffStrbufAppendNS(&os->version, index, os->variant.chars); - ffStrbufSubstrAfter(&os->variant, index); - - // Windows Server 20xx Rx - if(ffStrbufEndsWithC(&os->prettyName, 'r')) - { - if(os->variant.chars[0] == 'R' && - isdigit(os->variant.chars[1]) && - (os->variant.chars[2] == '\0' || os->variant.chars[2] == ' ')) - { - ffStrbufAppendF(&os->version, " R%c", os->variant.chars[1]); - ffStrbufSubstrAfter(&os->variant, strlen("Rx ") - 1); - } - } - } - else - { - // Unknown Windows name, please report this - ffStrbufAppend(&os->name, &os->variant); - ffStrbufClear(&os->variant); + ffStrbufAppendS(&os->name, " Server"); + ffStrbufAppendS(&os->prettyName, " Server"); + ffStrbufSubstrAfter(&os->variant, strlen(" Server") - 1); } - ffStrbufAppendF(&os->id, "%*s %*s", os->prettyName.length, os->prettyName.chars, os->version.length, os->version.chars); + uint32_t index = ffStrbufFirstIndexC(&os->variant, ' '); + ffStrbufAppendNS(&os->version, index, os->variant.chars); + ffStrbufSubstrAfter(&os->variant, index); - record.getString(L"BuildNumber", &os->buildID); - record.getString(L"OSArchitecture", &os->architecture); - - ffStrbufSetS(&os->systemName, instance->state.utsname.sysname); + // Windows Server 20xx Rx + if(ffStrbufEndsWithC(&os->prettyName, 'r')) + { + if(os->variant.chars[0] == 'R' && + isdigit(os->variant.chars[1]) && + (os->variant.chars[2] == '\0' || os->variant.chars[2] == ' ')) + { + ffStrbufAppendF(&os->version, " R%c", os->variant.chars[1]); + ffStrbufSubstrAfter(&os->variant, strlen("Rx ") - 1); + } + } } + else + { + // Unknown Windows name, please report this + ffStrbufAppend(&os->name, &os->variant); + ffStrbufClear(&os->variant); + } + + ffStrbufAppendF(&os->id, "%*s %*s", os->prettyName.length, os->prettyName.chars, os->version.length, os->version.chars); + + ffStrbufSetS(&os->architecture, instance->state.utsname.machine); + ffStrbufSetS(&os->systemName, instance->state.utsname.sysname); } diff --git a/src/detection/processes/processes_windows.cpp b/src/detection/processes/processes_windows.cpp index 68de794a6..2b267c4a5 100644 --- a/src/detection/processes/processes_windows.cpp +++ b/src/detection/processes/processes_windows.cpp @@ -1,12 +1,50 @@ extern "C" { #include "processes.h" +#include "util/mallocHelper.h" } -#include "util/windows/wmi.hpp" + +#ifdef FF_USE_WIN_NTAPI + +#include uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error) { FF_UNUSED(instance); + ULONG size = 0; + if(NtQuerySystemInformation(SystemProcessInformation, nullptr, 0, &size) != (NTSTATUS)0xC0000004 /*STATUS_INFO_LENGTH_MISMATCH*/) + { + ffStrbufAppendS(error, "NtQuerySystemInformation(SystemProcessInformation, NULL) failed"); + return 0; + } + size += sizeof(SystemProcessInformation) * 5; //What if new processes are created during two syscalls? + + SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = (SYSTEM_PROCESS_INFORMATION*)malloc(size); + if(!pstart) + { + ffStrbufAppendF(error, "malloc(%u) failed", (unsigned)size); + return 0; + } + + if(!NT_SUCCESS(NtQuerySystemInformation(SystemProcessInformation, pstart, size, nullptr))) + { + ffStrbufAppendS(error, "NtQuerySystemInformation(SystemProcessInformation, pstart) failed"); + return 0; + } + + uint32_t result = 1; //Init with 1 because we test for ptr->NextEntryOffset + for (auto ptr = pstart; ptr->NextEntryOffset; ptr = (SYSTEM_PROCESS_INFORMATION*)((uint8_t*)ptr + ptr->NextEntryOffset)) + ++result; + + return result; +} + +#else + +#include "util/windows/wmi.hpp" + +uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error) +{ FFWmiQuery query(L"SELECT NumberOfProcesses FROM Win32_OperatingSystem", error); if(!query) return 0; @@ -23,3 +61,5 @@ uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error) return 0; } } + +#endif diff --git a/src/detection/swap/swap_windows.c b/src/detection/swap/swap_windows.c new file mode 100644 index 000000000..9fc1d86a4 --- /dev/null +++ b/src/detection/swap/swap_windows.c @@ -0,0 +1,11 @@ +#include "swap.h" + +void ffDetectSwapImpl(FFMemoryStorage* swap) +{ + MEMORYSTATUSEX statex = { + .dwLength = sizeof(statex), + }; + GlobalMemoryStatusEx(&statex); + swap->bytesTotal = statex.ullTotalPageFile; + swap->bytesUsed = statex.ullTotalPageFile - statex.ullAvailPageFile; +} diff --git a/src/detection/swap/swap_windows.cpp b/src/detection/swap/swap_windows.cpp deleted file mode 100644 index 6e82cccab..000000000 --- a/src/detection/swap/swap_windows.cpp +++ /dev/null @@ -1,23 +0,0 @@ -extern "C" { -#include "swap.h" -} -#include "util/windows/wmi.hpp" - -extern "C" -void ffDetectSwapImpl(FFMemoryStorage* swap) -{ - FFWmiQuery query(L"SELECT AllocatedBaseSize, CurrentUsage FROM Win32_PageFileUsage", &swap->error); - if(!query) - return; - - if(FFWmiRecord record = query.next()) - { - //MB - record.getUnsigned(L"AllocatedBaseSize", &swap->bytesTotal); - record.getUnsigned(L"CurrentUsage", &swap->bytesUsed); - swap->bytesTotal *= 1024 * 1024; - swap->bytesUsed *= 1024 * 1024; - } - else - ffStrbufInitS(&swap->error, "No Wmi result returned"); -} diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index b673ff4fd..aeff09318 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -195,7 +195,7 @@ static void detectFromWindowsTeriminal(const FFinstance* instance, const FFstrbu const char* error = NULL; #ifdef _WIN32 - if(terminalExe && terminalExe->length > 0) + if(terminalExe && terminalExe->length > 0 && !ffStrbufEqualS(terminalExe, "Windows Terminal")) { char jsonPath[MAX_PATH + 1]; if(SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, jsonPath))) diff --git a/src/detection/terminalfont/terminalfont_windows.c b/src/detection/terminalfont/terminalfont_windows.c index e6ea456be..55da491b3 100644 --- a/src/detection/terminalfont/terminalfont_windows.c +++ b/src/detection/terminalfont/terminalfont_windows.c @@ -2,9 +2,7 @@ #include "common/io.h" #include "detection/terminalshell/terminalshell.h" #include "terminalfont.h" - -#define WIN32_LEAN_AND_MEAN 1 -#include +#include "util/windows/register.h" static void detectMintty(const FFinstance* instance, FFTerminalFontResult* terminalFont) { @@ -26,47 +24,29 @@ static void detectMintty(const FFinstance* instance, FFTerminalFontResult* termi ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); } -static inline void wrapRegCloseKey(HKEY* phKey) -{ - if(*phKey) - RegCloseKey(*phKey); -} - static void detectConhost(const FFinstance* instance, FFTerminalFontResult* terminalFont) { FF_UNUSED(instance); //Current font of conhost doesn't seem to be detectable, we detect default font instead - HKEY __attribute__((__cleanup__(wrapRegCloseKey))) hKey = NULL; - if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ, &hKey) != ERROR_SUCCESS) - { - ffStrbufAppendS(&terminalFont->error, "RegOpenKeyExW() failed"); + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Console", &hKey, &terminalFont->error)) return; - } - DWORD bufSize; - - char fontName[128]; - bufSize = sizeof(fontName); - if(RegGetValueA(hKey, NULL, "FaceName", RRF_RT_REG_SZ, NULL, fontName, &bufSize) != ERROR_SUCCESS) - { - ffStrbufAppendS(&terminalFont->error, "RegGetValueA(FaceName) failed"); + FF_STRBUF_AUTO_DESTROY fontName; + ffStrbufInit(&fontName); + if(!ffRegReadStrbuf(hKey, L"FaceName", &fontName, &terminalFont->error)) return; - } uint32_t fontSizeNum = 0; - bufSize = sizeof(fontSizeNum); - if(RegGetValueW(hKey, NULL, L"FontSize", RRF_RT_DWORD, NULL, &fontSizeNum, &bufSize) != ERROR_SUCCESS) - { - ffStrbufAppendS(&terminalFont->error, "RegGetValueW(FontSize) failed"); + if(!ffRegReadUint(hKey, L"FontSize", &fontSizeNum, &terminalFont->error)) return; - } char fontSize[16]; _ultoa((unsigned long)(fontSizeNum >> 16), fontSize, 10); - ffFontInitValues(&terminalFont->font, fontName, fontSize); + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize); } static void detectConEmu(const FFinstance* instance, FFTerminalFontResult* terminalFont) diff --git a/src/detection/terminalshell/terminalshell_windows.cpp b/src/detection/terminalshell/terminalshell_windows.cpp index 6f0139a2e..135388650 100644 --- a/src/detection/terminalshell/terminalshell_windows.cpp +++ b/src/detection/terminalshell/terminalshell_windows.cpp @@ -4,13 +4,11 @@ extern "C" { #include "common/thread.h" } -#include #include #include +#include -#include - -#ifdef FF_USE_WIN_FAST_PPID_DETECTION +#ifdef FF_USE_WIN_NTAPI #include @@ -59,6 +57,7 @@ static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrb #else #include "util/windows/wmi.hpp" +#include static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe, const char** exeName) { @@ -140,12 +139,33 @@ static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "powershell_ise")) ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell ISE"); else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "cmd")) - ffStrbufSetS(&result->shellPrettyName, "Command Prompt"); + { + ffStrbufClear(&result->shellPrettyName); + + HANDLE snapshot; + while(!(snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)) && GetLastError() == ERROR_BAD_LENGTH) {} + + if(snapshot) + { + MODULEENTRY32W module = { .dwSize = sizeof(module) }; + for(BOOL success = Module32FirstW(snapshot, &module); success; success = Module32NextW(snapshot, &module)) + { + if(wcsncmp(module.szModule, L"clink_dll_", wcslen(L"clink_dll_")) == 0) + { + ffStrbufAppendS(&result->shellPrettyName, "CMD (with Clink)"); + break; + } + } + CloseHandle(snapshot); + } + if(result->shellPrettyName.length == 0) + ffStrbufAppendS(&result->shellPrettyName, "Command Prompt"); + } else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "nu")) ffStrbufSetS(&result->shellPrettyName, "nushell"); - else if(ffStrbufIgnCaseEqualS(&result->terminalPrettyName, "explorer")) + else if(ffStrbufIgnCaseEqualS(&result->shellPrettyName, "explorer")) { - ffStrbufSetS(&result->terminalPrettyName, "Windows Explorer"); // Started without shell + ffStrbufSetS(&result->shellPrettyName, "Windows Explorer"); // Started without shell return 0; } @@ -199,12 +219,7 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) { if( result->terminalProcessName.length > 0 && - !ffStrbufStartsWithIgnCaseS(&result->terminalProcessName, "login") && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "(login)") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "systemd") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "init") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "(init)") != 0 && - ffStrbufIgnCaseCompS(&result->terminalProcessName, "0") != 0 + ffStrbufIgnCaseCompS(&result->terminalProcessName, "explorer") != 0 ) return; const char* term = nullptr; diff --git a/src/detection/users/users_windows.c b/src/detection/users/users_windows.c new file mode 100644 index 000000000..46e5b4add --- /dev/null +++ b/src/detection/users/users_windows.c @@ -0,0 +1,55 @@ +#include "users.h" +#include "util/windows/unicode.h" + +#include + +//at the time of writing, of MinGW doesn't have the definition of WTSEnumerateSessionsExW +typedef struct _WTS_SESSION_INFO_1W { + DWORD ExecEnvId; + WTS_CONNECTSTATE_CLASS State; + DWORD SessionId; + LPWSTR pSessionName; + LPWSTR pHostName; + LPWSTR pUserName; + LPWSTR pDomainName; + LPWSTR pFarmName; +} WTS_SESSION_INFO_1W, * PWTS_SESSION_INFO_1W; + +BOOL +WINAPI +WTSEnumerateSessionsExW( + HANDLE hServer, + DWORD* pLevel, + DWORD Filter, + PWTS_SESSION_INFO_1W* ppSessionInfo, + DWORD* pCount); + +void ffDetectUsers(FFlist* users, FFstrbuf* error) +{ + WTS_SESSION_INFO_1W* sessionInfo; + DWORD sessionCount; + DWORD level = 1; + + if(!WTSEnumerateSessionsExW(WTS_CURRENT_SERVER_HANDLE, &level, 0, &sessionInfo, &sessionCount)) + { + ffStrbufAppendS(error, "WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE) failed"); + return; + } + + for (DWORD i = 0; i < sessionCount; i++) + { + WTS_SESSION_INFO_1W* session = &sessionInfo[i]; + if(session->State != WTSActive) + continue; + + FF_STRBUF_AUTO_DESTROY domainName = ffStrbufFromWchar(session->pDomainName); + FF_STRBUF_AUTO_DESTROY userName = ffStrbufFromWchar(session->pUserName); + + ffStrbufInitF((FFstrbuf*)ffListAdd(users), "%s\\%s", domainName.chars, userName.chars); + } + + WTSFreeMemory(sessionInfo); + + if(users->length == 0) + ffStrbufAppendS(error, "Unable to detect users"); +} diff --git a/src/detection/users/users_windows.cpp b/src/detection/users/users_windows.cpp deleted file mode 100644 index 9e50cd365..000000000 --- a/src/detection/users/users_windows.cpp +++ /dev/null @@ -1,35 +0,0 @@ -extern "C" { -#include "users.h" -} -#include "util/windows/wmi.hpp" - -void ffDetectUsers(FFlist* users, FFstrbuf* error) -{ - FFWmiQuery query(L"SELECT Antecedent FROM Win32_LoggedOnUser", error); - if(!query) - return; - -next: - while(FFWmiRecord record = query.next()) - { - FFstrbuf antecedent; - ffStrbufInit(&antecedent); - record.getString(L"Antecedent", &antecedent); // \\.\root\cimv2:Win32_Account.Domain="DOMAIN",Name="NAME" - ffStrbufTrimRight(&antecedent, '"'); // \\.\root\cimv2:Win32_Account.Domain="DOMAIN",Name="NAME - ffStrbufSubstrAfterFirstC(&antecedent, '"'); // DOMAIN",Name="NAME - uint32_t index = ffStrbufFirstIndexC(&antecedent, '"'); - ffStrbufRemoveSubstr(&antecedent, index, ffStrbufLastIndexC(&antecedent, '"')); // DOMAIN"NAME - antecedent.chars[index] = '\\'; - - for(uint32_t i = 0; i < users->length; ++i) - { - if(ffStrbufComp((FFstrbuf*)ffListGet(users, i), &antecedent) == 0) - goto next; - } - - *(FFstrbuf*)ffListAdd(users) = antecedent; - } - - if(users->length == 0) - ffStrbufAppendS(error, "Unable to detect users"); -} diff --git a/src/detection/vulkan.c b/src/detection/vulkan.c index 685de47b8..200785892 100644 --- a/src/detection/vulkan.c +++ b/src/detection/vulkan.c @@ -182,7 +182,7 @@ static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* resu //Add the device to the list of devices shown by the GPU module - //We don't want softare rasterizers to show up as physical gpu + //We don't want software rasterizers to show up as physical gpu if(physicalDeviceProperties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) continue; diff --git a/src/detection/wmtheme/wmtheme_windows.c b/src/detection/wmtheme/wmtheme_windows.c index 00303300d..e19e9bcbc 100644 --- a/src/detection/wmtheme/wmtheme_windows.c +++ b/src/detection/wmtheme/wmtheme_windows.c @@ -1,70 +1,117 @@ #include "fastfetch.h" #include "wmtheme.h" +#include "util/windows/register.h" -#define WIN32_LEAN_AND_MEAN 1 -#include - -static inline void wrapRegCloseKey(HKEY* phKey) +const char* colorHexToString(DWORD hex) { - if(*phKey) - RegCloseKey(*phKey); + switch(hex) + { + case 0x696cc3: return "Yellow gold"; + case 0xff8c00: return "Gold"; + case 0xf7630c: return "Orange bright"; + case 0xca5010: return "Orange dark"; + case 0xda3b01: return "Rust"; + case 0xef6950: return "Pale rust"; + case 0xd13438: return "Brick red"; + case 0xff4343: return "Mod red"; + case 0xe74856: return "Pale red"; + case 0xe81123: return "Red"; + case 0xea005e: return "Rose bright"; + case 0xc30052: return "Rose"; + case 0xe3008c: return "Plum light"; + case 0xbf0077: return "Plum"; + case 0xc239b3: return "Orchid light"; + case 0x9a0089: return "Orchid"; + case 0x0078d4: return "Blue"; + case 0x0063b1: return "Navy blue"; + case 0x8d8bd7: return "Purple shadow"; + case 0x6b69d6: return "Purple shadow dark"; + case 0x8764b8: return "Iris pastel"; + case 0x744da9: return "Iris Spring"; + case 0xb146c2: return "Violet red light"; + case 0x881798: return "Violet red"; + case 0x0099bc: return "Cool blue bright"; + case 0x2d7d9a: return "Cool blue"; + case 0x00b7c3: return "Seafoam"; + case 0x038387: return "Seafoam teal"; + case 0x00b294: return "Mint light"; + case 0x018574: return "Mint dark"; + case 0x00cc6a: return "Turf green"; + case 0x10893e: return "Sport green"; + case 0x7a7574: return "Gray"; + case 0x5d5a58: return "Gray brown"; + case 0x68768a: return "Steel blue"; + case 0x515c6b: return "Metal blue"; + case 0x567c73: return "Pale moss"; + case 0x486860: return "Moss"; + case 0x498205: return "Meadow green"; + case 0x107c10: return "Green"; + case 0x767676: return "Overcast"; + case 0x4c4a48: return "Storm"; + case 0x69797e: return "Blue gray"; + case 0x4a5459: return "Gray dark"; + case 0x647c64: return "Liddy green"; + case 0x4c574e: return "Sage"; + case 0x807143: return "Camouflage desert"; + case 0x766c59: return "Camouflage"; + default: return NULL; + } } bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) { FF_UNUSED(instance); - HKEY __attribute__((__cleanup__(wrapRegCloseKey))) hKey = NULL; - if(RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - int SystemUsesLightTheme = 1; - DWORD bufSize = sizeof(SystemUsesLightTheme); - - if(RegGetValueW(hKey, NULL, L"SystemUsesLightTheme", RRF_RT_DWORD, NULL, &SystemUsesLightTheme, &bufSize) != ERROR_SUCCESS) + uint32_t bgrColor; + DWORD bufSize = sizeof(bgrColor); + if(RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"AccentColor", RRF_RT_REG_DWORD, NULL, &bgrColor, &bufSize) == ERROR_SUCCESS) { - ffStrbufAppendS(themeOrError, "RegGetValueW(SystemUsesLightTheme) failed"); - return false; + ffStrbufAppendS(themeOrError, "Accent Color - "); + DWORD rgbColor = ((bgrColor & 0xFF) << 16) | (bgrColor & 0xFF00) | ((bgrColor >> 16) & 0xFF); + const char* text = colorHexToString(rgbColor); + if(text) + ffStrbufAppendS(themeOrError, text); + else + ffStrbufAppendF(themeOrError, "#%06lX", rgbColor); } - - int AppsUsesLightTheme = 1; - bufSize = sizeof(AppsUsesLightTheme); - if(RegGetValueW(hKey, NULL, L"AppsUseLightTheme", RRF_RT_DWORD, NULL, &AppsUsesLightTheme, &bufSize) != ERROR_SUCCESS) - { - ffStrbufAppendS(themeOrError, "RegGetValueW(AppsUseLightTheme) failed"); - return false; - } - - ffStrbufAppendF(themeOrError, "System - %s, Apps - %s", SystemUsesLightTheme ? "Light" : "Dark", AppsUsesLightTheme ? "Light" : "Dark"); - - return true; } - else if(RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes", 0, KEY_READ, &hKey) == ERROR_SUCCESS) + + FF_HKEY_AUTO_DESTROY hKey = NULL; + if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", &hKey, NULL)) { - DWORD length = 0; - if(RegGetValueA(hKey, NULL, "CurrentTheme", RRF_RT_REG_SZ, NULL, NULL, &length) != ERROR_SUCCESS) + uint32_t value = 1; + if(ffRegReadUint(hKey, L"SystemUsesLightTheme", &value, NULL)) { - ffStrbufAppendS(themeOrError, "RegGetValueA(CurrentTheme, NULL) failed"); - return false; + if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, ", "); + ffStrbufAppendF(themeOrError, "System - %s", value ? "Light" : "Dark"); } - ffStrbufEnsureFree(themeOrError, length); - if(RegGetValueA(hKey, NULL, "CurrentTheme", RRF_RT_REG_SZ, NULL, themeOrError->chars, &length) != ERROR_SUCCESS) + if(ffRegReadUint(hKey, L"AppsUseLightTheme", &value, NULL)) { - ffStrbufAppendS(themeOrError, "RegGetValueA(CurrentTheme) failed"); - return false; + if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, ", "); + ffStrbufAppendF(themeOrError, "Apps - %s", value ? "Light" : "Dark"); } - - themeOrError->length = length; - ffStrbufSubstrBeforeLastC(themeOrError, '.'); - ffStrbufSubstrAfterLastC(themeOrError, '\\'); - if (isalpha(themeOrError->chars[0])) - themeOrError->chars[0] = (char)toupper(themeOrError->chars[0]); - - return true; } - else + else if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes", &hKey, NULL)) + { + FF_STRBUF_AUTO_DESTROY theme; + ffStrbufInit(&theme); + if(ffRegReadStrbuf(hKey, L"CurrentTheme", &theme, NULL)) + { + ffStrbufSubstrBeforeLastC(themeOrError, '.'); + ffStrbufSubstrAfterLastC(themeOrError, '\\'); + if(isalpha(themeOrError->chars[0])) + themeOrError->chars[0] = (char)toupper(themeOrError->chars[0]); + if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, ", "); + ffStrbufAppendF(themeOrError, "Theme - %s", theme.chars); + } + } + + if(themeOrError->length == 0) { ffStrbufAppendS(themeOrError, "Failed to find current theme"); return false; } + return true; } diff --git a/src/logo/builtin.c b/src/logo/builtin.c index bf95a9ba9..0cdaaa520 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1336,23 +1336,23 @@ static const FFlogo* getLogoWindows11() FF_LOGO_NAMES("Windows 11", "Windows Server 2022") FF_LOGO_LINES( "$1\n" - ",,**************/ ///////////////()\n" - "****************/ ///////////////((\n" - "***************// //////////////(((\n" - "**************/// ////////////(((((\n" - "************///// /////////((((((((\n" - "*********//////// /////((((((((((((\n" - "*******////////// ///((((((((((((((\n" - "****///////////// (((((((((((((((((\n" + ",,**************/ ///////////////()\n" + "****************/ ///////////////((\n" + "***************// //////////////(((\n" + "**************/// ////////////(((((\n" + "************///// /////////((((((((\n" + "*********//////// /////((((((((((((\n" + "*******////////// ///((((((((((((((\n" + "****///////////// (((((((((((((((((\n" "\n" - "/////////////(((( (((((((((((((((((\n" - "////////////((((( (((((((((((((((((\n" - "///////////(((((( (((((((((((((((((\n" - "/////////(((((((( (((((((((((((((((\n" - "///////(((((((((( (((((((((((((((((\n" - "////((((((((((((( (((((((((((((((((\n" - "((((((((((((((((( (((((((((((((((((\n" - "((((((((((((((((( (((((((((((((((()" + "/////////////(((( (((((((((((((((((\n" + "////////////((((( (((((((((((((((((\n" + "///////////(((((( (((((((((((((((((\n" + "/////////(((((((( (((((((((((((((((\n" + "///////(((((((((( (((((((((((((((((\n" + "////((((((((((((( (((((((((((((((((\n" + "((((((((((((((((( (((((((((((((((((\n" + "((((((((((((((((( (((((((((((((((()" ) FF_LOGO_COLORS( "34" //blue @@ -1362,32 +1362,26 @@ static const FFlogo* getLogoWindows11() FF_LOGO_RETURN } -static const FFlogo* getLogoWindows11Old() +static const FFlogo* getLogoWindows11Small() { FF_LOGO_INIT - FF_LOGO_NAMES("Windows 11_old") + FF_LOGO_NAMES("Windows 11_small", "Windows 11-small") FF_LOGO_LINES( "$1\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" "\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################\n" - "################ ################" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" + "lllllllll lllllllll\n" ) FF_LOGO_COLORS( "34" //blue ) - FF_LOGO_COLOR_KEYS("34"); //blue + FF_LOGO_COLOR_KEYS("33"); //yellow FF_LOGO_COLOR_TITLE("36"); //cyan FF_LOGO_RETURN } @@ -2149,7 +2143,7 @@ static const FFlogo* getLogoUbuntuSmall() static const FFlogo* getLogoVanilla() { - + FF_LOGO_INIT FF_LOGO_NAMES("vanilla", "vanilla-os","vanilla-linux"); FF_LOGO_LINES( @@ -2314,7 +2308,7 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoMintOld, getLogoMsys2, getLogoWindows11, - getLogoWindows11Old, + getLogoWindows11Small, getLogoWindows8, getLogoWindows, getLogoNixOS, diff --git a/src/modules/cursor.c b/src/modules/cursor.c index c7285427e..a5ddc90be 100644 --- a/src/modules/cursor.c +++ b/src/modules/cursor.c @@ -1,255 +1,55 @@ #include "fastfetch.h" -#include "common/properties.h" -#include "common/printing.h" -#include "common/parsing.h" -#include "common/settings.h" -#include "detection/gtk.h" -#include "detection/displayserver/displayserver.h" -#include +#include "common/printing.h" +#include "detection/cursor/cursor.h" #define FF_CURSOR_MODULE_NAME "Cursor" #define FF_CURSOR_NUM_FORMAT_ARGS 2 -#if !(defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32)) - -static void printCursor(FFinstance* instance, FFstrbuf* cursorTheme, const FFstrbuf* cursorSize) +static void printCursor(FFinstance* instance, FFCursorResult* cursor) { - ffStrbufRemoveIgnCaseEndS(cursorTheme, "cursors"); - ffStrbufRemoveIgnCaseEndS(cursorTheme, "cursor"); - ffStrbufTrimRight(cursorTheme, '_'); - ffStrbufTrimRight(cursorTheme, '-'); - if(cursorTheme->length == 0) - ffStrbufAppendS(cursorTheme, "default"); + ffStrbufRemoveIgnCaseEndS(&cursor->theme, "cursors"); + ffStrbufRemoveIgnCaseEndS(&cursor->theme, "cursor"); + ffStrbufTrimRight(&cursor->theme, '_'); + ffStrbufTrimRight(&cursor->theme, '-'); + if(cursor->theme.length == 0) + ffStrbufAppendS(&cursor->theme, "default"); if(instance->config.cursor.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor.key); - ffStrbufWriteTo(cursorTheme, stdout); + ffStrbufWriteTo(&cursor->theme, stdout); - if(cursorSize != NULL && cursorSize->length > 0) - { - fputs(" (", stdout); - ffStrbufWriteTo(cursorSize, stdout); - fputs("px)", stdout); - } + if(cursor->size.length > 0) + printf(" (%spx)", cursor->size.chars); putchar('\n'); } else { ffPrintFormat(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, FF_CURSOR_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRBUF, cursorTheme}, - {FF_FORMAT_ARG_TYPE_STRBUF, cursorSize} + {FF_FORMAT_ARG_TYPE_STRBUF, &cursor->theme}, + {FF_FORMAT_ARG_TYPE_STRBUF, &cursor->size} }); } } -static void printCursorGTK(FFinstance* instance) -{ - const FFGTKResult* gtk = ffDetectGTK4(instance); - - if(gtk->cursor.length == 0) - gtk = ffDetectGTK3(instance); - - if(gtk->cursor.length == 0) - gtk = ffDetectGTK2(instance); - - if(gtk->cursor.length == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Couldn't detect GTK Cursor"); - return; - } - - //gtk->cursor is const, so we don't want to modify it - FFstrbuf theme; - ffStrbufInitCopy(&theme, >k->cursor); - - printCursor(instance, &theme, >k->cursorSize); - - ffStrbufDestroy(&theme); -} - -static void printCursorXFCE(FFinstance* instance) -{ - FFstrbuf cursorTheme; - ffStrbufInit(&cursorTheme); - - ffStrbufAppendS(&cursorTheme, ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeName", FF_VARIANT_TYPE_STRING).strValue); - - if(cursorTheme.length == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Couldn't find xfce cursor in xfconf (xsettings::/Gtk/CursorThemeName)"); - return; - } - - FFstrbuf cursorSize; - ffStrbufInit(&cursorSize); - int cursorSizeVal = ffSettingsGetXFConf(instance, "xsettings", "/Gtk/CursorThemeSize", FF_VARIANT_TYPE_INT).intValue; - if(cursorSizeVal > 0) - ffStrbufAppendF(&cursorSize, "%i", cursorSizeVal); - - printCursor(instance, &cursorTheme, &cursorSize); - ffStrbufDestroy(&cursorTheme); - ffStrbufDestroy(&cursorSize); -} - -static void printCursorFromConfigFile(FFinstance* instance, const char* relativeFilePath, const char* themeStart, const char* themeDefault, const char* sizeStart, const char* sizeDefault) -{ - FFstrbuf cursorTheme; - ffStrbufInit(&cursorTheme); - - FFstrbuf cursorSize; - ffStrbufInit(&cursorSize); - - if(ffParsePropFileConfigValues(instance, relativeFilePath, 2, (FFpropquery[]) { - {themeStart, &cursorTheme}, - {sizeStart, &cursorSize} - })) { - - if(cursorTheme.length == 0) - ffStrbufAppendS(&cursorTheme, themeDefault); - - if(cursorSize.length == 0) - ffStrbufAppendS(&cursorSize, sizeDefault); - } - - if(cursorTheme.length == 0) - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Couldn't find cursor in %s", relativeFilePath); - else - printCursor(instance, &cursorTheme, &cursorSize); - - ffStrbufDestroy(&cursorTheme); - ffStrbufDestroy(&cursorSize); -} - -static bool printCursorFromXResources(FFinstance* instance) -{ - FFstrbuf theme; - ffStrbufInit(&theme); - - FFstrbuf size; - ffStrbufInit(&size); - - ffParsePropFileHomeValues(instance, ".Xresources", 2, (FFpropquery[]) { - {"Xcursor.theme :", &theme}, - {"Xcursor.size :", &size} - }); - - if(theme.length == 0) - { - ffStrbufDestroy(&size); - ffStrbufDestroy(&theme); - return false; - } - - printCursor(instance, &theme, &size); - ffStrbufDestroy(&size); - ffStrbufDestroy(&theme); - return true; -} - -static bool printCursorFromXDG(FFinstance* instance, bool user) -{ - FFstrbuf theme; - ffStrbufInit(&theme); - - if(user) - ffParsePropFileHome(instance, ".icons/default/index.theme", "Inherits =", &theme); - else - ffParsePropFile(FASTFETCH_TARGET_DIR_USR"/share/icons/default/index.theme", "Inherits =", &theme); - - if(theme.length == 0) - { - ffStrbufDestroy(&theme); - return false; - } - - printCursor(instance, &theme, NULL); - ffStrbufDestroy(&theme); - return true; -} - -static bool printCursorFromEnv(FFinstance* instance) -{ - const char* xcursor_theme = getenv("XCURSOR_THEME"); - - if(!ffStrSet(xcursor_theme)) - return false; - - FFstrbuf theme; - ffStrbufInit(&theme); - ffStrbufAppendS(&theme, xcursor_theme); - - FFstrbuf size; - ffStrbufInit(&size); - ffStrbufAppendS(&size, getenv("XCURSOR_SIZE")); - - printCursor(instance, &theme, &size); - - ffStrbufDestroy(&size); - ffStrbufDestroy(&theme); - return true; -} - -#endif void ffPrintCursor(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) + FFCursorResult result; + ffStrbufInit(&result.error); + ffStrbufInit(&result.theme); + ffStrbufInit(&result.size); - FF_UNUSED(instance); - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Cursor detection is not supported"); - return; + ffDetectCursor(instance, &result); - #else + if(result.error.length) + ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "%s", result.error.chars); + else + printCursor(instance, &result); - const FFDisplayServerResult* wmde = ffConnectDisplayServer(instance); - - if(ffStrbufCompS(&wmde->wmPrettyName, "WSLg") == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "WSLg uses native windows cursor"); - return; - } - - if(ffStrbufIgnCaseCompS(&wmde->wmProtocolName, "TTY") == 0) - { - ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Cursor isn't supported in TTY"); - return; - } - - if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "KDE Plasma") == 0) - { - printCursorFromConfigFile(instance, "kcminputrc", "cursorTheme =", "Breeze", "cursorSize =", "24"); - return; - } - - if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "XFCE")) - { - printCursorXFCE(instance); - return; - } - - if(ffStrbufStartsWithIgnCaseS(&wmde->dePrettyName, "LXQt")) - { - printCursorFromConfigFile(instance, "lxqt/session.conf", "cursor_theme =", "Adwaita", "cursor_size =", "24"); - return; - } - - if(ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Gnome") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Cinnamon") == 0 || ffStrbufIgnCaseCompS(&wmde->dePrettyName, "Mate") == 0) - { - printCursorGTK(instance); - return; - } - - if( - printCursorFromEnv(instance) || - printCursorFromXDG(instance, true) || - printCursorFromXResources(instance) || - printCursorFromXDG(instance, false) - ) return; - - printCursorGTK(instance); - - #endif + ffStrbufDestroy(&result.error); + ffStrbufDestroy(&result.theme); + ffStrbufDestroy(&result.size); } diff --git a/src/modules/font.c b/src/modules/font.c index 9ca20b547..25f394f61 100644 --- a/src/modules/font.c +++ b/src/modules/font.c @@ -45,9 +45,14 @@ static void printFont(const FFFontResult* font) static void printFont(const FFFontResult* font) { - if(font->fonts[0].length > 0) + const char* types[] = { "Caption", "Menu", "Message", "Status" }; + for(uint32_t i = 0; i < sizeof(types) / sizeof(types[0]); ++i) { - printf("%s [Desktop]", font->fonts[0].chars); + if(font->fonts[i].length > 0) + { + printf("%s [%s]", font->fonts[i].chars, types[i]); + break; + } } } diff --git a/src/modules/host.c b/src/modules/host.c index 8c3ec65d7..cd4f50106 100644 --- a/src/modules/host.c +++ b/src/modules/host.c @@ -35,8 +35,7 @@ void ffPrintHost(FFinstance* instance) if(host->productVersion.length > 0) { - ffStrbufAppendC(&output, ' '); - ffStrbufAppend(&output, &host->productVersion); + ffStrbufAppendF(&output, " (%s)", host->productVersion.chars); } ffPrintAndWriteToCache(instance, FF_HOST_MODULE_NAME, &instance->config.host, &output, FF_HOST_NUM_FORMAT_ARGS, (FFformatarg[]) { diff --git a/src/util/mallocHelper.h b/src/util/mallocHelper.h new file mode 100644 index 000000000..a4fd8eb6e --- /dev/null +++ b/src/util/mallocHelper.h @@ -0,0 +1,9 @@ +#include + +static inline void ffWrapFree(void* pPtr) +{ + if(*(void**)pPtr) + free(*(void**)pPtr); +} + +#define FF_AUTO_FREE __attribute__((__cleanup__(ffWrapFree))) diff --git a/src/util/windows/register.c b/src/util/windows/register.c new file mode 100644 index 000000000..9b0e916b0 --- /dev/null +++ b/src/util/windows/register.c @@ -0,0 +1,82 @@ +#include "register.h" +#include "unicode.h" +#include "util/mallocHelper.h" + +static const char* hKey2Str(HKEY hKey) +{ + #define HKEY_CASE(compareKey) if(hKey == compareKey) return #compareKey; + HKEY_CASE(HKEY_CLASSES_ROOT) + HKEY_CASE(HKEY_CURRENT_USER) + HKEY_CASE(HKEY_LOCAL_MACHINE) + HKEY_CASE(HKEY_USERS) + HKEY_CASE(HKEY_PERFORMANCE_DATA) + HKEY_CASE(HKEY_PERFORMANCE_TEXT) + HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT) + HKEY_CASE(HKEY_CURRENT_CONFIG) + HKEY_CASE(HKEY_DYN_DATA) + HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS) + #undef HKEY_CASE + + return "UNKNOWN"; +} + +bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error) +{ + if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS) + { + if(error) + { + FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufFromWchar(subKeyW); + ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars); + } + return false; + } + return true; +} + +bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error) +{ + DWORD bufSize; //with tailing '\0' + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufFromWchar(valueNameW); + ffStrbufAppendF(error, "RegGetValueA(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars); + } + return false; + } + wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufFromWchar(valueNameW); + ffStrbufAppendF(error, "RegGetValueA(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars); + } + return false; + } + ffWcharToUtf8(resultW, result); + return true; +} + +bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error) +{ + DWORD bufSize = sizeof(*result); + if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS) + { + if(error) + { + if(!valueNameW) + valueNameW = L"(default)"; + FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufFromWchar(valueNameW); + ffStrbufAppendF(error, "RegGetValueA(%s, result, RRF_RT_DWORD) failed", valueNameA.chars); + } + return false; + } + return true; +} diff --git a/src/util/windows/register.h b/src/util/windows/register.h new file mode 100644 index 000000000..22aa6f9ce --- /dev/null +++ b/src/util/windows/register.h @@ -0,0 +1,23 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_REGISTER_H +#define FASTFETCH_INCLUDED_REGISTER_H + +#include "fastfetch.h" + +#define WIN32_LEAN_AND_MEAN +#include + +static inline void wrapRegCloseKey(HKEY* phKey) +{ + if(*phKey) + RegCloseKey(*phKey); +} + +#define FF_HKEY_AUTO_DESTROY HKEY __attribute__((__cleanup__(wrapRegCloseKey))) + +bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error); +bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error); +bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error); + +#endif diff --git a/src/util/windows/unicode.c b/src/util/windows/unicode.c new file mode 100644 index 000000000..f2ffa28cc --- /dev/null +++ b/src/util/windows/unicode.c @@ -0,0 +1,36 @@ +#include "unicode.h" + +void ffWcharToUtf8(const wchar_t* input, FFstrbuf* result) +{ + int len = (int)wcslen(input); + if(len <= 0) + { + ffStrbufClear(result); + return; + } + + int size_needed = WideCharToMultiByte(CP_UTF8, 0, input, len, NULL, 0, NULL, NULL); + ffStrbufEnsureFree(result, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, input, len, result->chars, size_needed, NULL, NULL); + result->length = (uint32_t)size_needed; + result->chars[size_needed] = '\0'; +} + +FFstrbuf ffStrbufFromWchar(const wchar_t* input) +{ + FFstrbuf result; + + int len = input ? (int)wcslen(input) : 0; + if(len <= 0) + ffStrbufInit(&result); + else + { + int size_needed = WideCharToMultiByte(CP_UTF8, 0, input, len, NULL, 0, NULL, NULL); + ffStrbufInitA(&result, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, input, len, result.chars, size_needed, NULL, NULL); + result.length = (uint32_t)size_needed; + result.chars[size_needed] = '\0'; + } + + return result; +} diff --git a/src/util/windows/unicode.h b/src/util/windows/unicode.h new file mode 100644 index 000000000..d3926ff06 --- /dev/null +++ b/src/util/windows/unicode.h @@ -0,0 +1,11 @@ +#pragma once + +#ifndef FASTFETCH_INCLUDED_UNICODE_H +#define FASTFETCH_INCLUDED_UNICODE_H + +#include "fastfetch.h" + +void ffWcharToUtf8(const wchar_t* input, FFstrbuf* result); +FFstrbuf ffStrbufFromWchar(const wchar_t* input); + +#endif