diff --git a/CMakeLists.txt b/CMakeLists.txt index 61bddf0a9..ec7e6c459 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,12 @@ if("${CMAKE_SYSTEM_NAME}" MATCHES ".*[Ll]inux.*") set(LINUX TRUE CACHE BOOL "..." FORCE) # LINUX means GNU/Linux, not just the kernel elseif("${CMAKE_SYSTEM_NAME}" MATCHES ".*[Bb][Ss][Dd].*") set(BSD TRUE CACHE BOOL "..." FORCE) -elseif(NOT APPLE AND NOT ANDROID) - message(FATAL_ERROR "Unsupported platform") +elseif(NOT APPLE AND NOT ANDROID AND NOT MSYS) + message(FATAL_ERROR "Unsupported platform: ${CMAKE_SYSTEM_NAME}") +endif() + +if(MSYS) + enable_language(CXX) endif() ##################### @@ -26,7 +30,7 @@ endif() include(CMakeDependentOption) cmake_dependent_option(ENABLE_LIBPCI "Enable libpci" ON "LINUX OR BSD" OFF) -cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR BSD" OFF) +cmake_dependent_option(ENABLE_VULKAN "Enable vulkan" ON "LINUX OR APPLE OR BSD OR MSYS" OFF) cmake_dependent_option(ENABLE_WAYLAND "Enable wayland-client" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_XCB "Enable xcb" ON "LINUX OR BSD" OFF) @@ -45,8 +49,8 @@ cmake_dependent_option(ENABLE_ZLIB "Enable zlib" ON "ENABLE_IMAGEMAGICK6 OR ENAB cmake_dependent_option(ENABLE_EGL "Enable egl" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_GLX "Enable glx" ON "LINUX OR BSD" OFF) cmake_dependent_option(ENABLE_OSMESA "Enable osmesa" ON "LINUX OR BSD" OFF) -cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD" OFF) -cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX" OFF) +cmake_dependent_option(ENABLE_OPENCL "Enable opencl" ON "LINUX OR BSD OR MSYS" OFF) +cmake_dependent_option(ENABLE_LIBCJSON "Enable libcjson" ON "LINUX OR MSYS" OFF) cmake_dependent_option(ENABLE_FREETYPE "Enable freetype" ON "ANDROID" OFF) option(BUILD_TESTS "Build tests" OFF) # Also create test executables @@ -76,13 +80,21 @@ message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") set(CMAKE_C_STANDARD 11) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wconversion") +if(MSYS) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -fno-exceptions -fno-rtti") +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") endif() -set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined") +set(FASTFETCH_FLAGS_DEBUG "-fno-omit-frame-pointer") +if(NOT MSYS) + set(FASTFETCH_FLAGS_DEBUG "${FASTFETCH_FLAGS_DEBUG} -fsanitize=address -fsanitize=undefined") +endif() set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG}") set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} ${FASTFETCH_FLAGS_DEBUG} -rdynamic") @@ -190,7 +202,6 @@ set(LIBFASTFETCH_SRC src/common/caching.c src/common/properties.c src/common/font.c - src/common/processing.c src/common/format.c src/common/parsing.c src/common/settings.c @@ -201,15 +212,13 @@ set(LIBFASTFETCH_SRC src/logo/image/image.c src/logo/image/im7.c src/logo/image/im6.c - src/detection/qt.c - src/detection/gtk.c - src/detection/terminalShell.c src/detection/vulkan.c src/detection/datetime.c src/detection/title.c src/detection/host/host.c src/detection/os/os.c src/detection/cpu/cpu.c + src/detection/cpuUsage/cpuUsage.c src/detection/gpu/gpu.c src/detection/memory/memory.c src/detection/font/font.c @@ -222,6 +231,8 @@ set(LIBFASTFETCH_SRC src/modules/separator.c src/modules/os.c src/modules/host.c + src/modules/bios.c + src/modules/board.c src/modules/kernel.c src/modules/uptime.c src/modules/processes.c @@ -260,6 +271,18 @@ set(LIBFASTFETCH_SRC src/modules/users.c ) +if(LINUX OR APPLE OR ANDROID OR BSD) + list(APPEND LIBFASTFETCH_SRC + src/detection/users/users_linux.c + src/common/processing_linux.c + src/detection/disk/disk.c + src/detection/terminalshell/terminalshell_linux.c + src/detection/packages/packages_linux.c + src/detection/kernel/kernel_linux.c + src/detection/localip/localip_linux.c + ) +endif() + if(BSD OR APPLE) list(APPEND LIBFASTFETCH_SRC src/common/sysctl.c @@ -277,14 +300,19 @@ if(LINUX OR ANDROID OR BSD) list(APPEND LIBFASTFETCH_SRC src/detection/cpuUsage/cpuUsage_linux.c src/detection/disk/disk_linux.c - src/detection/poweradapter/poweradapter_linux.c src/detection/temps/temps_linux.c + src/detection/opengl/opengl_linux.c + src/detection/processes/processes_linux.c + + src/detection/poweradapter/poweradapter_nosupport.c ) endif() if(LINUX OR BSD) list(APPEND LIBFASTFETCH_SRC src/detection/host/host_linux.c + src/detection/bios/bios_linux.c + src/detection/board/board_linux.c src/detection/os/os_linux.c src/detection/gpu/gpu_linux.c src/detection/battery/battery_linux.c @@ -297,6 +325,42 @@ if(LINUX OR BSD) src/detection/media/media_linux.c src/detection/wmtheme/wmtheme_linux.c src/detection/font/font_linux.c + src/detection/qt.c + src/detection/gtk.c + ) +endif() + +if(MSYS) + list(APPEND LIBFASTFETCH_SRC + src/common/processing_linux.c + src/detection/host/host_windows.cpp + src/detection/bios/bios_windows.cpp + src/detection/board/board_windows.cpp + src/detection/cpu/cpu_windows.cpp + src/detection/gpu/gpu_windows.cpp + src/detection/battery/battery_windows.cpp + src/detection/displayserver/displayserver_windows.c + src/detection/wmtheme/wmtheme_windows.c + src/detection/opengl/opengl_windows.c + src/detection/users/users_windows.cpp + src/detection/os/os_windows.cpp + src/detection/processes/processes_windows.cpp + src/detection/disk/disk_windows.c + src/detection/cpuUsage/cpuUsage_windows.c + src/detection/cpuUsage/cpuUsage_nowait_windows.cpp + src/detection/memory/memory_windows.cpp + src/detection/font/font_windows.cpp + src/detection/terminalshell/terminalshell_linux.c + src/detection/terminalshell/terminalshell_windows.cpp + src/detection/terminalfont/terminalfont_linux.c + src/detection/packages/packages_linux.c + src/detection/packages/packages_windows.c + src/detection/kernel/kernel_windows.cpp + src/detection/localip/localip_windows.c + src/util/windows/wmi.cpp + + src/detection/poweradapter/poweradapter_nosupport.c + src/detection/media/media_nosupport.c ) endif() @@ -319,6 +383,11 @@ if(APPLE) src/detection/wmtheme/wmtheme_apple.m src/detection/temps/temps_apple.c src/detection/font/font_apple.m + src/detection/opengl/opengl_apple.c + src/detection/processes/processes_apple.c + + src/detection/bios/bios_nosupport.c + src/detection/board/board_nosupport.c ) endif() @@ -333,13 +402,16 @@ if(ANDROID) list(APPEND LIBFASTFETCH_SRC src/detection/host/host_android.c src/detection/os/os_android.c - src/detection/gpu/gpu_android.c - src/detection/battery/battery_android.c - src/detection/displayserver/displayserver_android.c src/detection/terminalfont/terminalfont_android.c - src/detection/media/media_android.c - src/detection/wmtheme/wmtheme_android.c - src/detection/font/font_android.c + + src/detection/bios/bios_nosupport.c + src/detection/board/board_nosupport.c + src/detection/displayserver/displayserver_nosupport.c + src/detection/battery/battery_nosupport.c + src/detection/gpu/gpu_nosupport.c + src/detection/font/font_nosupport.c + src/detection/media/media_nosupport.c + src/detection/wmtheme/wmtheme_nosupport.c ) endif() @@ -417,6 +489,16 @@ if(APPLE) PRIVATE "-framework Cocoa" PRIVATE "-weak_framework MediaRemote -F /System/Library/PrivateFrameworks" ) +elseif(MSYS) + target_link_libraries(libfastfetch + PRIVATE "wbemuuid" + PRIVATE "ole32" + PRIVATE "oleaut32" + PRIVATE "dwmapi" + PRIVATE "opengl32" + PRIVATE "gdi32" + PRIVATE "iphlpapi" + ) endif() target_include_directories(libfastfetch diff --git a/README.md b/README.md index 8eb20f122..f100921bb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Fastfetch -Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying them in a pretty way. It is written in pure c, with performance and customizability in mind. Currently Linux, Android, BSD and MacOS are supported. +Fastfetch is a [neofetch](https://github.com/dylanaraps/neofetch)-like tool for fetching system information and displaying them in a pretty way. It is written in pure c, with performance and customizability in mind. Currently Linux, Android, BSD, MacOS and Windows on [MSYS2](https://www.msys2.org/) are supported. @@ -44,7 +44,7 @@ The following libraries are used if present at runtime: * [`libXFConf`](https://gitlab.xfce.org/xfce/xfconf): Needed for XFWM theme and XFCE Terminal font. * [`libsqlite3`](https://www.sqlite.org/index.html): Needed for pkg & rpm package count. * [`librpm`](http://rpm.org/): Slower fallback for rpm package count. Needed on openSUSE. -* [`libcJSON`](https://github.com/DaveGamble/cJSON): Needed for Windows Terminal font ( WSL ). +* [`libcJSON`](https://github.com/DaveGamble/cJSON): Needed for Windows Terminal font ( Windows, WSL ). * [`freetype`](https://www.freetype.org/): Needed for Termux font detection ( Android ). ## Support status @@ -52,12 +52,12 @@ All categories not listed here should work without needing a specific implementa ##### Available Modules ``` -Title, Separator, OS, Host, Kernel, Uptime, Processes, Packages, Shell, Resolution, DE, WM, WMTheme, Theme, Icons, Font, Cursor, Terminal, Terminal Font, CPU, CPUUsage, GPU, Memory, Swap, Disk, Battery, Power Adapter, Player, Media, Vulkan, OpenGL, OpenCL, LocalIP, PublicIP, DateTime, Date, Time, Locale, Colors, Break, Custom +Title, Separator, OS, Host, Bios, Board, Kernel, Uptime, Processes, Packages, Shell, Resolution, DE, WM, WMTheme, Theme, Icons, Font, Cursor, Terminal, Terminal Font, CPU, CPUUsage, GPU, Memory, Swap, Disk, Battery, Power Adapter, Player, Media, Vulkan, OpenGL, OpenCL, LocalIP, PublicIP, DateTime, Date, Time, Locale, Colors, Break, Custom ``` ##### Logos ``` -AlmaLinux, Alpine, Android, Arch, Arco, Artix, Bedrock, CachyOS, CentOS, Crystal, Debian, Devuan, Deepin, Endeavour, Fedora, FreeBSD, Garuda, Gentoo, KDE Neon, KISS, Kubuntu, LangitKetujuh, Linux, MacOS, Manjaro, Mint, MSYS2, NixOS, OpenSUSE, OpenSUSE Tumbleweed, OpenSUSE LEAP, Pop!_OS, RebornOS, RedstarOS, Rocky, Rosa, Slackware, Ubuntu, Void, Zorin +AlmaLinux, Alpine, Android, Arch, Arco, Artix, Bedrock, CachyOS, CentOS, Crystal, Debian, Devuan, Deepin, Endeavour, Fedora, FreeBSD, Garuda, Gentoo, KDE Neon, KISS, Kubuntu, LangitKetujuh, Linux, MacOS, Manjaro, Mint, MSYS2, NixOS, OpenSUSE, OpenSUSE Tumbleweed, OpenSUSE LEAP, Pop!_OS, RebornOS, RedstarOS, Rocky, Rosa, Slackware, Ubuntu, Void, Windows 11, Windows 8, Windows, Zorin ``` * Most of the logos have a small variant. Access it by appending _small to the logo name. * Some logos have an old variant. Access it by appending _old to the logo name. @@ -67,12 +67,12 @@ AlmaLinux, Alpine, Android, Arch, Arco, Artix, Bedrock, CachyOS, CentOS, Crystal ##### Package managers ``` -Pacman, dpkg, rpm, emerge, xbps, nix, Flatpak, Snap, apk, pkg, brew, MacPorts +Pacman, dpkg, rpm, emerge, xbps, nix, Flatpak, Snap, apk, pkg, brew, MacPorts, scoop ``` ##### WM themes ``` -KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE), Quartz Compositor (macOS) +KWin, Mutter, Muffin, Marco, XFWM, Openbox (LXDE, LXQT & without DE), Quartz Compositor (macOS), DWM (Windows) ``` ##### DE versions @@ -82,7 +82,7 @@ KDE Plasma, Gnome, Cinnamon, Mate, XFCE4, LXQt ##### Terminal fonts ``` -Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, LXTerminal, iTerm2, Apple Terminal, TTY, Windows Terminal, Termux +Konsole, Gnome Terminal, Tilix, XFCE4 Terminal, Alacritty, LXTerminal, iTerm2, Apple Terminal, TTY, Windows Terminal, Termux, mintty ``` ## Building @@ -97,6 +97,12 @@ cmake --build . --target fastfetch --target flashfetch If pkg-config fails to find the headers for a library listed in [dependencies](#dependencies), fastfetch will simply build without support for that specific feature. This means, it won't look for it at runtime and just act like it isn't available. +### Building on Windows + +Currently [MSYS2](https://www.msys2.org/) is required to build fastfetch. Running fastfetch requires msys2 runtime library (`msys-2.0.dll`) but not full MSYS2 environment. + +Full native Windows executable is planned. + ## Packaging ### Repositories diff --git a/src/common/caching.c b/src/common/caching.c index 12a34c82b..14551b120 100644 --- a/src/common/caching.c +++ b/src/common/caching.c @@ -99,7 +99,10 @@ static bool printCachedValue(FFinstance* instance, const char* moduleName, const ffStrbufTrimRight(&content, '\0'); //Strbuf always appends a '\0' at the end. We want the last null byte to be at the position of the length if(content.length == 0) + { + ffStrbufDestroy(&content); return false; + } uint8_t moduleCounter = 1; diff --git a/src/common/init.c b/src/common/init.c index a9eb9b25a..be5dd73c1 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -162,6 +162,8 @@ static void defaultConfig(FFinstance* instance) initModuleArg(&instance->config.os); initModuleArg(&instance->config.host); + initModuleArg(&instance->config.bios); + initModuleArg(&instance->config.board); initModuleArg(&instance->config.kernel); initModuleArg(&instance->config.uptime); initModuleArg(&instance->config.processes); @@ -257,7 +259,7 @@ void ffInitInstance(FFinstance* instance) defaultConfig(instance); } -#if !defined(__ANDROID__) +#if !defined(__ANDROID__) && !defined(_WIN32) && !defined(__MSYS__) static void* connectDisplayServerThreadMain(void* instance) { @@ -416,6 +418,8 @@ static void destroyConfig(FFinstance* instance) destroyModuleArg(&instance->config.os); destroyModuleArg(&instance->config.host); + destroyModuleArg(&instance->config.bios); + destroyModuleArg(&instance->config.board); destroyModuleArg(&instance->config.kernel); destroyModuleArg(&instance->config.uptime); destroyModuleArg(&instance->config.processes); diff --git a/src/common/io.h b/src/common/io.h index 35a70675f..a6ecbc5ac 100644 --- a/src/common/io.h +++ b/src/common/io.h @@ -3,6 +3,8 @@ #ifndef FF_INCLUDED_common_io #define FF_INCLUDED_common_io +#include "fastfetch.h" + #include //mode_t bool ffWriteFDBuffer(int fd, const FFstrbuf* content); diff --git a/src/common/library.c b/src/common/library.c index 441b9f611..c131a3862 100644 --- a/src/common/library.c +++ b/src/common/library.c @@ -19,6 +19,14 @@ static void* libraryLoad(const char* path, int maxVersion) { void* result = dlopen(path, FF_DLOPEN_FLAGS); + + #if defined(_WIN32) || defined(__MSYS__) + + // libX.dll.1 never exists on Windows, while libX-1.dll may exist + FF_UNUSED(maxVersion) + + #else + if(result != NULL || maxVersion < 0) return result; @@ -40,6 +48,9 @@ static void* libraryLoad(const char* path, int maxVersion) } ffStrbufDestroy(&pathbuf); + + #endif + return result; } diff --git a/src/common/library.h b/src/common/library.h index 25a323119..885cd32dc 100644 --- a/src/common/library.h +++ b/src/common/library.h @@ -5,7 +5,9 @@ #include -#ifdef __APPLE__ +#if defined(_WIN32) || defined(__MSYS__) + #define FF_LIBRARY_EXTENSION ".dll" +#elif defined(__APPLE__) #define FF_LIBRARY_EXTENSION ".dylib" #else #define FF_LIBRARY_EXTENSION ".so" diff --git a/src/common/processing.c b/src/common/processing_linux.c similarity index 100% rename from src/common/processing.c rename to src/common/processing_linux.c diff --git a/src/common/processing_windows.c b/src/common/processing_windows.c new file mode 100644 index 000000000..f3bb64a2d --- /dev/null +++ b/src/common/processing_windows.c @@ -0,0 +1,69 @@ +#include "fastfetch.h" +#include "common/processing.h" + +#define WIN32_LEAN_AND_MEAN 1 +#include + +//We can't use this native version yet because we still use POSIX path ( eg /usr/bin/fish ) at a lot of places. +const char* ffProcessAppendStdOut(FFstrbuf* buffer, char* const argv[]) +{ + SECURITY_ATTRIBUTES saAttr = { + .nLength = sizeof(SECURITY_ATTRIBUTES), + .lpSecurityDescriptor = NULL, + .bInheritHandle = TRUE, + }; + + HANDLE hChildStdoutRead, hChildStdoutWrite; + if (!CreatePipe(&hChildStdoutRead, &hChildStdoutWrite, &saAttr, 0)) + return "CreatePipe() failed"; + + if (!SetHandleInformation(hChildStdoutRead, HANDLE_FLAG_INHERIT, 0)) + return "SetHandleInformation(hChildStdoutRead) failed"; + + PROCESS_INFORMATION piProcInfo = {0}; + STARTUPINFOA siStartInfo = { + .cb = sizeof(siStartInfo), + .dwFlags = STARTF_USESTDHANDLES, + .hStdOutput = hChildStdoutWrite, + }; + + FFstrbuf cmdline; + ffStrbufInit(&cmdline); + for(char* const* parg = argv; *parg; ++parg) + { + if(cmdline.length > 0) + ffStrbufAppendC(&cmdline, ' '); + ffStrbufAppendF(&cmdline, "\"%s\"", * parg); + } + + BOOL success = CreateProcessA( + NULL, // application name + cmdline.chars, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + CloseHandle(hChildStdoutWrite); + if(!success) + { + CloseHandle(hChildStdoutRead); + return "CreateProcessA() failed"; + } + + char str[128]; + DWORD nRead; + while(ReadFile(hChildStdoutRead, str, sizeof(str), &nRead, NULL) && nRead > 0) + { + ffStrbufAppendNS(buffer, nRead, str); + if(nRead < sizeof(str)) + break; + } + + CloseHandle(hChildStdoutRead); + return NULL; +} diff --git a/src/common/time.h b/src/common/time.h new file mode 100644 index 000000000..7569d40e2 --- /dev/null +++ b/src/common/time.h @@ -0,0 +1,35 @@ +#pragma once + +#ifndef FF_INCLUDED_common_time +#define FF_INCLUDED_common_time + +#include +#if defined(_WIN32) || defined(__MSYS__) + #include + #include +#else + #include + #include +#endif + +static inline uint64_t ffTimeGetTick() //In msec +{ + #if defined(_WIN32) || defined(__MSYS__) + return GetTickCount64(); + #else + struct timeval timeNow; + gettimeofday(&timeNow, NULL); + return (uint64_t)((timeNow.tv_sec * 1000) + (timeNow.tv_usec / 1000)); + #endif +} + +static inline void ffTimeSleep(uint32_t msec) +{ + #if defined(_WIN32) || defined(__MSYS__) + SleepEx(msec, TRUE); + #else + nanosleep(&(struct timespec){ msec / 1000, (msec % 1000) * 1000000 }, NULL); + #endif +} + +#endif diff --git a/src/data/config_user.txt b/src/data/config_user.txt index 26b90679f..1e68fe85b 100644 --- a/src/data/config_user.txt +++ b/src/data/config_user.txt @@ -5,7 +5,7 @@ # Whitespaces are trimmed at the beginning and the end. # Empty lines or lines starting with # are ignored. -# This file was shipped with $"FASTFETCH_PROJECT_VERSION$". +# This file was shipped with $" FASTFETCH_PROJECT_VERSION $". # Use fastfetch --print-config-user > ~/.config/fastfetch/config.conf to overwrite this file with the current defaults # Below some often usefull options are listed. Uncomment and modify them so they take affect. @@ -25,7 +25,7 @@ # Must be a list of module names, separated by colons. # List available modules with "fastfetch --list-modules". # Get the default structure with "fastfetch --print-structure". -#--structure $"FASTFETCH_DATATEXT_STRUCTURE$" +#--structure $" FASTFETCH_DATATEXT_STRUCTURE $" # Multithreading option: # Sets if fastfetch should use multiple threads to detect the values. diff --git a/src/data/help.txt b/src/data/help.txt index f24242ec4..181f24671 100644 --- a/src/data/help.txt +++ b/src/data/help.txt @@ -90,7 +90,7 @@ Module specific options: --title-fqdn : Sets if the title should use fully qualified domain name. Default is false. --separator-string : Set the string printed by the separator module --os-file : Set the path to the file containing OS informations - --disk-folders : A colon separated list of folder paths for the disk output. Default is "/:/home" + --disk-folders : A colon (semicolon on Windows) separated list of folder paths for the disk output. Default is "/:/home" ("C:\\;D:\\ ..." on Windows) --disk-removable : Sets if removable volume should be printed. Default is false --battery-dir : The directory where the battery folders are. Standard: /sys/class/power_supply/ --cpu-temp : Detect and display CPU temperature if supported. Default is false diff --git a/src/detection/battery/battery_android.c b/src/detection/battery/battery_nosupport.c similarity index 64% rename from src/detection/battery/battery_android.c rename to src/detection/battery/battery_nosupport.c index 2fc2b1d85..40ca05241 100644 --- a/src/detection/battery/battery_android.c +++ b/src/detection/battery/battery_nosupport.c @@ -1,10 +1,8 @@ #include "fastfetch.h" -#include "common/io.h" #include "battery.h" -#include - -const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) { +const char* ffDetectBatteryImpl(FFinstance* instance, FFlist* results) +{ FF_UNUSED(instance, results) - return "Unimplemented"; + return "Not supported on this platform"; } diff --git a/src/detection/battery/battery_windows.cpp b/src/detection/battery/battery_windows.cpp new file mode 100644 index 000000000..48084720a --- /dev/null +++ b/src/detection/battery/battery_windows.cpp @@ -0,0 +1,70 @@ +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 + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT SystemName, Name, Chemistry, EstimatedChargeRemaining, BatteryStatus FROM Win32_Battery", nullptr); + + if(!pEnumerator) + return "Query WMI service failed"; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + while(SUCCEEDED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) && uReturn != 0) + { + BatteryResult* battery = (BatteryResult*)ffListAdd(results); + + ffStrbufInit(&battery->manufacturer); + ffGetWmiObjString(pclsObj, L"SystemName", &battery->manufacturer); + + ffStrbufInit(&battery->modelName); + ffGetWmiObjString(pclsObj, L"Name", &battery->modelName); + + uint64_t chemistry; + ffGetWmiObjUnsigned(pclsObj, 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; + } + + uint64_t capacity; + ffGetWmiObjUnsigned(pclsObj, L"EstimatedChargeRemaining", &capacity); + ffStrbufInitF(&battery->capacity, "%d", (int)capacity); + + uint64_t batteryStatus; + ffGetWmiObjUnsigned(pclsObj, 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; + } + + battery->temperature = FF_BATTERY_TEMP_UNSET; + } + + pclsObj->Release(); + pEnumerator->Release(); + return nullptr; +} diff --git a/src/detection/bios/bios.h b/src/detection/bios/bios.h new file mode 100644 index 000000000..6f664b4b2 --- /dev/null +++ b/src/detection/bios/bios.h @@ -0,0 +1,19 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_bios_bios +#define FF_INCLUDED_detection_bios_bios + +#include "fastfetch.h" + +typedef struct FFBiosResult +{ + FFstrbuf biosDate; + FFstrbuf biosRelease; + FFstrbuf biosVendor; + FFstrbuf biosVersion; + FFstrbuf error; +} FFBiosResult; + +void ffDetectBios(FFBiosResult* bios); + +#endif diff --git a/src/detection/bios/bios_linux.c b/src/detection/bios/bios_linux.c new file mode 100644 index 000000000..10bbab589 --- /dev/null +++ b/src/detection/bios/bios_linux.c @@ -0,0 +1,58 @@ +#include "bios.h" +#include "common/io.h" + +#include + +static bool hostValueSet(FFstrbuf* value) +{ + return + value->length > 0 && + ffStrbufStartsWithIgnCaseS(value, "To be filled") != true && + ffStrbufStartsWithIgnCaseS(value, "To be set") != true && + ffStrbufStartsWithIgnCaseS(value, "OEM") != true && + ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true && + ffStrbufIgnCaseCompS(value, "None") != 0 && + ffStrbufIgnCaseCompS(value, "System Product") != 0 && + ffStrbufIgnCaseCompS(value, "System Product Name") != 0 && + ffStrbufIgnCaseCompS(value, "System Product Version") != 0 && + ffStrbufIgnCaseCompS(value, "System Name") != 0 && + ffStrbufIgnCaseCompS(value, "System Version") != 0 && + ffStrbufIgnCaseCompS(value, "Default string") != 0 && + ffStrbufIgnCaseCompS(value, "Undefined") != 0 && + ffStrbufIgnCaseCompS(value, "Not Specified") != 0 && + ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 && + ffStrbufIgnCaseCompS(value, "INVALID") != 0 && + ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 && + ffStrbufIgnCaseCompS(value, "All Series") != 0 + ; +} + +static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) +{ + ffReadFileBuffer(devicesPath, buffer); + if(hostValueSet(buffer)) + return; + + ffReadFileBuffer(classPath, buffer); + if(hostValueSet(buffer)) + return; + + ffStrbufClear(buffer); +} + +void ffDetectBios(FFBiosResult* bios) +{ + ffStrbufInit(&bios->error); + + ffStrbufInit(&bios->biosDate); + getHostValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &bios->biosDate); + + ffStrbufInit(&bios->biosRelease); + getHostValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &bios->biosRelease); + + ffStrbufInit(&bios->biosVendor); + getHostValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &bios->biosVendor); + + ffStrbufInit(&bios->biosVersion); + getHostValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &bios->biosVersion); +} diff --git a/src/detection/bios/bios_nosupport.c b/src/detection/bios/bios_nosupport.c new file mode 100644 index 000000000..f8d886182 --- /dev/null +++ b/src/detection/bios/bios_nosupport.c @@ -0,0 +1,11 @@ +#include "bios.h" + +void ffDetectBios(FFBiosResult* bios) +{ + ffStrbufInitS(&bios->error, "Not supported on this platform"); + + ffStrbufInit(&bios->biosDate); + ffStrbufInit(&bios->biosRelease); + ffStrbufInit(&bios->biosVendor); + ffStrbufInit(&bios->biosVersion); +} diff --git a/src/detection/bios/bios_windows.cpp b/src/detection/bios/bios_windows.cpp new file mode 100644 index 000000000..d04726ebb --- /dev/null +++ b/src/detection/bios/bios_windows.cpp @@ -0,0 +1,38 @@ +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); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Name, ReleaseDate, Version, Manufacturer FROM Win32_BIOS", &bios->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if(uReturn == 0) + { + ffStrbufInitS(&bios->error, "No Wmi result returned"); + pEnumerator->Release(); + return; + } + + ffGetWmiObjString(pclsObj, L"Name", &bios->biosRelease); + ffGetWmiObjString(pclsObj, L"ReleaseDate", &bios->biosDate); + ffGetWmiObjString(pclsObj, L"Version", &bios->biosVersion); + ffGetWmiObjString(pclsObj, L"Manufacturer", &bios->biosVendor); + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/board/board.h b/src/detection/board/board.h new file mode 100644 index 000000000..15044469e --- /dev/null +++ b/src/detection/board/board.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_board_board +#define FF_INCLUDED_detection_board_board + +#include "fastfetch.h" + +typedef struct FFBoardResult +{ + FFstrbuf boardName; + FFstrbuf boardVendor; + FFstrbuf boardVersion; + FFstrbuf error; +} FFBoardResult; + +void ffDetectBoard(FFBoardResult* result); + +#endif diff --git a/src/detection/board/board_linux.c b/src/detection/board/board_linux.c new file mode 100644 index 000000000..8e92eec4e --- /dev/null +++ b/src/detection/board/board_linux.c @@ -0,0 +1,55 @@ +#include "board.h" +#include "common/io.h" + +#include + +static bool hostValueSet(FFstrbuf* value) +{ + return + value->length > 0 && + ffStrbufStartsWithIgnCaseS(value, "To be filled") != true && + ffStrbufStartsWithIgnCaseS(value, "To be set") != true && + ffStrbufStartsWithIgnCaseS(value, "OEM") != true && + ffStrbufStartsWithIgnCaseS(value, "O.E.M.") != true && + ffStrbufIgnCaseCompS(value, "None") != 0 && + ffStrbufIgnCaseCompS(value, "System Product") != 0 && + ffStrbufIgnCaseCompS(value, "System Product Name") != 0 && + ffStrbufIgnCaseCompS(value, "System Product Version") != 0 && + ffStrbufIgnCaseCompS(value, "System Name") != 0 && + ffStrbufIgnCaseCompS(value, "System Version") != 0 && + ffStrbufIgnCaseCompS(value, "Default string") != 0 && + ffStrbufIgnCaseCompS(value, "Undefined") != 0 && + ffStrbufIgnCaseCompS(value, "Not Specified") != 0 && + ffStrbufIgnCaseCompS(value, "Not Applicable") != 0 && + ffStrbufIgnCaseCompS(value, "INVALID") != 0 && + ffStrbufIgnCaseCompS(value, "Type1ProductConfigId") != 0 && + ffStrbufIgnCaseCompS(value, "All Series") != 0 + ; +} + +static void getHostValue(const char* devicesPath, const char* classPath, FFstrbuf* buffer) +{ + ffReadFileBuffer(devicesPath, buffer); + if(hostValueSet(buffer)) + return; + + ffReadFileBuffer(classPath, buffer); + if(hostValueSet(buffer)) + return; + + ffStrbufClear(buffer); +} + +void ffDetectBoard(FFBoardResult* board) +{ + ffStrbufInit(&board->error); + + ffStrbufInit(&board->boardName); + getHostValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &board->boardName); + + ffStrbufInit(&board->boardVendor); + getHostValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &board->boardVendor); + + ffStrbufInit(&board->boardVersion); + getHostValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &board->boardVersion); +} diff --git a/src/detection/board/board_nosupport.c b/src/detection/board/board_nosupport.c new file mode 100644 index 000000000..0b182ba3e --- /dev/null +++ b/src/detection/board/board_nosupport.c @@ -0,0 +1,10 @@ +#include "board.h" + +void ffDetectBoard(FFBoardResult* board) +{ + ffStrbufInitS(&board->error, "Not supported on this platform"); + + ffStrbufInit(&board->boardName); + ffStrbufInit(&board->boardVendor); + ffStrbufInit(&board->boardVersion); +} diff --git a/src/detection/board/board_windows.cpp b/src/detection/board/board_windows.cpp new file mode 100644 index 000000000..895d6f8ed --- /dev/null +++ b/src/detection/board/board_windows.cpp @@ -0,0 +1,36 @@ +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); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Product, Version, Manufacturer FROM Win32_BaseBoard", &board->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if(uReturn == 0) + { + ffStrbufInitS(&board->error, "No Wmi result returned"); + pEnumerator->Release(); + return; + } + + ffGetWmiObjString(pclsObj, L"Product", &board->boardName); + ffGetWmiObjString(pclsObj, L"Manufacturer", &board->boardVendor); + ffGetWmiObjString(pclsObj, L"Version", &board->boardVersion); + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/cpu/cpu_windows.cpp b/src/detection/cpu/cpu_windows.cpp new file mode 100644 index 000000000..250212cfc --- /dev/null +++ b/src/detection/cpu/cpu_windows.cpp @@ -0,0 +1,51 @@ +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); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Name, Manufacturer, NumberOfCores, NumberOfLogicalProcessors, ThreadCount, CurrentClockSpeed, MaxClockSpeed FROM Win32_Processor WHERE ProcessorType = 3", nullptr); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + pEnumerator->Release(); + return; + } + + ffGetWmiObjString(pclsObj, L"Name", &cpu->name); + ffGetWmiObjString(pclsObj, L"Manufacturer", &cpu->vendor); + + uint64_t value; + + ffGetWmiObjUnsigned(pclsObj, L"NumberOfCores", &value); + cpu->coresPhysical = (uint16_t)value; + ffGetWmiObjUnsigned(pclsObj, L"NumberOfLogicalProcessors", &value); + cpu->coresLogical = (uint16_t)value; + ffGetWmiObjUnsigned(pclsObj, L"ThreadCount", &value); + cpu->coresOnline = (uint16_t)value; + ffGetWmiObjUnsigned(pclsObj, L"CurrentClockSpeed", &value); //There's no MinClockSpeed in Win32_Processor + cpu->frequencyMin = (double)value / 1000.0; + ffGetWmiObjUnsigned(pclsObj, L"MaxClockSpeed", &value); + cpu->frequencyMax = (double)value / 1000.0; + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/cpuUsage/cpuUsage.c b/src/detection/cpuUsage/cpuUsage.c new file mode 100644 index 000000000..7470236dc --- /dev/null +++ b/src/detection/cpuUsage/cpuUsage.c @@ -0,0 +1,58 @@ +#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 + +// We need to use uint64_t because sizeof(long) == 4 on Windows +const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll); + +static uint64_t inUseAll1, totalAll1, startTime; + +void ffPrepareCPUUsage() +{ + startTime = ffTimeGetTick(); + ffGetCpuUsageInfo(&inUseAll1, &totalAll1); +} + +const char* ffGetCpuUsageResult(double* result) +{ + const char* error = NULL; + if(startTime == 0) + { + error = ffGetCpuUsageInfo(&inUseAll1, &totalAll1); + if(error) + return error; + ffTimeSleep(1000); + } + else + { + uint64_t duration = ffTimeGetTick() - startTime; + if(duration < 1000) + ffTimeSleep(1000 - (uint32_t) duration); + } + + uint64_t inUseAll2, totalAll2; + error = ffGetCpuUsageInfo(&inUseAll2, &totalAll2); + if(error) + return error; + + *result = (double)(inUseAll2 - inUseAll1) / (double)(totalAll2 - totalAll1) * 100; + + return NULL; +} + +#endif //FF_DETECTION_CPUUSAGE_NOWAIT diff --git a/src/detection/cpuUsage/cpuUsage.h b/src/detection/cpuUsage/cpuUsage.h index cddd1866b..4ac03c222 100644 --- a/src/detection/cpuUsage/cpuUsage.h +++ b/src/detection/cpuUsage/cpuUsage.h @@ -3,6 +3,11 @@ #ifndef FF_INCLUDED_detection_cpu_cpuUsage #define FF_INCLUDED_detection_cpu_cpuUsage -const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll); +#if defined(_WIN32) || defined(__MSYS__) + // 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_apple.c b/src/detection/cpuUsage/cpuUsage_apple.c index e170a7ceb..38a6b4865 100644 --- a/src/detection/cpuUsage/cpuUsage_apple.c +++ b/src/detection/cpuUsage/cpuUsage_apple.c @@ -4,7 +4,7 @@ #include #include -const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll) +const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll) { natural_t numCPUs = 0U; processor_info_array_t cpuInfo; @@ -21,8 +21,8 @@ const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll) + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_SYSTEM] + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_NICE]; integer_t total = inUse + cpuInfo[CPU_STATE_MAX * i + CPU_STATE_IDLE]; - *inUseAll += inUse; - *totalAll += total; + *inUseAll += (uint64_t)inUse; + *totalAll += (uint64_t)total; } return NULL; } diff --git a/src/detection/cpuUsage/cpuUsage_linux.c b/src/detection/cpuUsage/cpuUsage_linux.c index 3696a8d77..8667fd526 100644 --- a/src/detection/cpuUsage/cpuUsage_linux.c +++ b/src/detection/cpuUsage/cpuUsage_linux.c @@ -2,16 +2,17 @@ #include "cpuUsage.h" #include +#include -const char* ffGetCpuUsageInfo(long* inUseAll, long* totalAll) +const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll) { - long user, nice, system, idle, iowait, irq, softirq; + uint64_t user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0; FILE* procStat = fopen("/proc/stat", "r"); if(procStat == NULL) return "fopen(\"""/proc/stat\", \"r\") == NULL"; - if (fscanf(procStat, "cpu%ld%ld%ld%ld%ld%ld%ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0) + if (fscanf(procStat, "cpu%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64 "%" PRIu64, &user, &nice, &system, &idle, &iowait, &irq, &softirq) < 0) { fclose(procStat); return "fscanf() failed"; diff --git a/src/detection/cpuUsage/cpuUsage_nowait_windows.cpp b/src/detection/cpuUsage/cpuUsage_nowait_windows.cpp new file mode 100644 index 000000000..19af6ffbe --- /dev/null +++ b/src/detection/cpuUsage/cpuUsage_nowait_windows.cpp @@ -0,0 +1,26 @@ +extern "C" { +#include "cpuUsage.h" +} +#include "util/windows/wmi.hpp" + +extern "C" const char* ffGetCpuUsageResultNoWait(double* result) +{ + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT LoadPercentage FROM Win32_Processor", nullptr); + if(!pEnumerator) + return "Query WMI service failed"; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + pEnumerator->Release(); + return "No WMI result returned"; + } + + ffGetWmiObjReal(pclsObj, L"LoadPercentage", result); + + pclsObj->Release(); + pEnumerator->Release(); + return NULL; +} diff --git a/src/detection/cpuUsage/cpuUsage_windows.c b/src/detection/cpuUsage/cpuUsage_windows.c new file mode 100644 index 000000000..1aa9ba3f6 --- /dev/null +++ b/src/detection/cpuUsage/cpuUsage_windows.c @@ -0,0 +1,20 @@ +#include "fastfetch.h" +#include "cpuUsage.h" + +#define WIN32_LEAN_AND_MEAN 1 +#include + +static inline uint64_t fileTimeToUint64(const FILETIME* ft) { + return (((uint64_t)ft->dwHighDateTime) << 32) | ((uint64_t)ft->dwLowDateTime); +} + +const char* ffGetCpuUsageInfo(uint64_t* inUseAll, uint64_t* totalAll) +{ + FILETIME idleTime, kernelTime, userTime; + if(GetSystemTimes(&idleTime, &kernelTime, &userTime) == 0) + return "GetSystemTimes() failed"; + + *inUseAll = fileTimeToUint64(&userTime) + fileTimeToUint64(&kernelTime); + *totalAll = *inUseAll + fileTimeToUint64(&idleTime); + return NULL; +} diff --git a/src/detection/disk/disk.c b/src/detection/disk/disk.c new file mode 100644 index 000000000..7d469c2ba --- /dev/null +++ b/src/detection/disk/disk.c @@ -0,0 +1,56 @@ +#include "fastfetch.h" +#include "disk.h" + +#include + +void ffDetectDiskWithStatvfs(const char* folderPath, struct statvfs* fs, FFDiskResult* result) +{ + ffStrbufInitS(&result->path, folderPath); + result->used = result->total = 0; + ffStrbufInit(&result->error); + + struct statvfs newFs; + + if(fs == NULL) + { + fs = &newFs; + int ret = statvfs(folderPath, fs); + if(ret != 0) + { + ffStrbufAppendF(&result->error, "statvfs(\"%s\", &fs) != 0 (%i)", folderPath, ret); + return; + } + } + + result->total = fs->f_blocks * fs->f_frsize; + + if(result->total == 0) + { + ffStrbufAppendF(&result->error, "statvfs for %s returned size 0", folderPath); + return; + } + + result->used = result->total - (fs->f_bavail * fs->f_frsize); + result->files = (uint32_t) (fs->f_files - fs->f_ffree); + result->removable = false; //To be set at other place +} + +bool ffDiskDetectDiskFolders(FFinstance* instance, FFlist* folders) +{ + ffStrbufTrim(&instance->config.diskFolders, ':'); + if(instance->config.diskFolders.length == 0) + return false; + + uint32_t startIndex = 0; + while(startIndex < instance->config.diskFolders.length) + { + uint32_t colonIndex = ffStrbufNextIndexC(&instance->config.diskFolders, startIndex, ':'); + instance->config.diskFolders.chars[colonIndex] = '\0'; + + ffDetectDiskWithStatvfs(instance->config.diskFolders.chars + startIndex, NULL, (FFDiskResult*)ffListAdd(folders)); + + startIndex = colonIndex + 1; + } + + return true; +} diff --git a/src/detection/disk/disk.h b/src/detection/disk/disk.h index aaf34a608..a68347fde 100644 --- a/src/detection/disk/disk.h +++ b/src/detection/disk/disk.h @@ -5,6 +5,17 @@ #include "fastfetch.h" +typedef struct FFDiskResult +{ + FFstrbuf path; + uint64_t used; + uint64_t total; + uint32_t files; + bool removable; + FFstrbuf error; +} FFDiskResult; + const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders); +bool ffDiskDetectDiskFolders(FFinstance* instance, FFlist* folders); #endif diff --git a/src/detection/disk/disk_apple.m b/src/detection/disk/disk_apple.m index 95d5fd8ec..b79d03e86 100644 --- a/src/detection/disk/disk_apple.m +++ b/src/detection/disk/disk_apple.m @@ -2,6 +2,8 @@ #import +void ffDetectDiskWithStatvfs(const char* folderPath, struct statvfs* fs, FFDiskResult* result); + const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders) { NSArray *keys = [NSArray arrayWithObjects:NSURLVolumeNameKey, nil]; @@ -18,7 +20,9 @@ const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders) if(removable.boolValue && !instance->config.diskRemovable) continue; - ffStrbufInitS((FFstrbuf *)ffListAdd(folders), [url.relativePath cStringUsingEncoding:NSUTF8StringEncoding]); + FFDiskResult* folder = (FFDiskResult*)ffListAdd(folders); + ffDetectDiskWithStatvfs([url.relativePath cStringUsingEncoding:NSUTF8StringEncoding], NULL, folder); + folder->removable = removable.boolValue; } return NULL; diff --git a/src/detection/disk/disk_linux.c b/src/detection/disk/disk_linux.c index 80d5808c4..eb8ceb0f3 100644 --- a/src/detection/disk/disk_linux.c +++ b/src/detection/disk/disk_linux.c @@ -2,6 +2,8 @@ #include +void ffDetectDiskWithStatvfs(const char* folderPath, struct statvfs* fs, FFDiskResult* result); + const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders) { FF_UNUSED(instance); @@ -11,12 +13,12 @@ const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders) if(rootRet != 0) return "statvfs(\"/\") failed"; - ffStrbufInitS((FFstrbuf *)ffListAdd(folders), FASTFETCH_TARGET_DIR_ROOT"/"); + ffDetectDiskWithStatvfs(FASTFETCH_TARGET_DIR_ROOT"/", &fsRoot, (FFDiskResult*)ffListAdd(folders)); struct statvfs fsHome; int homeRet = statvfs(FASTFETCH_TARGET_DIR_HOME, &fsHome); if(homeRet == 0 && (fsRoot.f_fsid != fsHome.f_fsid)) - ffStrbufInitS((FFstrbuf *)ffListAdd(folders), FASTFETCH_TARGET_DIR_HOME); + ffDetectDiskWithStatvfs(FASTFETCH_TARGET_DIR_HOME, &fsHome, (FFDiskResult*)ffListAdd(folders)); return NULL; } diff --git a/src/detection/disk/disk_windows.c b/src/detection/disk/disk_windows.c new file mode 100644 index 000000000..27117a017 --- /dev/null +++ b/src/detection/disk/disk_windows.c @@ -0,0 +1,66 @@ +#include "disk.h" + +#define WIN32_LEAN_AND_MEAN 1 +#include + +static void detectDrive(const char* folderPath, uint32_t pathLen, FFDiskResult* result) +{ + ffStrbufInitNS(&result->path, pathLen, folderPath); + result->removable = false; //To be set at other place + result->files = 0; //Unsupported + + //According to https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdiskfreespaceexa + //GetDiskFreeSpaceExA does not have to specify the root directory on a disk. The function accepts any directory on a disk. + uint64_t freeBytes; + if(GetDiskFreeSpaceExA(folderPath, NULL, (PULARGE_INTEGER)&result->total, (PULARGE_INTEGER)&freeBytes)) { + result->used = result->total - freeBytes; + ffStrbufInit(&result->error); + } + else + ffStrbufInitS(&result->error, "GetDiskFreeSpaceExA() failed"); +} + +const char* ffDiskAutodetectFolders(FFinstance* instance, FFlist* folders) +{ + FF_UNUSED(instance); + + char buf[128]; // "C:\\\0D:\\\0" + uint32_t length = GetLogicalDriveStringsA(sizeof(buf) / sizeof(*buf), buf); + + for(size_t i = 0; i < length;) + { + const char* drive = buf + i; + uint32_t driveLen = (uint32_t) strlen(drive); + i += driveLen + 1; + + bool removable = GetDriveTypeA(drive) == DRIVE_REMOVABLE; + if(removable && !instance->config.diskRemovable) + continue; + + FFDiskResult* folder = (FFDiskResult*)ffListAdd(folders); + detectDrive(drive, driveLen, folder); + folder->removable = removable; + } + + return NULL; +} + +bool ffDiskDetectDiskFolders(FFinstance* instance, FFlist* folders) +{ + ffStrbufTrim(&instance->config.diskFolders, ';'); + if(instance->config.diskFolders.length == 0) + return false; + + uint32_t startIndex = 0; + while(startIndex < instance->config.diskFolders.length) + { + uint32_t colonIndex = ffStrbufNextIndexC(&instance->config.diskFolders, startIndex, ';'); + instance->config.diskFolders.chars[colonIndex] = '\0'; + + detectDrive(instance->config.diskFolders.chars + startIndex, colonIndex - startIndex, (FFDiskResult*)ffListAdd(folders)); + + startIndex = colonIndex + 1; + } + + return true; +} diff --git a/src/detection/displayserver/displayserver_android.c b/src/detection/displayserver/displayserver_nosupport.c similarity index 100% rename from src/detection/displayserver/displayserver_android.c rename to src/detection/displayserver/displayserver_nosupport.c diff --git a/src/detection/displayserver/displayserver_windows.c b/src/detection/displayserver/displayserver_windows.c new file mode 100644 index 000000000..354961094 --- /dev/null +++ b/src/detection/displayserver/displayserver_windows.c @@ -0,0 +1,48 @@ +#include "displayserver.h" +#include "detection/os/os.h" + +#include +#include + +void ffConnectDisplayServerImpl(FFDisplayServerResult* ds, const FFinstance* instance) +{ + FF_UNUSED(instance); + + BOOL enabled; + if(SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled == TRUE) + { + ffStrbufInitS(&ds->wmProcessName, "dwm.exe"); + ffStrbufInitS(&ds->wmPrettyName, "Desktop Window Manager"); + } + else + { + ffStrbufInitS(&ds->wmProcessName, "internal"); + ffStrbufInitS(&ds->wmPrettyName, "internal"); + } + ffStrbufInit(&ds->wmProtocolName); + ffStrbufInit(&ds->deProcessName); + ffStrbufInit(&ds->dePrettyName); + ffStrbufInit(&ds->deVersion); + ffListInit(&ds->resolutions, sizeof(FFResolutionResult)); + + DISPLAY_DEVICEW displayDevice = { .cb = sizeof(DISPLAY_DEVICEW) }; + for(DWORD devNum = 0; EnumDisplayDevicesW(NULL, devNum, &displayDevice, 0) != 0; ++devNum) + { + if(!(displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + DEVMODEW devMode = { .dmSize = sizeof(DEVMODEW) }; + if(EnumDisplaySettingsW(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode) == 0) + continue; + + ffdsAppendResolution(ds, devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmDisplayFrequency); + } + + //https://github.com/hykilpikonna/hyfetch/blob/master/neofetch#L2067 + const FFOSResult* os = ffDetectOS(instance); + if(ffStrbufCompS(&os->version, "11") == 0 || ffStrbufCompS(&os->version, "10") == 0) + ffStrbufSetS(&ds->dePrettyName, "Fluent"); + else if(ffStrbufCompS(&os->version, "8") == 0 || ffStrbufStartsWithS(&os->version, "8.")) + ffStrbufSetS(&ds->dePrettyName, "Metro"); + else + ffStrbufSetS(&ds->dePrettyName, "Aero"); +} diff --git a/src/detection/font/font.h b/src/detection/font/font.h index de78e5a92..63d19e2ee 100644 --- a/src/detection/font/font.h +++ b/src/detection/font/font.h @@ -12,9 +12,10 @@ typedef struct FFFontResult FFstrbuf error; /** - * Linux / BSD: QT, GTK2, GTK3, GTK4 - * MacOS: System, User, Monospace, Application - * Other: Unset, Unset, Unset, Unset + * Linux / BSD: QT, GTK2, GTK3, GTK4 + * MacOS: System, User, Monospace, Application + * Windows: Desktop, Unset, Unset, Unset + * Other: Unset, Unset, Unset, Unset */ FFstrbuf fonts[FF_DETECT_FONT_NUM_FONTS]; } FFFontResult; diff --git a/src/detection/font/font_android.c b/src/detection/font/font_nosupport.c similarity index 66% rename from src/detection/font/font_android.c rename to src/detection/font/font_nosupport.c index f2331a302..f88ad8395 100644 --- a/src/detection/font/font_android.c +++ b/src/detection/font/font_nosupport.c @@ -4,5 +4,5 @@ void ffDetectFontImpl(const FFinstance* instance, FFFontResult* result) { FF_UNUSED(instance); - ffStrbufAppendS(&result->error, "Not implemented"); + ffStrbufAppendS(&result->error, "Not supported on this platform"); } diff --git a/src/detection/font/font_windows.cpp b/src/detection/font/font_windows.cpp new file mode 100644 index 000000000..1d4196bde --- /dev/null +++ b/src/detection/font/font_windows.cpp @@ -0,0 +1,44 @@ +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 query[256] = {}; + swprintf(query, 256, L"SELECT IconTitleFaceName, IconTitleSize FROM Win32_Desktop WHERE Name LIKE '%%\\\\%s'", instance->state.passwd->pw_name); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(query, &result->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if(uReturn == 0) + { + ffStrbufInitS(&result->error, "No WMI result returned"); + pEnumerator->Release(); + return; + } + + FFstrbuf fontName; + ffStrbufInit(&fontName); + ffGetWmiObjString(pclsObj, L"IconTitleFaceName", &fontName); + + uint64_t fontSize; + ffGetWmiObjUnsigned(pclsObj, L"IconTitleSize", &fontSize); + + ffStrbufAppendF(&result->fonts[0], "%*s (%upt)", fontName.length, fontName.chars, (unsigned)fontSize); + + ffStrbufDestroy(&fontName); + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/gpu/gpu_android.c b/src/detection/gpu/gpu_nosupport.c similarity index 73% rename from src/detection/gpu/gpu_android.c rename to src/detection/gpu/gpu_nosupport.c index 6e0afe389..ef2d54abd 100644 --- a/src/detection/gpu/gpu_android.c +++ b/src/detection/gpu/gpu_nosupport.c @@ -3,5 +3,5 @@ const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) { FF_UNUSED(gpus, instance); - return "Unimplemented"; + return "Not supported on this platform"; } diff --git a/src/detection/gpu/gpu_windows.cpp b/src/detection/gpu/gpu_windows.cpp new file mode 100644 index 000000000..443ad422e --- /dev/null +++ b/src/detection/gpu/gpu_windows.cpp @@ -0,0 +1,44 @@ +extern "C" { +#include "gpu.h" +} +#include "util/windows/wmi.hpp" + +extern "C" +const char* ffDetectGPUImpl(FFlist* gpus, const FFinstance* instance) +{ + FF_UNUSED(instance); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Name, AdapterCompatibility, DriverVersion FROM Win32_VideoController", nullptr); + + if(!pEnumerator) + return "Query WMI service failed"; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + while(SUCCEEDED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) && uReturn != 0) + { + FFGPUResult* gpu = (FFGPUResult*)ffListAdd(gpus); + + ffStrbufInit(&gpu->vendor); + ffGetWmiObjString(pclsObj, L"AdapterCompatibility", &gpu->vendor); + if(ffStrbufStartsWithS(&gpu->vendor, "Intel ")) + { + //Intel returns "Intel Corporation", not sure about AMD + ffStrbufSetS(&gpu->vendor, "Intel"); + } + + ffStrbufInit(&gpu->name); + ffGetWmiObjString(pclsObj, L"Name", &gpu->name); + + ffStrbufInit(&gpu->driver); + ffGetWmiObjString(pclsObj, L"DriverVersion", &gpu->driver); + + gpu->temperature = FF_GPU_TEMP_UNSET; + gpu->coreCount = FF_GPU_CORE_COUNT_UNSET; + } + + pclsObj->Release(); + pEnumerator->Release(); + return nullptr; +} diff --git a/src/detection/host/host.h b/src/detection/host/host.h index cdf2fbf53..e66af41fb 100644 --- a/src/detection/host/host.h +++ b/src/detection/host/host.h @@ -6,6 +6,7 @@ #include "fastfetch.h" #define FF_HOST_PRODUCT_NAME_WSL "Windows Subsystem for Linux" +#define FF_HOST_PRODUCT_NAME_MSYS "Windows on MSYS" typedef struct FFHostResult { @@ -13,17 +14,11 @@ typedef struct FFHostResult FFstrbuf productName; FFstrbuf productVersion; FFstrbuf productSku; - FFstrbuf biosDate; - FFstrbuf biosRelease; - FFstrbuf biosVendor; - FFstrbuf biosVersion; - FFstrbuf boardName; - FFstrbuf boardVendor; - FFstrbuf boardVersion; FFstrbuf chassisType; FFstrbuf chassisVendor; FFstrbuf chassisVersion; FFstrbuf sysVendor; + FFstrbuf error; } FFHostResult; const FFHostResult* ffDetectHost(); diff --git a/src/detection/host/host_android.c b/src/detection/host/host_android.c index 1f2f10c11..b45283eb3 100644 --- a/src/detection/host/host_android.c +++ b/src/detection/host/host_android.c @@ -4,6 +4,8 @@ void ffDetectHostImpl(FFHostResult* host) { + ffStrbufInit(&host->error); + //Family ffStrbufInit(&host->productFamily); @@ -32,13 +34,6 @@ void ffDetectHostImpl(FFHostResult* host) ffStrbufInitA(&host->productVersion, 0); ffStrbufInitA(&host->productSku, 0); - ffStrbufInitA(&host->biosDate, 0); - ffStrbufInitA(&host->biosRelease, 0); - ffStrbufInitA(&host->biosVendor, 0); - ffStrbufInitA(&host->biosVersion, 0); - ffStrbufInitA(&host->boardName, 0); - ffStrbufInitA(&host->boardVendor, 0); - ffStrbufInitA(&host->boardVersion, 0); ffStrbufInitA(&host->chassisType, 0); ffStrbufInitA(&host->chassisVendor, 0); ffStrbufInitA(&host->chassisVersion, 0); diff --git a/src/detection/host/host_apple.c b/src/detection/host/host_apple.c index f39edf0e0..f00946ad6 100644 --- a/src/detection/host/host_apple.c +++ b/src/detection/host/host_apple.c @@ -112,19 +112,14 @@ static const char* getProductName(const FFstrbuf* hwModel) void ffDetectHostImpl(FFHostResult* host) { + ffStrbufInit(&host->error); + ffStrbufInit(&host->productName); ffStrbufInit(&host->productFamily); + ffStrbufInit(&host->productVersion); + ffStrbufInit(&host->productSku); ffStrbufInitA(&host->sysVendor, 0); - ffStrbufInitA(&host->productVersion, 0); - ffStrbufInitA(&host->productSku, 0); - ffStrbufInitA(&host->biosDate, 0); - ffStrbufInitA(&host->biosRelease, 0); - ffStrbufInitA(&host->biosVendor, 0); - ffStrbufInitA(&host->biosVersion, 0); - ffStrbufInitA(&host->boardName, 0); - ffStrbufInitA(&host->boardVendor, 0); - ffStrbufInitA(&host->boardVersion, 0); ffStrbufInitA(&host->chassisType, 0); ffStrbufInitA(&host->chassisVendor, 0); ffStrbufInitA(&host->chassisVersion, 0); diff --git a/src/detection/host/host_linux.c b/src/detection/host/host_linux.c index 80e048ae7..ef9cfbf5e 100644 --- a/src/detection/host/host_linux.c +++ b/src/detection/host/host_linux.c @@ -60,6 +60,8 @@ static void getHostProductName(FFstrbuf* name) void ffDetectHostImpl(FFHostResult* host) { + ffStrbufInit(&host->error); + ffStrbufInit(&host->productFamily); getHostValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->productFamily); @@ -72,27 +74,6 @@ void ffDetectHostImpl(FFHostResult* host) ffStrbufInit(&host->productSku); getHostValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->productSku); - ffStrbufInit(&host->biosDate); - getHostValue("/sys/devices/virtual/dmi/id/bios_date", "/sys/class/dmi/id/bios_date", &host->biosDate); - - ffStrbufInit(&host->biosRelease); - getHostValue("/sys/devices/virtual/dmi/id/bios_release", "/sys/class/dmi/id/bios_release", &host->biosRelease); - - ffStrbufInit(&host->biosVendor); - getHostValue("/sys/devices/virtual/dmi/id/bios_vendor", "/sys/class/dmi/id/bios_vendor", &host->biosVendor); - - ffStrbufInit(&host->biosVersion); - getHostValue("/sys/devices/virtual/dmi/id/bios_version", "/sys/class/dmi/id/bios_version", &host->biosVersion); - - ffStrbufInit(&host->boardName); - getHostValue("/sys/devices/virtual/dmi/id/board_name", "/sys/class/dmi/id/board_name", &host->boardName); - - ffStrbufInit(&host->boardVendor); - getHostValue("/sys/devices/virtual/dmi/id/board_vendor", "/sys/class/dmi/id/board_vendor", &host->boardVendor); - - ffStrbufInit(&host->boardVersion); - getHostValue("/sys/devices/virtual/dmi/id/board_version", "/sys/class/dmi/id/board_version", &host->boardVersion); - ffStrbufInit(&host->chassisType); getHostValue("/sys/devices/virtual/dmi/id/chassis_type", "/sys/class/dmi/id/chassis_type", &host->chassisType); @@ -109,10 +90,10 @@ void ffDetectHostImpl(FFHostResult* host) if(ffStrbufStartsWithS(&host->productName, "Standard PC")) ffStrbufPrependS(&host->productName, "KVM/QEMU "); - //On WSL, the real host can't be detected. Instead use WSL as host. - if(host->productFamily.length == 0 && host->productName.length == 0 && ( - getenv("WSLENV") != NULL || - getenv("WSL_DISTRO") != NULL || - getenv("WSL_INTEROP") != NULL - )) ffStrbufAppendS(&host->productName, FF_HOST_PRODUCT_NAME_WSL); + if(host->productFamily.length == 0 && host->productName.length == 0) + { + //On WSL, the real host can't be detected. Instead use WSL as host. + if(getenv("WSL_DISTRO") != NULL || getenv("WSL_INTEROP") != NULL) + ffStrbufAppendS(&host->productName, FF_HOST_PRODUCT_NAME_WSL); + } } diff --git a/src/detection/host/host_windows.cpp b/src/detection/host/host_windows.cpp new file mode 100644 index 000000000..34141cf72 --- /dev/null +++ b/src/detection/host/host_windows.cpp @@ -0,0 +1,42 @@ +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); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Name, Version, SKUNumber, Vendor FROM Win32_ComputerSystemProduct", &host->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + + if(uReturn == 0) + { + ffStrbufInitS(&host->error, "No Wmi result returned"); + pEnumerator->Release(); + return; + } + + ffGetWmiObjString(pclsObj, L"Name", &host->productName); + ffGetWmiObjString(pclsObj, L"Version", &host->productVersion); + ffGetWmiObjString(pclsObj, L"SKUNumber", &host->productSku); + ffGetWmiObjString(pclsObj, L"Vendor", &host->sysVendor); + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/kernel/kernel.h b/src/detection/kernel/kernel.h new file mode 100644 index 000000000..1b29350f3 --- /dev/null +++ b/src/detection/kernel/kernel.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_kernel_kernel +#define FF_INCLUDED_detection_kernel_kernel + +#include "fastfetch.h" + +typedef struct FFKernelResult +{ + FFstrbuf sysname; + FFstrbuf release; + FFstrbuf version; + FFstrbuf error; +} FFKernelResult; + +void ffDetectKernel(FFinstance* instance, FFKernelResult* result); + +#endif diff --git a/src/detection/kernel/kernel_linux.c b/src/detection/kernel/kernel_linux.c new file mode 100644 index 000000000..4ab70f027 --- /dev/null +++ b/src/detection/kernel/kernel_linux.c @@ -0,0 +1,9 @@ +#include "kernel.h" + +void ffDetectKernel(FFinstance* instance, FFKernelResult* result) +{ + ffStrbufInit(&result->error); + ffStrbufInitS(&result->sysname, instance->state.utsname.sysname); + ffStrbufInitS(&result->release, instance->state.utsname.release); + ffStrbufInitS(&result->version, instance->state.utsname.version); +} diff --git a/src/detection/kernel/kernel_windows.cpp b/src/detection/kernel/kernel_windows.cpp new file mode 100644 index 000000000..94a06d950 --- /dev/null +++ b/src/detection/kernel/kernel_windows.cpp @@ -0,0 +1,34 @@ +extern "C" { +#include "kernel.h" +} +#include "util/windows/wmi.hpp" + +extern "C" void ffDetectKernel(FFinstance* instance, FFKernelResult* kernel) +{ + FF_UNUSED(instance); + + ffStrbufInit(&kernel->error); + + ffStrbufInitS(&kernel->sysname, "Windows_NT"); + ffStrbufInit(&kernel->release); + ffStrbufInit(&kernel->version); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Version FROM Win32_OperatingSystem", &kernel->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + ffStrbufInitS(&kernel->error, "No WMI result returned"); + pEnumerator->Release(); + return; + } + + ffGetWmiObjString(pclsObj, L"Version", &kernel->release); + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/localip/localip.h b/src/detection/localip/localip.h new file mode 100644 index 000000000..ef16da50f --- /dev/null +++ b/src/detection/localip/localip.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_localip_localip +#define FF_INCLUDED_detection_localip_localip + +#include "fastfetch.h" + +typedef struct FFLocalIpResult +{ + FFstrbuf name; + FFstrbuf addr; + bool ipv6; +} FFLocalIpResult; + +const char* ffDetectLocalIps(const FFinstance* instance, FFlist* results); + +#endif diff --git a/src/detection/localip/localip_linux.c b/src/detection/localip/localip_linux.c new file mode 100644 index 000000000..b38958b19 --- /dev/null +++ b/src/detection/localip/localip_linux.c @@ -0,0 +1,66 @@ +#include "localip.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ + #include // FreeBSD needs this for AF_INET +#endif + +static void addNewIp(FFlist* list, const char* name, const char* addr, bool ipv6) +{ + FFLocalIpResult* ip = (FFLocalIpResult*) ffListAdd(list); + ffStrbufInitS(&ip->name, name); + ffStrbufInitS(&ip->addr, addr); + ip->ipv6 = ipv6; +} + +const char* ffDetectLocalIps(const FFinstance* instance, FFlist* results) +{ + struct ifaddrs* ifAddrStruct = NULL; + if(getifaddrs(&ifAddrStruct) < 0) + return "getifaddrs(&ifAddrStruct) failed"; + + for (struct ifaddrs* ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) + { + if (!ifa->ifa_addr || !(ifa->ifa_flags & IFF_RUNNING)) + continue; + + // loop back + if (strncmp(ifa->ifa_name, "lo", 2) == 0 && (ifa->ifa_name[2] == '\0' || isdigit(ifa->ifa_name[2])) && !instance->config.localIpShowLoop) + continue; + + if (instance->config.localIpNamePrefix.length && strncmp(ifa->ifa_name, instance->config.localIpNamePrefix.chars, instance->config.localIpNamePrefix.length) != 0) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) + { + // IPv4 + if (!instance->config.localIpShowIpV4) + continue; + + struct sockaddr_in* ipv4 = (struct sockaddr_in*) ifa->ifa_addr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); + addNewIp(results, ifa->ifa_name, addressBuffer, false); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + // IPv6 + if (!instance->config.localIpShowIpV6) + continue; + + struct sockaddr_in6* ipv6 = (struct sockaddr_in6 *)ifa->ifa_addr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); + addNewIp(results, ifa->ifa_name, addressBuffer, true); + } + } + + if (ifAddrStruct) freeifaddrs(ifAddrStruct); + return NULL; +} diff --git a/src/detection/localip/localip_windows.c b/src/detection/localip/localip_windows.c new file mode 100644 index 000000000..881dd2d6e --- /dev/null +++ b/src/detection/localip/localip_windows.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +#include "localip.h" + +static void addNewIp(FFlist* list, const wchar_t* name, const char* addr, bool ipv6) +{ + FFLocalIpResult* ip = (FFLocalIpResult*) ffListAdd(list); + + int len = (int)wcslen(name); + int size_needed = WideCharToMultiByte(CP_UTF8, 0, name, len, NULL, 0, NULL, NULL); + ffStrbufInitA(&ip->name, (uint32_t)size_needed + 1); + WideCharToMultiByte(CP_UTF8, 0, name, len, ip->name.chars, size_needed, NULL, NULL); + ip->name.length = (uint32_t)size_needed; + ip->name.chars[size_needed] = '\0'; + + ffStrbufInitS(&ip->addr, addr); + ip->ipv6 = ipv6; +} + +const char* ffDetectLocalIps(const FFinstance* instance, FFlist* results) +{ + IP_ADAPTER_ADDRESSES* adapter_addresses = NULL; + + // Start with a 16 KB buffer and resize if needed - + // multiple attempts in case interfaces change while + // we are in the middle of querying them. + DWORD adapter_addresses_buffer_size = 16 * 1024; + for (int attempts = 0; attempts != 3; ++attempts) + { + adapter_addresses = (IP_ADAPTER_ADDRESSES*)realloc(adapter_addresses, adapter_addresses_buffer_size); + assert(adapter_addresses); + + DWORD error = GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME, + NULL, + adapter_addresses, + &adapter_addresses_buffer_size); + + if (error == ERROR_SUCCESS) + break; + else if (ERROR_BUFFER_OVERFLOW == error) + continue; + else + return "GetAdaptersAddresses() failed"; + } + + // Iterate through all of the adapters + for (IP_ADAPTER_ADDRESSES* adapter = adapter_addresses; adapter; adapter = adapter->Next) + { + if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK && !instance->config.localIpShowLoop) + continue; + + for (IP_ADAPTER_UNICAST_ADDRESS* ifa = adapter->FirstUnicastAddress; ifa; ifa = ifa->Next) + { + if (ifa->Address.lpSockaddr->sa_family == AF_INET) + { + // IPv4 + if (!instance->config.localIpShowIpV4) + continue; + + SOCKADDR_IN* ipv4 = (SOCKADDR_IN*) ifa->Address.lpSockaddr; + char addressBuffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &ipv4->sin_addr, addressBuffer, INET_ADDRSTRLEN); + addNewIp(results, adapter->FriendlyName, addressBuffer, false); + } + else if (ifa->Address.lpSockaddr->sa_family == AF_INET6) + { + // IPv6 + if (!instance->config.localIpShowIpV6) + continue; + + SOCKADDR_IN6* ipv6 = (SOCKADDR_IN6*) ifa->Address.lpSockaddr; + char addressBuffer[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ipv6->sin6_addr, addressBuffer, INET6_ADDRSTRLEN); + addNewIp(results, adapter->FriendlyName, addressBuffer, false); + } + } + } + + free(adapter_addresses); + return NULL; +} diff --git a/src/detection/media/media_android.c b/src/detection/media/media_nosupport.c similarity index 63% rename from src/detection/media/media_android.c rename to src/detection/media/media_nosupport.c index 0cf6d53d1..e3d038186 100644 --- a/src/detection/media/media_android.c +++ b/src/detection/media/media_nosupport.c @@ -3,5 +3,5 @@ void ffDetectMediaImpl(const FFinstance* instance, FFMediaResult* media) { FF_UNUSED(instance); - ffStrbufAppendS(&media->error, "Media not supported on Android"); + ffStrbufAppendS(&media->error, "Not supported on this platform"); } diff --git a/src/detection/memory/memory_windows.cpp b/src/detection/memory/memory_windows.cpp new file mode 100644 index 000000000..91a42ef3c --- /dev/null +++ b/src/detection/memory/memory_windows.cpp @@ -0,0 +1,67 @@ +extern "C" { +#include "memory.h" +} +#include "util/windows/wmi.hpp" + +void detectRam(FFMemoryStorage* ram) +{ + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT TotalVisibleMemorySize, FreePhysicalMemory FROM Win32_OperatingSystem", &ram->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + ffStrbufInitS(&ram->error, "No WMI result returned"); + pEnumerator->Release(); + return; + } + + //KB + ffGetWmiObjUnsigned(pclsObj, L"TotalVisibleMemorySize", &ram->bytesTotal); + uint64_t bytesFree; + ffGetWmiObjUnsigned(pclsObj, L"FreePhysicalMemory", &bytesFree); + ram->bytesUsed = ram->bytesTotal - bytesFree; + + pclsObj->Release(); + pEnumerator->Release(); + + ram->bytesTotal *= 1024; + ram->bytesUsed *= 1024; +} + +void detectSwap(FFMemoryStorage* swap) +{ + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT AllocatedBaseSize, CurrentUsage FROM Win32_PageFileUsage", &swap->error); + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + ffStrbufInitS(&swap->error, "No WMI result returned"); + pEnumerator->Release(); + return; + } + + //MB + ffGetWmiObjUnsigned(pclsObj, L"AllocatedBaseSize", &swap->bytesTotal); + ffGetWmiObjUnsigned(pclsObj, L"CurrentUsage", &swap->bytesUsed); + + pclsObj->Release(); + pEnumerator->Release(); + + swap->bytesTotal *= 1024 * 1024; + swap->bytesUsed *= 1024 * 1024; +} + +extern "C" +void ffDetectMemoryImpl(FFMemoryResult* memory) +{ + detectRam(&memory->ram); + detectSwap(&memory->swap); +} diff --git a/src/detection/opengl/opengl.h b/src/detection/opengl/opengl.h new file mode 100644 index 000000000..206347184 --- /dev/null +++ b/src/detection/opengl/opengl.h @@ -0,0 +1,18 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_opengl_opengl +#define FF_INCLUDED_detection_opengl_opengl + +#include "fastfetch.h" + +typedef struct FFOpenGLResult +{ + FFstrbuf version; + FFstrbuf renderer; + FFstrbuf vendor; + FFstrbuf slv; +} FFOpenGLResult; + +const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result); + +#endif diff --git a/src/detection/opengl/opengl_apple.c b/src/detection/opengl/opengl_apple.c new file mode 100644 index 000000000..4ccbb5fe6 --- /dev/null +++ b/src/detection/opengl/opengl_apple.c @@ -0,0 +1,56 @@ + +#include "fastfetch.h" +#include "opengl.h" + +#define GL_SILENCE_DEPRECATION +#include +#include // This brings in CGL, not GL + +static const char* glHandleResult(FFOpenGLResult* result) +{ + ffStrbufAppendS(&result->version, (const char*) glGetString(GL_VERSION)); + ffStrbufAppendS(&result->renderer, (const char*) glGetString(GL_RENDERER)); + ffStrbufAppendS(&result->vendor, (const char*) glGetString(GL_VENDOR)); + ffStrbufAppendS(&result->slv, (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); + return NULL; +} + +static const char* cglHandleContext(FFOpenGLResult* result, CGLContextObj context) +{ + if(CGLSetCurrentContext(context) != kCGLNoError) + return "CGLSetCurrentContext() failed"; + + return glHandleResult(result); +} + +static const char* cglHandlePixelFormat(FFOpenGLResult* result, CGLPixelFormatObj pixelFormat) +{ + CGLContextObj context; + + if(CGLCreateContext(pixelFormat, NULL, &context) != kCGLNoError) + return "CGLCreateContext() failed"; + + const char* error = cglHandleContext(result, context); + CGLDestroyContext(context); + return error; +} + +const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result) +{ + FF_UNUSED(instance); + + CGLPixelFormatObj pixelFormat; + CGLPixelFormatAttribute attrs[] = { + kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, + kCGLPFAAccelerated, + 0 + }; + + GLint num; + if (CGLChoosePixelFormat(attrs, &pixelFormat, &num) != kCGLNoError) + return "CGLChoosePixelFormat() failed"; + + const char* error = cglHandlePixelFormat(result, pixelFormat); + CGLDestroyPixelFormat(pixelFormat); + return error; +} diff --git a/src/detection/opengl/opengl_linux.c b/src/detection/opengl/opengl_linux.c new file mode 100644 index 000000000..6e5b8d503 --- /dev/null +++ b/src/detection/opengl/opengl_linux.c @@ -0,0 +1,369 @@ +#include "fastfetch.h" +#include "opengl.h" + +#include + +#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) || defined(FF_HAVE_OSMESA) +#define FF_HAVE_GL 1 + +#include "common/library.h" + +#include + +#define FF_OPENGL_BUFFER_WIDTH 1 +#define FF_OPENGL_BUFFER_HEIGHT 1 + +typedef struct GLData +{ + FF_LIBRARY_SYMBOL(glGetString) +} GLData; + +static const char* glHandleResult(FFOpenGLResult* result, const GLData* data) +{ + ffStrbufAppendS(&result->version, (const char*) data->ffglGetString(GL_VERSION)); + ffStrbufAppendS(&result->renderer, (const char*) data->ffglGetString(GL_RENDERER)); + ffStrbufAppendS(&result->vendor, (const char*) data->ffglGetString(GL_VENDOR)); + ffStrbufAppendS(&result->slv, (const char*) data->ffglGetString(GL_SHADING_LANGUAGE_VERSION)); + return NULL; +} + +#endif // FF_HAVE_GL + +#ifdef FF_HAVE_EGL +#include + +typedef struct EGLData +{ + GLData glData; + + FF_LIBRARY_SYMBOL(eglGetProcAddress) + FF_LIBRARY_SYMBOL(eglGetDisplay) + FF_LIBRARY_SYMBOL(eglInitialize) + FF_LIBRARY_SYMBOL(eglBindAPI) + FF_LIBRARY_SYMBOL(eglGetConfigs) + FF_LIBRARY_SYMBOL(eglCreatePbufferSurface) + FF_LIBRARY_SYMBOL(eglCreateContext) + FF_LIBRARY_SYMBOL(eglMakeCurrent) + FF_LIBRARY_SYMBOL(eglDestroyContext) + FF_LIBRARY_SYMBOL(eglDestroySurface) + FF_LIBRARY_SYMBOL(eglTerminate) + + EGLDisplay display; + EGLConfig config; + EGLSurface surface; + EGLContext context; +} EGLData; + +static const char* eglHandleContext(FFOpenGLResult* result, EGLData* data) +{ + if(data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE) + return "eglMakeCurrent returned EGL_FALSE"; + + return glHandleResult(result, &data->glData); +} + +static const char* eglHandleSurface(FFOpenGLResult* result, EGLData* data) +{ + data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE}); + if(data->context == EGL_NO_CONTEXT) + return "eglCreateContext returned EGL_NO_CONTEXT"; + + const char* error = eglHandleContext(result, data); + data->ffeglDestroyContext(data->display, data->context); + return error; +} + +static const char* eglHandleDisplay(FFOpenGLResult* result, EGLData* data) +{ + if(data->ffeglBindAPI(EGL_OPENGL_API) != EGL_TRUE) + return "eglBindAPI returned EGL_FALSE"; + + EGLint eglConfigCount; + data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount); + if(eglConfigCount == 0) + return "eglGetConfigs returned 0 configs"; + + data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]){ + EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH, + EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT, + EGL_NONE + }); + + if(data->surface == EGL_NO_SURFACE) + return "eglCreatePbufferSurface returned EGL_NO_SURFACE"; + + const char* error = eglHandleSurface(result, data); + data->ffeglDestroySurface(data->display, data->surface); + return error; +} + +static const char* eglHandleData(FFOpenGLResult* result, EGLData* data) +{ + data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) data->ffeglGetProcAddress("glGetString"); + if(!data->glData.ffglGetString) + return "eglGetProcAddress(glGetString) returned NULL"; + + data->display = data->ffeglGetDisplay(EGL_DEFAULT_DISPLAY); + if(data->display == EGL_NO_DISPLAY) + return "eglGetDisplay returned EGL_NO_DISPLAY"; + + EGLint major, minor; + if(data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE) + return "eglInitialize returned EGL_FALSE"; + + const char* error = eglHandleDisplay(result, data); + data->ffeglTerminate(data->display); + return error; +} + +static const char* eglPrint(FFinstance* instance, FFOpenGLResult* result) +{ + EGLData eglData; + + FF_LIBRARY_LOAD(egl, &instance->config.libEGL, "dlopen egl failed", "libEGL.so", 1); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetProcAddress); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetDisplay); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglInitialize); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglBindAPI); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetConfigs); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreatePbufferSurface); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreateContext); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglMakeCurrent); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroyContext); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate); + + const char* error = eglHandleData(result, &eglData); + dlclose(egl); + return error; +} + +#endif //FF_HAVE_EGL + +#ifdef FF_HAVE_GLX +#include + +typedef struct GLXData +{ + GLData glData; + + FF_LIBRARY_SYMBOL(glXGetProcAddress) + FF_LIBRARY_SYMBOL(XOpenDisplay) + FF_LIBRARY_SYMBOL(glXChooseVisual) + FF_LIBRARY_SYMBOL(XCreatePixmap); + FF_LIBRARY_SYMBOL(glXCreateGLXPixmap) + FF_LIBRARY_SYMBOL(glXCreateContext) + FF_LIBRARY_SYMBOL(glXMakeCurrent) + FF_LIBRARY_SYMBOL(glXDestroyContext) + FF_LIBRARY_SYMBOL(glXDestroyGLXPixmap) + FF_LIBRARY_SYMBOL(XFreePixmap) + FF_LIBRARY_SYMBOL(XCloseDisplay) + + Display* display; + XVisualInfo* visualInfo; + Pixmap pixmap; + GLXPixmap glxPixmap; + GLXContext context; +} GLXData; + +static const char* glxHandleContext(FFOpenGLResult* result, GLXData* data) +{ + if(data->ffglXMakeCurrent(data->display, data->glxPixmap, data->context) != True) + return "glXMakeCurrent returned False"; + + return glHandleResult(result, &data->glData); +} + +static const char* glxHandleGLXPixmap(FFOpenGLResult* result, GLXData* data) +{ + data->context = data->ffglXCreateContext(data->display, data->visualInfo, NULL, True); + if(data->context == NULL) + return "glXCreateContext returned NULL"; + + const char* error = glxHandleContext(result, data); + data->ffglXDestroyContext(data->display, data->context); + return error; +} + +static const char* glxHandlePixmap(FFOpenGLResult* result, GLXData* data) +{ + data->glxPixmap = data->ffglXCreateGLXPixmap(data->display, data->visualInfo, data->pixmap); + if(data->glxPixmap == None) + return "glXCreateGLXPixmap returned None"; + + const char* error = glxHandleGLXPixmap(result, data); + data->ffglXDestroyGLXPixmap(data->display, data->glxPixmap); + return error; +} + +static const char* glxHandleVisualInfo(FFOpenGLResult* result, GLXData* data) +{ + data->pixmap = data->ffXCreatePixmap(data->display, DefaultRootWindow(data->display), FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT, (unsigned int) data->visualInfo->depth); + if(data->pixmap == None) + return "XCreatePixmap returned None"; + + const char* error = glxHandlePixmap(result, data); + data->ffXFreePixmap(data->display, data->pixmap); + return error; +} + +static const char* glxHandleDisplay(FFOpenGLResult* result, GLXData* data) +{ + data->visualInfo = data->ffglXChooseVisual(data->display, DefaultScreen(data->display), (int[]){None}); + if(data->visualInfo == NULL) + return "glXChooseVisual returned NULL"; + + return glxHandleVisualInfo(result, data); +} + +static const char* glxHandleData(FFOpenGLResult* result, GLXData* data) +{ + data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) data->ffglXGetProcAddress((const GLubyte*) "glGetString"); + if(data->glData.ffglGetString == NULL) + return "glXGetProcAddress(glGetString) returned NULL"; + + data->display = data->ffXOpenDisplay(NULL); + if(data->display == NULL) + return "XOpenDisplay returned NULL"; + + const char* error = glxHandleDisplay(result, data); + data->ffXCloseDisplay(data->display); + return error; +} + +static const char* glxPrint(FFinstance* instance, FFOpenGLResult* result) +{ + GLXData data; + + FF_LIBRARY_LOAD(glx, &instance->config.libGLX, "dlopen glx failed", "libGLX.so", 1); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXGetProcAddress); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XOpenDisplay); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXChooseVisual); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCreatePixmap); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateGLXPixmap); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateContext); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXMakeCurrent); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyContext); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyGLXPixmap); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFreePixmap); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCloseDisplay); + + const char* error = glxHandleData(result, &data); + dlclose(glx); + return error; +} + +#endif //FF_HAVE_GLX + +#ifdef FF_HAVE_OSMESA +#include + +typedef struct OSMesaData +{ + GLData glData; + + FF_LIBRARY_SYMBOL(OSMesaGetProcAddress) + FF_LIBRARY_SYMBOL(OSMesaCreateContext) + FF_LIBRARY_SYMBOL(OSMesaMakeCurrent) + FF_LIBRARY_SYMBOL(OSMesaDestroyContext) + + OSMesaContext context; +} OSMesaData; + +static const char* osMesaHandleContext(FFOpenGLResult* result, OSMesaData* data) +{ + unsigned char buffer[FF_OPENGL_BUFFER_WIDTH * FF_OPENGL_BUFFER_HEIGHT * sizeof(uint32_t)]; // 4 bytes per pixel (RGBA) + + if(data->ffOSMesaMakeCurrent(data->context, buffer, GL_UNSIGNED_BYTE, FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT) != GL_TRUE) + return "OSMesaMakeCurrent returned GL_FALSE"; + + return glHandleResult(result, &data->glData); +} + +static const char* osMesaHandleData(FFOpenGLResult* result, OSMesaData* data) +{ + //The case to void* is required here, because OSMESAproc can't be cast to (__typeof__(data->glData.ffglGetString)) without a warning, even though it is the actual type. + data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) (void*) data->ffOSMesaGetProcAddress("glGetString"); + if(data->glData.ffglGetString == NULL) + return "OSMesaGetProcAddress(glGetString) returned NULL"; + + data->context = data->ffOSMesaCreateContext(OSMESA_RGBA, NULL); + if(data->context == NULL) + return "OSMesaCreateContext returned NULL"; + + const char* error = osMesaHandleContext(result, data); + data->ffOSMesaDestroyContext(data->context); + return error; +} + +static const char* osMesaPrint(FFinstance* instance, FFOpenGLResult* result) +{ + OSMesaData data; + + FF_LIBRARY_LOAD(osmesa, &instance->config.libOSMesa, "dlopen osmesa failed", "libOSMesa.so", 8); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaGetProcAddress); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaCreateContext); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaMakeCurrent); + FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaDestroyContext); + + const char* error = osMesaHandleData(result, &data); + dlclose(osmesa); + return error; +} + +#endif //FF_HAVE_OSMESA + +const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result) +{ + #if FF_HAVE_GL + + if(instance->config.glType == FF_GL_TYPE_GLX) + { + #ifdef FF_HAVE_GLX + return glxPrint(instance, result); + #else + return "fastfetch was compiled without glx support"; + #endif + } + + if(instance->config.glType == FF_GL_TYPE_EGL) + { + #ifdef FF_HAVE_EGL + return eglPrint(instance, result); + #else + return "fastfetch was compiled without egl support"; + #endif + } + + if(instance->config.glType == FF_GL_TYPE_OSMESA) + { + #ifdef FF_HAVE_OSMESA + return osMesaPrint(instance, result); + #else + return "fastfetch was compiled without osmesa support"; + #endif + } + + const char* error = ""; // not NULL dummy value + + #ifdef FF_HAVE_EGL + error = eglPrint(instance, result); + #endif + + #ifdef FF_HAVE_GLX + if(error != NULL) + error = glxPrint(instance, result); + #endif + + //We don't use osmesa in auto mode here, because it is a software implementation, + //that doesn't reflect the opengl supported by the hardware + + return error; + + #else + + FF_UNUSED(instance, result); + return "Fastfetch was built without gl support."; + + #endif //FF_HAVE_GL +} diff --git a/src/detection/opengl/opengl_windows.c b/src/detection/opengl/opengl_windows.c new file mode 100644 index 000000000..92e56b974 --- /dev/null +++ b/src/detection/opengl/opengl_windows.c @@ -0,0 +1,108 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "opengl.h" + +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +#ifndef GL_SHADING_LANGUAGE_VERSION // For WGL + #define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#endif + +static const char* glHandleResult(FFOpenGLResult* result) +{ + ffStrbufAppendS(&result->version, (const char*) glGetString(GL_VERSION)); + ffStrbufAppendS(&result->renderer, (const char*) glGetString(GL_RENDERER)); + ffStrbufAppendS(&result->vendor, (const char*) glGetString(GL_VENDOR)); + ffStrbufAppendS(&result->slv, (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION)); + return NULL; +} + +const char* wglHandleContext(FFOpenGLResult* result, HDC hdc, HGLRC context) +{ + if(wglMakeCurrent(hdc, context) == FALSE) + return "wglMakeCurrent() failed"; + return glHandleResult(result); +} + +const char* wglHandlePixelFormat(FFOpenGLResult* result, HWND hWnd) +{ + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags + PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. + 32, // Colordepth of the framebuffer. + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 24, // Number of bits for the depthbuffer + 8, // Number of bits for the stencilbuffer + 0, // Number of Aux buffers in the framebuffer. + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + HDC hdc = GetDC(hWnd); + + if(SetPixelFormat(hdc, ChoosePixelFormat(hdc, &pfd), &pfd) == FALSE) + return "SetPixelFormat() failed"; + + HGLRC context = wglCreateContext(hdc); + if(context == NULL) + return "wglCreateContext() failed"; + + const char* error = wglHandleContext(result, hdc, context); + wglDeleteContext(context); + + return error; +} + +typedef struct WGLData +{ + FFOpenGLResult* result; + const char* error; +} WGLData; + +LRESULT CALLBACK wglHandleWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) + { + case WM_CREATE: { + WGLData* wglData = (WGLData*)((CREATESTRUCT*)lParam)->lpCreateParams; + wglData->error = wglHandlePixelFormat(wglData->result, hWnd); + PostQuitMessage(0); + return 0; + } + default: + return DefWindowProcW(hWnd, message, wParam, lParam); + } +} + +const char* ffDetectOpenGL(FFinstance* instance, FFOpenGLResult* result) +{ + FF_UNUSED(instance); + + MSG msg = {0}; + WNDCLASSW wc = { + .lpfnWndProc = wglHandleWndProc, + .hInstance = NULL, + .hbrBackground = (HBRUSH)COLOR_BACKGROUND, + .lpszClassName = L"ogl_version_check", + .style = CS_OWNDC, + }; + if(!RegisterClassW(&wc)) + return "RegisterClassW() failed"; + + WGLData data = { .result = result }; + HWND hWnd = CreateWindowW(wc.lpszClassName, L"ogl_version_check", 0, 0, 0, 1, 1, NULL, NULL, NULL, &data); + + while(GetMessageW(&msg, hWnd, 0, 0) > 0) + DispatchMessage(&msg); + + return data.error; +} diff --git a/src/detection/os/os_windows.cpp b/src/detection/os/os_windows.cpp new file mode 100644 index 000000000..b1c595dd4 --- /dev/null +++ b/src/detection/os/os_windows.cpp @@ -0,0 +1,70 @@ +extern "C" { +#include "os.h" +} +#include "util/windows/wmi.hpp" + +extern "C" +void ffDetectOSImpl(FFOSResult* os, const FFinstance* instance) +{ + ffStrbufInit(&os->name); + ffStrbufInit(&os->prettyName); + ffStrbufInit(&os->id); + ffStrbufInit(&os->idLike); + ffStrbufInit(&os->variant); + ffStrbufInit(&os->variantID); + ffStrbufInit(&os->version); + ffStrbufInit(&os->versionID); + ffStrbufInit(&os->codename); + ffStrbufInit(&os->buildID); + ffStrbufInit(&os->systemName); + ffStrbufInit(&os->architecture); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Caption, Version, BuildNumber, OSArchitecture FROM Win32_OperatingSystem", nullptr); + + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + pEnumerator->Release(); + return; + } + + ffGetWmiObjString(pclsObj, L"Caption", &os->variant); // Microsoft Windows 11 家庭中文版 + if(ffStrbufStartsWithS(&os->variant, "Microsoft Windows ")) + { + ffStrbufAppendS(&os->name, "Microsoft Windows"); + ffStrbufAppendS(&os->prettyName, "Windows"); + + ffStrbufSubstrAfter(&os->variant, strlen("Microsoft Windows ") - 1); // 11 家庭中文版 + uint32_t index = ffStrbufFirstIndexC(&os->variant, ' '); + ffStrbufAppendNS(&os->version, index, os->variant.chars); + ffStrbufSubstrAfter(&os->variant, index); + } + else + { + // Unknown Windows name, please report this + ffStrbufAppend(&os->name, &os->variant); + ffStrbufClear(&os->variant); + } + + if(getenv("MSYSTEM")) + ffStrbufAppendS(&os->id, "MSYS2"); + else + ffStrbufAppendF(&os->id, "Windows %*s", os->version.length, os->version.chars); + + ffGetWmiObjString(pclsObj, L"BuildNumber", &os->buildID); + ffGetWmiObjString(pclsObj, L"OSArchitecture", &os->architecture); + + #ifdef __MSYS__ + ffStrbufSetS(&os->systemName, instance->state.utsname.sysname); + #else + ffStrbufSetS(&os->systemName, "Windows"); + #endif + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/packages/packages.h b/src/detection/packages/packages.h new file mode 100644 index 000000000..9ecfdb7e9 --- /dev/null +++ b/src/detection/packages/packages.h @@ -0,0 +1,32 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_packages_packages +#define FF_INCLUDED_detection_packages_packages + +#include "fastfetch.h" + +typedef struct FFPackageCounts +{ + uint32_t pacman; + uint32_t dpkg; + uint32_t rpm; + uint32_t emerge; + uint32_t xbps; + uint32_t nixSystem; + uint32_t nixDefault; + uint32_t apk; + uint32_t pkg; + uint32_t flatpak; + uint32_t snap; + uint32_t brew; + uint32_t port; + uint32_t scoop; + + FFstrbuf pacmanBranch; + + uint32_t nixUser; +} FFPackageCounts; + +void ffDetectPackages(FFinstance* instance, FFPackageCounts* counts); + +#endif diff --git a/src/detection/packages/packages_linux.c b/src/detection/packages/packages_linux.c new file mode 100644 index 000000000..d25df2970 --- /dev/null +++ b/src/detection/packages/packages_linux.c @@ -0,0 +1,448 @@ +#include "common/io.h" +#include "common/properties.h" +#include "common/settings.h" +#include "common/processing.h" +#include "common/parsing.h" +#include "detection/os/os.h" + +#include "packages.h" + +#include +#include +#include +#include + +static uint32_t getNumElements(const char* dirname, unsigned char type) +{ + DIR* dirp = opendir(dirname); + if(dirp == NULL) + return 0; + + uint32_t num_elements = 0; + + struct dirent *entry; + while((entry = readdir(dirp)) != NULL) { + if(entry->d_type == type) + ++num_elements; + } + + if(type == DT_DIR) + num_elements -= 2; // accounting for . and .. + + closedir(dirp); + + return num_elements; +} + +#ifndef __APPLE__ + +static uint32_t getNumStrings(const char* filename, const char* needle) +{ + FILE* file = fopen(filename, "r"); + if(file == NULL) + return 0; + + uint32_t count = 0; + + char* line = NULL; + size_t len = 0; + + while(getline(&line, &len, file) != EOF) + { + if(strstr(line, needle) != NULL) + ++count; + } + + if(line != NULL) + free(line); + + fclose(file); + + return count; +} + +#ifndef __ANDROID__ + +static uint32_t countFilesRecursive(FFstrbuf* baseDirPath, const char* filename) +{ + uint32_t baseDirPathLength = baseDirPath->length; + + ffStrbufAppendC(baseDirPath, '/'); + ffStrbufAppendS(baseDirPath, filename); + bool exists = ffFileExists(baseDirPath->chars, S_IFREG); + ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); + if(exists) + return 1; + + DIR* dirp = opendir(baseDirPath->chars); + if(dirp == NULL) + return 0; + + ffStrbufAppendC(baseDirPath, '/'); + baseDirPathLength = baseDirPath->length; + + uint32_t sum = 0; + + struct dirent *entry; + while((entry = readdir(dirp)) != NULL) { + // According to the PMS, neither category nor package name can begin with '.', so no need to check for . or .. specifically + if(entry->d_type != DT_DIR || entry->d_name[0] == '.') + continue; + + ffStrbufAppendS(baseDirPath, entry->d_name); + sum += countFilesRecursive(baseDirPath, filename); + ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); + } + + closedir(dirp); + return sum; +} + +static uint32_t getNixPackages(char* path) +{ + //Nix detection is kinda slow, so we only do it if the dir exists + if(!ffFileExists(path, S_IFDIR)) + return 0; + + FFstrbuf output; + ffStrbufInitA(&output, 128); + + //https://github.com/LinusDierheimer/fastfetch/issues/195#issuecomment-1191748222 + FFstrbuf command; + ffStrbufInitA(&command, 255); + ffStrbufAppendS(&command, "for x in $(nix-store --query --requisites "); + ffStrbufAppendS(&command, path); + ffStrbufAppendS(&command, "); do if [ -d $x ]; then echo $x ; fi ; done | cut -d- -f2- | egrep '([0-9]{1,}\\.)+[0-9]{1,}' | egrep -v '\\-doc$|\\-man$|\\-info$|\\-dev$|\\-bin$|^nixos-system-nixos-' | uniq"); + + ffProcessAppendStdOut(&output, (char* const[]) { + "sh", + "-c", + command.chars, + NULL + }); + + //Each package is a new line in the output. If at least one line is found, add 1 for the last line. + uint32_t result = ffStrbufCountC(&output, '\n'); + if(result > 0) + result++; + + ffStrbufDestroy(&output); + return result; +} + +static uint32_t getXBPS(FFstrbuf* baseDir) +{ + DIR* dir = opendir(baseDir->chars); + if(dir == NULL) + return 0; + + uint32_t result = 0; + + struct dirent *entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_type != DT_REG || strncasecmp(entry->d_name, "pkgdb-", 6) != 0) + continue; + + ffStrbufAppendC(baseDir, '/'); + ffStrbufAppendS(baseDir, entry->d_name); + result = getNumStrings(baseDir->chars, "installed"); + break; + } + + closedir(dir); + return result; +} + +#endif // !__ANDROID__ + +#else // !__APPLE__ + +static uint32_t countBrewPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + ffStrbufAppendS(baseDir, "/Caskroom"); + result += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + ffStrbufAppendS(baseDir, "/Cellar"); + result += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +static uint32_t getBrewPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + const char* prefix = getenv("HOMEBREW_PREFIX"); + bool prefixSet = ffStrSet(prefix); + + if(prefixSet) + { + ffStrbufAppendS(baseDir, prefix); + result += countBrewPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + } + + ffStrbufAppendS(baseDir, "/opt/homebrew"); + if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) + result += countBrewPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + ffStrbufAppendS(baseDir, "/usr/local"); + if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) + result += countBrewPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +static uint32_t countMacPortsPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + ffStrbufAppendS(baseDir, "/var/macports/software"); + result += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +static uint32_t getMacPortsPackages(FFstrbuf* baseDir) +{ + uint32_t result = 0; + uint32_t baseDirLength = baseDir->length; + + const char* prefix = getenv("MACPORTS_PREFIX"); + bool prefixSet = ffStrSet(prefix); + + if(prefixSet) + { + ffStrbufAppendS(baseDir, prefix); + result += countMacPortsPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + } + + ffStrbufAppendS(baseDir, "/opt/local"); + if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) + result += countMacPortsPackages(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + return result; +} + +#endif // __APPLE__ + +#ifdef FF_HAVE_RPM +#include "common/library.h" +#include +#include +#include +#include + +static uint32_t getRpmFromLibrpm(const FFinstance* instance) +{ + FF_LIBRARY_LOAD(rpm, &instance->config.librpm, 0, "librpm.so", 12) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0) + FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0) + + // Don't print any error messages + ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG)); + + if(ffrpmReadConfigFiles(NULL, NULL) != 0) + { + dlclose(rpm); + return 0; + } + + rpmts ts = ffrpmtsCreate(); + if(ts == NULL) + { + dlclose(rpm); + return 0; + } + + rpmdbMatchIterator mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0); + if(mi == NULL) + { + ffrpmtsFree(ts); + dlclose(rpm); + return 0; + } + + int count = ffrpmdbGetIteratorCount(mi); + + ffrpmdbFreeIterator(mi); + ffrpmtsFree(ts); + dlclose(rpm); + + return count > 0 ? (uint32_t) count : 0; +} + +#endif //FF_HAVE_RPM + +static void getPackageCounts(const FFinstance* instance, FFstrbuf* baseDir, FFPackageCounts* packageCounts) +{ + #if defined(__APPLE__) || defined(__ANDROID__) + FF_UNUSED(instance); + #endif + + #ifndef __APPLE__ //Linux desktop and Android + + uint32_t baseDirLength = baseDir->length; + + //pacman + ffStrbufAppendS(baseDir, "/var/lib/pacman/local"); + packageCounts->pacman += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //dpkg + ffStrbufAppendS(baseDir, "/var/lib/dpkg/status"); + packageCounts->dpkg += getNumStrings(baseDir->chars, "Status: "); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + #ifndef __ANDROID__ //Linux Desktop + + //rpm + ffStrbufAppendS(baseDir, "/var/lib/rpm/rmpdb.sqlite"); + packageCounts->rpm += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(blob) FROM Packages"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //emerge + ffStrbufAppendS(baseDir, "/var/db/pkg"); + packageCounts->emerge += countFilesRecursive(baseDir, "SIZE"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //xps + ffStrbufAppendS(baseDir, "/var/db/xbps"); + packageCounts->xbps += getXBPS(baseDir); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //nix system + ffStrbufAppendS(baseDir, "/run/current-system"); + packageCounts->nixSystem += getNixPackages(baseDir->chars); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //nix default + ffStrbufAppendS(baseDir, "/nix/var/nix/profiles/default"); + packageCounts->nixDefault += getNixPackages(baseDir->chars); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //apk + ffStrbufAppendS(baseDir, "/lib/apk/db/installed"); + packageCounts->apk += getNumStrings(baseDir->chars, "C:Q"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //flatpak + ffStrbufAppendS(baseDir, "/var/lib/flatpak/app"); + packageCounts->flatpak += getNumElements(baseDir->chars, DT_DIR); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //snap + ffStrbufAppendS(baseDir, "/snap"); + uint32_t snap = getNumElements(baseDir->chars, DT_DIR); + if(snap > 0) + packageCounts->snap += (snap - 1); //Accounting for the /snap/bin folder + ffStrbufSubstrBefore(baseDir, baseDirLength); + + //pacman branch + ffStrbufAppendS(baseDir, FASTFETCH_TARGET_DIR_ETC"/pacman-mirrors.conf"); + if(ffParsePropFile(baseDir->chars, "Branch =", &packageCounts->pacmanBranch) && packageCounts->pacmanBranch.length == 0) + ffStrbufAppendS(&packageCounts->pacmanBranch, "stable"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + #endif // !__ANDROID__ + + #else // !__APPLE__ + + //brew + packageCounts->brew += getBrewPackages(baseDir); + packageCounts->port += getMacPortsPackages(baseDir); + + #endif // __APPLE__ + + #ifdef __FreeBSD__ + + ffStrbufAppendS(baseDir, "/var/db/pkg/local.sqlite"); + packageCounts->pkg += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(id) FROM packages"); + ffStrbufSubstrBefore(baseDir, baseDirLength); + + #endif // __FreeBSD__ +} + +static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDir, FFPackageCounts* packageCounts) +{ + uint32_t baseDirLength = baseDir->length; + + ffStrbufAppendS(baseDir, "/bedrock/strata"); + + DIR* dir = opendir(baseDir->chars); + if(dir == NULL) + { + ffStrbufSubstrBefore(baseDir, baseDirLength); + getPackageCounts(instance, baseDir, packageCounts); + return; + } + + ffStrbufAppendC(baseDir, '/'); + baseDirLength = baseDir->length; + + struct dirent* entry; + while((entry = readdir(dir)) != NULL) + { + if(entry->d_type != DT_DIR) + continue; + + ffStrbufAppendS(baseDir, entry->d_name); + getPackageCounts(instance, baseDir, packageCounts); + ffStrbufSubstrBefore(baseDir, baseDirLength); + } + + closedir(dir); +} + +void +#ifdef __MSYS__ +ffDetectPackagesPosix +#else +ffDetectPackages +#endif +(FFinstance* instance, FFPackageCounts* counts) +{ + FFstrbuf baseDir; + ffStrbufInitA(&baseDir, 512); + ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT); + + if(ffStrbufIgnCaseCompS(&ffDetectOS(instance)->id, "bedrock") == 0) + getPackageCountsBedrock(instance, &baseDir, counts); + else + getPackageCounts(instance, &baseDir, counts); + + // If SQL failed, we can still try with librpm. + // This is needed on openSUSE, which seems to use a proprietary database file + // This method doesn't work on bedrock, so we do it here. + #ifdef FF_HAVE_RPM + if(counts->rpm == 0) + counts->rpm = getRpmFromLibrpm(instance); + #endif + + #if !defined(__ANDROID__) && !defined(__APPLE__) + //nix user + ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir); + ffStrbufAppendS(&baseDir, "/.nix-profile"); + counts->nixUser = getNixPackages(baseDir.chars); + #endif + + ffStrbufDestroy(&baseDir); +} diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c new file mode 100644 index 000000000..53d45d351 --- /dev/null +++ b/src/detection/packages/packages_windows.c @@ -0,0 +1,41 @@ +#include "packages.h" + +#include +#include + +static uint32_t getNumElements(const char* searchPath /* including `\*` suffix */, DWORD type) +{ + uint32_t counter = 0; + WIN32_FIND_DATAA wfd; + HANDLE hFind = FindFirstFileA(searchPath, &wfd); + + if (hFind != INVALID_HANDLE_VALUE) + { + do // Managed to locate and create an handle to that folder. + { + if(wfd.dwFileAttributes & type) + counter++; + } while (FindNextFileA(hFind, &wfd) == TRUE); + FindClose(hFind); + } + + return counter; +} + +#ifdef __MSYS__ + void ffDetectPackagesPosix(const FFinstance* instance, FFPackageCounts* counts); +#endif + +void ffDetectPackages(FFinstance* instance, FFPackageCounts* counts) +{ + #ifdef __MSYS__ + //We have pacman and maybe others in MSYS, but not package managers for Windows + if(getenv("MSYSTEM")) + return ffDetectPackagesPosix(instance, counts); + #endif + + FFstrbuf scoopPath; + ffStrbufInitF(&scoopPath, "%s/scoop/apps/*", getenv("USERPROFILE")); + counts->scoop = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY) - 3; // . .. scoop + ffStrbufDestroy(&scoopPath); +} diff --git a/src/detection/poweradapter/poweradapter_linux.c b/src/detection/poweradapter/poweradapter_nosupport.c similarity index 75% rename from src/detection/poweradapter/poweradapter_linux.c rename to src/detection/poweradapter/poweradapter_nosupport.c index 4e5bebd80..cf6d1c841 100644 --- a/src/detection/poweradapter/poweradapter_linux.c +++ b/src/detection/poweradapter/poweradapter_nosupport.c @@ -3,5 +3,5 @@ const char* ffDetectPowerAdapterImpl(FFinstance* instance, FFlist* results) { FF_UNUSED(instance, results); - return "Unimplemented"; + return "Not supported on this platform"; } diff --git a/src/detection/processes/processes.h b/src/detection/processes/processes.h new file mode 100644 index 000000000..bfc4bce6c --- /dev/null +++ b/src/detection/processes/processes.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_processes_processes +#define FF_INCLUDED_detection_processes_processes + +#include "fastfetch.h" + +uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error); + +#endif diff --git a/src/detection/processes/processes_apple.c b/src/detection/processes/processes_apple.c new file mode 100644 index 000000000..32a251b1a --- /dev/null +++ b/src/detection/processes/processes_apple.c @@ -0,0 +1,18 @@ +#include "processes.h" + +#include + +uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error) +{ + FF_UNUSED(instance); + + int request[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; + size_t length; + + if(sysctl(request, sizeof(request) / sizeof(*request), NULL, &length, NULL, 0) != 0) + { + ffStrbufAppend(error, "sysctl() failed"); + return 0; + } + return (uint32_t)(length / sizeof(struct kinfo_proc)); +} diff --git a/src/detection/processes/processes_linux.c b/src/detection/processes/processes_linux.c new file mode 100644 index 000000000..da197b03e --- /dev/null +++ b/src/detection/processes/processes_linux.c @@ -0,0 +1,12 @@ +#include "processes.h" + +uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error) +{ + #if FF_HAVE_SYSINFO_H + FF_UNUSED(error); + return (uint32_t) instance->state.sysinfo.procs; + #else + ffStrbufAppendS(error, "Unimplemented"); + return 0; + #endif +} diff --git a/src/detection/processes/processes_windows.cpp b/src/detection/processes/processes_windows.cpp new file mode 100644 index 000000000..5d491d16e --- /dev/null +++ b/src/detection/processes/processes_windows.cpp @@ -0,0 +1,30 @@ +extern "C" { +#include "processes.h" +} +#include "util/windows/wmi.hpp" + +uint32_t ffDetectProcesses(FFinstance* instance, FFstrbuf* error) +{ + FF_UNUSED(instance); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT NumberOfProcesses FROM Win32_OperatingSystem", error); + + if(!pEnumerator) + return 0; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + ffStrbufAppendS(error, "No Wmi result returned"); + pEnumerator->Release(); + return 0; + } + + uint64_t result = 0; + ffGetWmiObjUnsigned(pclsObj, L"NumberOfProcesses", &result); + pclsObj->Release(); + pEnumerator->Release(); + return (uint32_t)result; +} diff --git a/src/detection/terminalfont/terminalfont.c b/src/detection/terminalfont/terminalfont.c index 2ecb332df..297301f2f 100644 --- a/src/detection/terminalfont/terminalfont.c +++ b/src/detection/terminalfont/terminalfont.c @@ -2,7 +2,7 @@ #include "common/properties.h" #include "common/processing.h" #include "detection/internal.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" static void detectAlacritty(const FFinstance* instance, FFTerminalFontResult* terminalFont) { FFstrbuf fontName; diff --git a/src/detection/terminalfont/terminalfont_android.c b/src/detection/terminalfont/terminalfont_android.c index 8211ab1d2..24a9baacd 100644 --- a/src/detection/terminalfont/terminalfont_android.c +++ b/src/detection/terminalfont/terminalfont_android.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "terminalfont.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include "common/io.h" #include diff --git a/src/detection/terminalfont/terminalfont_apple.m b/src/detection/terminalfont/terminalfont_apple.m index ffe0064af..fb8c2769b 100644 --- a/src/detection/terminalfont/terminalfont_apple.m +++ b/src/detection/terminalfont/terminalfont_apple.m @@ -1,6 +1,6 @@ #include "terminalfont.h" #include "common/font.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include "util/apple/osascript.h" #include diff --git a/src/detection/terminalfont/terminalfont_linux.c b/src/detection/terminalfont/terminalfont_linux.c index f68d45744..06a71da50 100644 --- a/src/detection/terminalfont/terminalfont_linux.c +++ b/src/detection/terminalfont/terminalfont_linux.c @@ -2,7 +2,7 @@ #include "common/settings.h" #include "common/properties.h" #include "common/parsing.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include "detection/displayserver/displayserver.h" static const char* getSystemMonospaceFont(const FFinstance* instance) @@ -189,7 +189,7 @@ static const char* detectFromWTImpl(const FFinstance* instance, FFstrbuf* conten { CJSONData cjsonData; - FF_LIBRARY_LOAD(libcjson, &instance->config.libcJSON, "dlopen libcjson failed", "libcjson"FF_LIBRARY_EXTENSION, 1) + FF_LIBRARY_LOAD(libcjson, &instance->config.libcJSON, "dlopen libcjson"FF_LIBRARY_EXTENSION" failed", "libcjson"FF_LIBRARY_EXTENSION, 1) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_Parse) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_IsObject) FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libcjson, cjsonData, cJSON_GetObjectItemCaseSensitive) @@ -292,12 +292,17 @@ static void detectFromWindowsTeriminal(const FFinstance* instance, FFTerminalFon FFstrbuf name; ffStrbufInit(&name); int size = -1; - detectFromWTImpl(instance, &json, &name, &size); + error = detectFromWTImpl(instance, &json, &name, &size); ffStrbufDestroy(&json); - char sizeStr[16]; - snprintf(sizeStr, sizeof(sizeStr), "%d", size); - ffFontInitValues(&terminalFont->font, name.chars, sizeStr); + if(error) + ffStrbufAppendS(&terminalFont->error, error); + else + { + char sizeStr[16]; + snprintf(sizeStr, sizeof(sizeStr), "%d", size); + ffFontInitValues(&terminalFont->font, name.chars, sizeStr); + } ffStrbufDestroy(&name); } @@ -312,6 +317,82 @@ static void detectFromWindowsTeriminal(const FFinstance* instance, FFTerminalFon #endif +#if defined(_WIN32) || defined(__MSYS__) +// TODO: move to a separate file + +static void detectMintty(const FFinstance* instance, FFTerminalFontResult* terminalFont) +{ + FFstrbuf fontName; + ffStrbufInit(&fontName); + + FFstrbuf fontSize; + ffStrbufInit(&fontSize); + + ffParsePropFileHomeValues(instance, ".minttyrc", 2, (FFpropquery[]) { + {"Font=", &fontName}, + {"FontHeight=", &fontSize} + }); + if(fontName.length == 0) + ffStrbufAppendS(&fontName, "Lucida Console"); + if(fontSize.length == 0) + ffStrbufAppendC(&fontSize, '9'); + + ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars); + + ffStrbufDestroy(&fontName); + ffStrbufDestroy(&fontSize); +} + +#define WIN32_LEAN_AND_MEAN 1 +#include + +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 hKey; + if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ, &hKey) != ERROR_SUCCESS) + { + ffStrbufAppendS(&terminalFont->error, "RegOpenKeyExW() failed"); + return; + } + + DWORD bufSize; + + wchar_t fontNameW[64]; + bufSize = sizeof(fontNameW); + if(RegQueryValueExW(hKey, L"FaceName", NULL, NULL, (LPBYTE)fontNameW, &bufSize) != ERROR_SUCCESS) + { + ffStrbufAppendS(&terminalFont->error, "RegOpenKeyExW(FaceName) failed"); + goto exit; + } + fontNameW[bufSize] = '\0'; + + char fontNameA[128]; + int fontNameALen = WideCharToMultiByte(CP_UTF8, 0, fontNameW, (int)(bufSize / 2), fontNameA, sizeof(fontNameA), NULL, NULL); + fontNameA[fontNameALen] = '\0'; + + uint32_t fontSizeNum = 0; + bufSize = sizeof(fontSizeNum); + if(RegQueryValueExW(hKey, L"fontSize", NULL, NULL, (LPBYTE)&fontSizeNum, &bufSize) != ERROR_SUCCESS) + { + ffStrbufAppendS(&terminalFont->error, "RegOpenKeyExW(fontSize) failed"); + goto exit; + } + + char fontSize[16]; + snprintf(fontSize, sizeof(fontSize), "%u", (fontSizeNum >> 16)); + + ffFontInitValues(&terminalFont->font, fontNameA, fontSize); + +exit: + RegCloseKey(hKey); +} + +#endif //defined(_WIN32) || defined(__MSYS__) + void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalShellResult* terminalShell, FFTerminalFontResult* terminalFont) { if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0) @@ -324,6 +405,14 @@ void ffDetectTerminalFontPlatform(const FFinstance* instance, const FFTerminalSh detectFromGSettings(instance, "/com/gexperts/Tilix/profiles/", "com.gexperts.Tilix.ProfilesList", "com.gexperts.Tilix.Profile", terminalFont); else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "gnome-terminal-") == 0) detectFromGSettings(instance, "/org/gnome/terminal/legacy/profiles:/:", "org.gnome.Terminal.ProfilesList", "org.gnome.Terminal.Legacy.Profile", terminalFont); - else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Windows Terminal") == 0) + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "Windows Terminal") == 0 || + ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "WindowsTerminal.exe") == 0) detectFromWindowsTeriminal(instance, terminalFont); + + #if defined(_WIN32) || defined(__MSYS__) + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "mintty") == 0) + detectMintty(instance, terminalFont); + else if(ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "conhost.exe") == 0) + detectConhost(instance, terminalFont); + #endif } diff --git a/src/detection/terminalshell.h b/src/detection/terminalshell/terminalshell.h similarity index 91% rename from src/detection/terminalshell.h rename to src/detection/terminalshell/terminalshell.h index 2791856ac..8783e3410 100644 --- a/src/detection/terminalshell.h +++ b/src/detection/terminalshell/terminalshell.h @@ -10,10 +10,12 @@ typedef struct FFTerminalShellResult FFstrbuf shellProcessName; FFstrbuf shellExe; const char* shellExeName; //pointer to a char in shellExe + FFstrbuf shellPrettyName; FFstrbuf shellVersion; FFstrbuf terminalProcessName; FFstrbuf terminalExe; + FFstrbuf terminalPrettyName; const char* terminalExeName; //pointer to a char in terminalExe FFstrbuf userShellExe; diff --git a/src/detection/terminalShell.c b/src/detection/terminalshell/terminalshell_linux.c similarity index 81% rename from src/detection/terminalShell.c rename to src/detection/terminalshell/terminalshell_linux.c index cd3785a0f..3adca984f 100644 --- a/src/detection/terminalShell.c +++ b/src/detection/terminalshell/terminalshell_linux.c @@ -1,8 +1,9 @@ #include "fastfetch.h" -#include "detection/terminalshell.h" +#include "detection/host/host.h" #include "common/io.h" #include "common/parsing.h" #include "common/processing.h" +#include "terminalshell.h" #include #include @@ -12,6 +13,7 @@ static void setExeName(FFstrbuf* exe, const char** exeName) { + assert(exe->length > 0); uint32_t lastSlashIndex = ffStrbufLastIndexC(exe, '/'); if(lastSlashIndex < exe->length) *exeName = exe->chars + lastSlashIndex + 1; @@ -19,15 +21,20 @@ static void setExeName(FFstrbuf* exe, const char** exeName) static void getProcessInformation(const char* pid, FFstrbuf* processName, FFstrbuf* exe, const char** exeName) { + assert(processName->length > 0); + FFstrbuf cmdlineFilePath; ffStrbufInit(&cmdlineFilePath); ffStrbufAppendS(&cmdlineFilePath, "/proc/"); ffStrbufAppendS(&cmdlineFilePath, pid); ffStrbufAppendS(&cmdlineFilePath, "/cmdline"); - ffReadFileBuffer(cmdlineFilePath.chars, exe); - ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments - ffStrbufTrimLeft(exe, '-'); //Happens in TTY + ffStrbufClear(exe); + if(ffAppendFileBuffer(cmdlineFilePath.chars, exe)) + { + ffStrbufSubstrBeforeFirstC(exe, '\0'); //Trim the arguments + ffStrbufTrimLeft(exe, '-'); //Happens in TTY + } if(exe->length == 0) ffStrbufSet(exe, processName); @@ -93,16 +100,17 @@ static void getTerminalShell(FFTerminalShellResult* result, const char* pid) strcasecmp(name, "ksh") == 0 || strcasecmp(name, "fish") == 0 || strcasecmp(name, "dash") == 0 || + strcasecmp(name, "pwsh") == 0 || strcasecmp(name, "git-shell") == 0 ) { - ffStrbufAppendS(&result->shellProcessName, name); + ffStrbufSetS(&result->shellProcessName, name); // prevent from `fishbash` getProcessInformation(pid, &result->shellProcessName, &result->shellExe, &result->shellExeName); getTerminalShell(result, ppid); return; } - ffStrbufAppendS(&result->terminalProcessName, name); + ffStrbufSetS(&result->terminalProcessName, name); getProcessInformation(pid, &result->terminalProcessName, &result->terminalExe, &result->terminalExeName); } @@ -149,17 +157,18 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) getenv("KONSOLE_VERSION") != NULL )) term = "konsole"; - //MacOS + //MacOS, mintty if(!ffStrSet(term)) term = getenv("TERM_PROGRAM"); //We are in WSL but not in Windows Terminal - if(!ffStrSet(term) && ( - getenv("WSLENV") != NULL || - getenv("WSL_DISTRO") != NULL || - getenv("WSL_INTEROP") != NULL - )) + if(!ffStrSet(term)) + { + const FFHostResult* host = ffDetectHost(); + if(ffStrbufCompS(&host->productName, FF_HOST_PRODUCT_NAME_WSL) == 0 || + ffStrbufCompS(&host->productName, FF_HOST_PRODUCT_NAME_MSYS) == 0) //TODO better WSL or MSYS detection term = "conhost"; + } //Normal Terminal if(!ffStrSet(term)) @@ -169,14 +178,19 @@ static void getTerminalFromEnv(FFTerminalShellResult* result) if(!ffStrSet(term) || strcasecmp(term, "linux") == 0) term = ttyname(STDIN_FILENO); - ffStrbufSetS(&result->terminalProcessName, term); - ffStrbufSetS(&result->terminalExe, term); - setExeName(&result->terminalExe, &result->terminalExeName); + if(ffStrSet(term)) + { + ffStrbufSetS(&result->terminalProcessName, term); + ffStrbufSetS(&result->terminalExe, term); + setExeName(&result->terminalExe, &result->terminalExeName); + } } static void getUserShellFromEnv(FFTerminalShellResult* result) { ffStrbufAppendS(&result->userShellExe, getenv("SHELL")); + if(result->userShellExe.length == 0) + return; setExeName(&result->userShellExe, &result->userShellExeName); //If shell detection via processes failed @@ -250,17 +264,24 @@ static void getShellVersionGeneric(FFstrbuf* exe, const char* exeName, FFstrbuf* static void getShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version) { + ffStrbufClear(version); if(strcasecmp(exeName, "bash") == 0) getShellVersionBash(exe, version); else if(strcasecmp(exeName, "zsh") == 0) getShellVersionZsh(exe, version); - else if(strcasecmp(exeName, "fish") == 0) + else if(strcasecmp(exeName, "fish") == 0 || strcasecmp(exeName, "pwsh") == 0) getShellVersionFish(exe, version); else getShellVersionGeneric(exe, exeName, version); } -const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) +const FFTerminalShellResult* +#if defined(__MSYS__) || defined(_WIN32) + ffDetectTerminalShellPosix +#else + ffDetectTerminalShell +#endif +(const FFinstance* instance) { FF_UNUSED(instance); @@ -301,6 +322,14 @@ const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) else ffStrbufSet(&result.userShellVersion, &result.shellVersion); + // https://github.com/LinusDierheimer/fastfetch/discussions/280#discussioncomment-3831734 + ffStrbufInitS(&result.shellPrettyName, result.shellExeName); + + 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); + pthread_mutex_unlock(&mutex); return &result; } diff --git a/src/detection/terminalshell/terminalshell_windows.cpp b/src/detection/terminalshell/terminalshell_windows.cpp new file mode 100644 index 000000000..7496fd6be --- /dev/null +++ b/src/detection/terminalshell/terminalshell_windows.cpp @@ -0,0 +1,158 @@ +extern "C" { +#include "terminalshell.h" +#include "common/processing.h" +} +#include "util/windows/wmi.hpp" + +#include +#include +#include + +struct ProcessInfo +{ + uint32_t pid; + FFstrbuf psName; +}; + +static bool getProcessInfo(uint32_t pid, uint32_t* ppid, FFstrbuf* pname, FFstrbuf* exe) +{ + wchar_t query[256] = {}; + swprintf(query, 256, L"SELECT Name, ParentProcessId, ExecutablePath FROM Win32_Process WHERE ProcessId = %" PRIu32, pid); + + IEnumWbemClassObject* pEnumerator = ffQueryWmi(query, nullptr); + if(!pEnumerator) + return false; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + + if(FAILED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) || uReturn == 0) + { + pEnumerator->Release(); + return false; + } + + if(ppid) + { + uint64_t value; + ffGetWmiObjUnsigned(pclsObj, L"ParentProcessId", &value); + *ppid = (uint32_t) value; + } + + if(pname) + ffGetWmiObjString(pclsObj, L"Name", pname); + + if(exe) + ffGetWmiObjString(pclsObj, L"ExecutablePath", exe); + + pclsObj->Release(); + pEnumerator->Release(); + return true; +} + +static void getShellVersion(FFstrbuf* exe, FFstrbuf* version) +{ + char* const argv[] = { exe->chars, (char*)"--version", NULL }; + ffProcessAppendStdOut(version, argv); + ffStrbufTrimRight(version, '\n'); + ffStrbufSubstrAfterLastC(version, ' '); +} + +static uint32_t getShellInfo(FFTerminalShellResult* result, uint32_t pid) +{ + uint32_t ppid; + + if(!getProcessInfo(pid, &ppid, &result->shellProcessName, &result->shellExe)) + return 0; + result->shellExeName = result->shellExe.chars + ffStrbufLastIndexC(&result->shellExe, '\\') + 1; + + ffStrbufSet(&result->shellPrettyName, &result->shellProcessName); + if(ffStrbufEndsWithIgnCaseS(&result->shellPrettyName, ".exe")) + ffStrbufSubstrBefore(&result->shellPrettyName, result->shellPrettyName.length - 4); + + if(ffStrbufIgnCaseCompS(&result->shellPrettyName, "pwsh") == 0) + { + ffStrbufSetS(&result->shellPrettyName, "PowerShell"); + getShellVersion(&result->shellExe, &result->shellVersion); + } + else if(ffStrbufIgnCaseCompS(&result->shellPrettyName, "powershell") == 0) + ffStrbufSetS(&result->shellPrettyName, "Windows PowerShell"); + else if(ffStrbufIgnCaseCompS(&result->shellPrettyName, "cmd") == 0) + ffStrbufSetS(&result->shellPrettyName, "Command Prompt"); + + return ppid; +} + +static uint32_t getTerminalInfo(FFTerminalShellResult* result, uint32_t pid) +{ + uint32_t ppid; + + if(!getProcessInfo(pid, &ppid, &result->terminalProcessName, &result->terminalExe)) + return 0; + result->terminalExeName = result->terminalExe.chars + ffStrbufLastIndexC(&result->terminalExe, '\\'); + + ffStrbufSet(&result->terminalPrettyName, &result->terminalProcessName); + if(ffStrbufEndsWithIgnCaseS(&result->terminalPrettyName, ".exe")) + ffStrbufSubstrBefore(&result->terminalPrettyName, result->terminalPrettyName.length - 4); + + if(ffStrbufIgnCaseCompS(&result->terminalPrettyName, "WindowsTerminal") == 0) + ffStrbufSetS(&result->terminalPrettyName, "Windows Terminal"); + else if(ffStrbufIgnCaseCompS(&result->terminalPrettyName, "conhost") == 0) + ffStrbufSetS(&result->terminalPrettyName, "Console Window Host"); + + return ppid; +} + +#ifdef __MSYS__ + extern "C" + const FFTerminalShellResult* ffDetectTerminalShellPosix(const FFinstance* instance); +#endif + +const FFTerminalShellResult* ffDetectTerminalShell(const FFinstance* instance) +{ + #ifdef __MSYS__ + // This is hacky. + // When running inside of MSYS2, the real Windows parent process doesn't exist and we must find it in Linux way ( /proc/self/xxx ) + // When running outside of MSYS2, /proc/self/xxx doesn't exist and we must find it in Windows way + if(getenv("MSYSTEM")) + return ffDetectTerminalShellPosix(instance); + #endif + + static FFTerminalShellResult result; + static bool init = false; + if(init) + return &result; + init = true; + + ffStrbufInit(&result.shellProcessName); + ffStrbufInitA(&result.shellExe, 128); + result.shellExeName = result.shellExe.chars; + ffStrbufInit(&result.shellPrettyName); + ffStrbufInit(&result.shellVersion); + + ffStrbufInit(&result.terminalProcessName); + ffStrbufInitA(&result.terminalExe, 128); + result.terminalExeName = result.terminalExe.chars; + ffStrbufInit(&result.terminalPrettyName); + + ffStrbufInit(&result.userShellExe); + result.userShellExeName = result.userShellExe.chars; + ffStrbufInit(&result.userShellVersion); + + uint32_t ppid = GetCurrentProcessId(); + if(!getProcessInfo(ppid, &ppid, nullptr, nullptr)) + return &result; + + ppid = getShellInfo(&result, ppid); + if(ppid == 0) + return &result; + + // TODO: handle nested shells + // TODO: handle running without shells ( dblclick exe in Windows Explorer ) + + ppid = getTerminalInfo(&result, ppid); + if(ppid == 0) + return &result; + + return &result; +} diff --git a/src/detection/users/users.h b/src/detection/users/users.h new file mode 100644 index 000000000..8842e16a2 --- /dev/null +++ b/src/detection/users/users.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_users_users +#define FF_INCLUDED_detection_users_users + +#include "fastfetch.h" + +void ffDetectUsers(FFlist* users /* List of FFstrbuf */, FFstrbuf* error); + +#endif diff --git a/src/detection/users/users_linux.c b/src/detection/users/users_linux.c new file mode 100644 index 000000000..b1dcee8ed --- /dev/null +++ b/src/detection/users/users_linux.c @@ -0,0 +1,36 @@ +#include "fastfetch.h" +#include "users.h" + +#if FF_HAVE_UTMPX_H + #include +#else + //for Android compatibility + #include + #define utmpx utmp + #define setutxent setutent + #define getutxent getutent +#endif + +void ffDetectUsers(FFlist* users, FFstrbuf* error) +{ + struct utmpx* n = NULL; + setutxent(); + +next: + while((n = getutxent())) + { + if(n->ut_type != USER_PROCESS) + continue; + + for(uint32_t i = 0; i < users->length; ++i) + { + if(ffStrbufCompS((FFstrbuf*)ffListGet(users, i), n->ut_user) == 0) + goto next; + } + + ffStrbufInitS((FFstrbuf*)ffListAdd(users), n->ut_user); + } + + 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 new file mode 100644 index 000000000..43a6fb3d2 --- /dev/null +++ b/src/detection/users/users_windows.cpp @@ -0,0 +1,42 @@ +extern "C" { +#include "users.h" +} +#include "util/windows/wmi.hpp" + +void ffDetectUsers(FFlist* users, FFstrbuf* error) +{ + IEnumWbemClassObject* pEnumerator = ffQueryWmi(L"SELECT Antecedent FROM Win32_LoggedOnUser", error); + + if(!pEnumerator) + return; + + IWbemClassObject *pclsObj = NULL; + ULONG uReturn = 0; + +next: + while(SUCCEEDED(pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn)) && uReturn != 0) + { + FFstrbuf antecedent; + ffStrbufInit(&antecedent); + ffGetWmiObjString(pclsObj, 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"); + + pclsObj->Release(); + pEnumerator->Release(); +} diff --git a/src/detection/vulkan.c b/src/detection/vulkan.c index a04ab11d2..1ff96e2c9 100644 --- a/src/detection/vulkan.c +++ b/src/detection/vulkan.c @@ -35,7 +35,7 @@ static void applyDriverName(VkPhysicalDeviceDriverProperties* properties, FFstrb static const char* detectVulkan(const FFinstance* instance, FFVulkanResult* result) { - FF_LIBRARY_LOAD(vulkan, &instance->config.libVulkan, "dlopen libvulkan"FF_LIBRARY_EXTENSION " failed", "libvulkan"FF_LIBRARY_EXTENSION, 2) + FF_LIBRARY_LOAD(vulkan, &instance->config.libVulkan, "dlopen libvulkan"FF_LIBRARY_EXTENSION " failed", "libvulkan"FF_LIBRARY_EXTENSION, 2, "vulkan-1"FF_LIBRARY_EXTENSION, -1) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkGetInstanceProcAddr) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkCreateInstance) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(vulkan, vkDestroyInstance) diff --git a/src/detection/wmtheme/wmtheme_linux.c b/src/detection/wmtheme/wmtheme_linux.c index 0650d2f4a..19765c79a 100644 --- a/src/detection/wmtheme/wmtheme_linux.c +++ b/src/detection/wmtheme/wmtheme_linux.c @@ -224,6 +224,6 @@ bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) return detectOpenbox(instance, &wm->dePrettyName, themeOrError); ffStrbufAppendS(themeOrError, "Unknown WM: "); - ffStrbufAppend(themeOrError, &wm->dePrettyName); + ffStrbufAppend(themeOrError, &wm->wmPrettyName); return false; } diff --git a/src/detection/wmtheme/wmtheme_android.c b/src/detection/wmtheme/wmtheme_nosupport.c similarity index 71% rename from src/detection/wmtheme/wmtheme_android.c rename to src/detection/wmtheme/wmtheme_nosupport.c index 7d9da9a5d..5dc5691d3 100644 --- a/src/detection/wmtheme/wmtheme_android.c +++ b/src/detection/wmtheme/wmtheme_nosupport.c @@ -5,6 +5,6 @@ bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) { FF_UNUSED(instance); - ffStrbufAppendS(themeOrError, "WM theme detection is not supported on Android"); + ffStrbufAppendS(themeOrError, "Not supported on this platform"); return false; } diff --git a/src/detection/wmtheme/wmtheme_windows.c b/src/detection/wmtheme/wmtheme_windows.c new file mode 100644 index 000000000..7611f05da --- /dev/null +++ b/src/detection/wmtheme/wmtheme_windows.c @@ -0,0 +1,41 @@ +#include "fastfetch.h" +#include "wmtheme.h" + +#define WIN32_LEAN_AND_MEAN 1 +#include + +bool ffDetectWmTheme(FFinstance* instance, FFstrbuf* themeOrError) +{ + FF_UNUSED(instance); + + HKEY hKey; + if(RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey) != ERROR_SUCCESS) + { + ffStrbufAppendS(themeOrError, "RegOpenKeyExW() failed"); + return false; + } + + bool result = true; + int SystemUsesLightTheme = 1; + DWORD bufSize = sizeof(SystemUsesLightTheme); + + if(RegQueryValueExW(hKey, L"SystemUsesLightTheme", NULL, NULL, (LPBYTE)&SystemUsesLightTheme, &bufSize) != ERROR_SUCCESS) + { + ffStrbufAppendS(themeOrError, "RegOpenKeyExW(SystemUsesLightTheme) failed"); + goto exit; + } + + int AppsUsesLightTheme = 1; + bufSize = sizeof(AppsUsesLightTheme); + if(RegQueryValueExW(hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&AppsUsesLightTheme, &bufSize) != ERROR_SUCCESS) + { + ffStrbufAppendS(themeOrError, "RegOpenKeyExW(AppsUseLightTheme) failed"); + goto exit; + } + + ffStrbufAppendF(themeOrError, "System - %s, Apps - %s", SystemUsesLightTheme ? "Light" : "Dark", AppsUsesLightTheme ? "Light" : "Dark"); + +exit: + RegCloseKey(hKey); + return result; +} diff --git a/src/fastfetch.c b/src/fastfetch.c index c20bf5ba3..fff00ea5b 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -71,24 +71,34 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "host-format") == 0) { - constructAndPrintCommandHelpFormat("host", "{2} {3}", 15, + constructAndPrintCommandHelpFormat("host", "{2} {3}", 8, "product family", "product name", "product version", "product sku", - "bios date", - "bios release", - "bios vendor", - "bios version", - "board name", - "board vendor", - "board version", "chassis type", "chassis vendor", "chassis version", "sys vendor" ); } + else if(strcasecmp(command, "bios-format") == 0) + { + constructAndPrintCommandHelpFormat("bios", "{2} {3}", 4, + "bios date", + "bios release", + "bios vendor", + "bios version" + ); + } + else if(strcasecmp(command, "board-format") == 0) + { + constructAndPrintCommandHelpFormat("board", "{2} {3}", 3, + "board name", + "board vendor", + "board version" + ); + } else if(strcasecmp(command, "kernel-format") == 0) { constructAndPrintCommandHelpFormat("kernel", "{2}", 3, @@ -114,7 +124,7 @@ static inline void printCommandHelp(const char* command) } else if(strcasecmp(command, "packages-format") == 0) { - constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (xbps), {8} (nix-system), {9} (nix-user), {10} (nix-default), {11} (apk), {12} (pkg), {13} (flatpak), {14} (snap), {15} (brew), {16} (port)", 16, + constructAndPrintCommandHelpFormat("packages", "{2} (pacman){?3}[{3}]{?}, {4} (dpkg), {5} (rpm), {6} (emerge), {7} (xbps), {8} (nix-system), {9} (nix-user), {10} (nix-default), {11} (apk), {12} (pkg), {13} (flatpak), {14} (snap), {15} (brew), {16} (port), {17} (scoop)", 17, "Number of all packages", "Number of pacman packages", "Pacman branch on manjaro", @@ -131,6 +141,7 @@ static inline void printCommandHelp(const char* command) "Number of snap packages", "Number of brew packages", "Number of macports packages" + "Number of scoop packages" ); } else if(strcasecmp(command, "shell-format") == 0) @@ -978,6 +989,12 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con optionParseString(key, value, &instance->config.host.outputFormat); else if(strcasecmp(key, "--host-error") == 0) optionParseString(key, value, &instance->config.host.errorFormat); + else if(strcasecmp(key, "--bios-key") == 0) + optionParseString(key, value, &instance->config.bios.key); + else if(strcasecmp(key, "--bios-format") == 0) + optionParseString(key, value, &instance->config.bios.outputFormat); + else if(strcasecmp(key, "--bios-error") == 0) + optionParseString(key, value, &instance->config.bios.errorFormat); else if(strcasecmp(key, "--kernel-key") == 0) optionParseString(key, value, &instance->config.kernel.key); else if(strcasecmp(key, "--kernel-format") == 0) @@ -1367,6 +1384,10 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintOS(instance); else if(strcasecmp(line, "host") == 0) ffPrintHost(instance); + else if(strcasecmp(line, "bios") == 0) + ffPrintBios(instance); + else if(strcasecmp(line, "board") == 0) + ffPrintBoard(instance); else if(strcasecmp(line, "kernel") == 0) ffPrintKernel(instance); else if(strcasecmp(line, "uptime") == 0) diff --git a/src/fastfetch.h b/src/fastfetch.h index f466d59e5..372fb221b 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -94,6 +94,8 @@ typedef struct FFconfig FFModuleArgs os; FFModuleArgs host; + FFModuleArgs bios; + FFModuleArgs board; FFModuleArgs kernel; FFModuleArgs uptime; FFModuleArgs processes; @@ -249,6 +251,8 @@ void ffPrintTitle(FFinstance* instance); void ffPrintSeparator(FFinstance* instance); void ffPrintOS(FFinstance* instance); void ffPrintHost(FFinstance* instance); +void ffPrintBios(FFinstance* instance); +void ffPrintBoard(FFinstance* instance); void ffPrintKernel(FFinstance* instance); void ffPrintUptime(FFinstance* instance); void ffPrintProcesses(FFinstance* instance); diff --git a/src/fastfetch_config.h.in b/src/fastfetch_config.h.in index 40677fa6b..171881da9 100644 --- a/src/fastfetch_config.h.in +++ b/src/fastfetch_config.h.in @@ -16,7 +16,7 @@ #define FASTFETCH_TARGET_DIR_INSTALL_SYSCONF "@CMAKE_INSTALL_SYSCONFDIR@" #define FASTFETCH_DATATEXT_STRUCTURE "@DATATEXT_STRUCTURE@" -#define FASTFETCH_DATATEXT_CONFIG_SYSTEM "@DATATEXT_CONFIG_SYSTEM@" //Requires FASTFETCH_PROJECT_VERSION to be set +#define FASTFETCH_DATATEXT_CONFIG_SYSTEM "@DATATEXT_CONFIG_SYSTEM@" #define FASTFETCH_DATATEXT_CONFIG_USER "@DATATEXT_CONFIG_USER@" //Requires FASTFETCH_PROJECT_VERSION and FASTFETCH_DATATEXT_STRUCTURE to be set #define FASTFETCH_DATATEXT_MODULES "@DATATEXT_MODULES@" #define FASTFETCH_DATATEXT_HELP "@DATATEXT_HELP@" diff --git a/src/flashfetch.c b/src/flashfetch.c index e20b2aa1b..dea96af08 100644 --- a/src/flashfetch.c +++ b/src/flashfetch.c @@ -22,6 +22,8 @@ int main(int argc, char** argv) ffPrintSeparator(&instance); ffPrintOS(&instance); ffPrintHost(&instance); + //ffPrintBios(&instance); + //ffPrintBoard(&instance); ffPrintKernel(&instance); ffPrintUptime(&instance); //ffPrintProcesses(&instance); diff --git a/src/logo/builtin.c b/src/logo/builtin.c index 991115c0b..285cfb8c7 100644 --- a/src/logo/builtin.c +++ b/src/logo/builtin.c @@ -1277,6 +1277,102 @@ static const FFlogo* getLogoMsys2() FF_LOGO_RETURN } +static const FFlogo* getLogoWindows11() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows 11") + FF_LOGO_LINES( + "$1\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################\n" + "################ ################" + ) + FF_LOGO_COLORS( + "34" //blue + ) + FF_LOGO_COLOR_KEYS("34"); //blue + FF_LOGO_COLOR_TITLE("36"); //cyan + FF_LOGO_RETURN +} + +static const FFlogo* getLogoWindows8() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows 8", "Windows 8.1", "Windows 10") + FF_LOGO_LINES( + "$1 ..,\n" + " ....,,:;+ccllll\n" + " ...,,+:; cllllllllllllllllll\n" + ",cclllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "llllllllllllll lllllllllllllllllll\n" + "`'ccllllllllll lllllllllllllllllll\n" + " `' \\*:: :ccllllllllllllllll\n" + " ````''*::cll\n" + " ``" + ) + FF_LOGO_COLORS( + "36" //cyan + ) + FF_LOGO_COLOR_KEYS("36"); //cyan + FF_LOGO_COLOR_TITLE("37"); //white + FF_LOGO_RETURN +} + +static const FFlogo* getLogoWindows() +{ + FF_LOGO_INIT + FF_LOGO_NAMES("Windows") + FF_LOGO_LINES( + "$1 ,.=:!!t3Z3z.,\n" + " :tt:::tt333EE3\n" + "$1 Et:::ztt33EEEL$2 @Ee., ..,\n" + "$1 ;tt:::tt333EE7$2 ;EEEEEEttttt33#\n" + "$1 :Et:::zt333EEQ.$2 $EEEEEttttt33QL\n" + "$1 it::::tt333EEF$2 @EEEEEEttttt33F\n" + "$1 ;3=*^```\"*4EEV$2 :EEEEEEttttt33@.\n" + "$3 ,.=::::!t=., $1`$2 @EEEEEEtttz33QF\n" + "$3 ;::::::::zt33)$2 \"4EEEtttji3P*\n" + "$3 :t::::::::tt33.$4:Z3z..$2 ``$4 ,..g.\n" + "$3 i::::::::zt33F$4 AEEEtttt::::ztF\n" + "$3 ;:::::::::t33V$4 ;EEEttttt::::t3\n" + "$3 E::::::::zt33L$4 @EEEtttt::::z3F\n" + "$3{3=*^```\"*4E3)$4 ;EEEtttt:::::tZ`\n" + "$3 `$4 :EEEEtttt::::z7\n" + " \"VEzjt:;;z>*`" + ) + FF_LOGO_COLORS( + "31", //red + "32", //green + "34", //blue + "33" //yellow + ) + FF_LOGO_COLOR_KEYS("34"); //blue + FF_LOGO_COLOR_TITLE("32"); //green + FF_LOGO_RETURN +} + static const FFlogo* getLogoNixOS() { FF_LOGO_INIT @@ -2016,6 +2112,9 @@ GetLogoMethod* ffLogoBuiltinGetAll() getLogoMintSmall, getLogoMintOld, getLogoMsys2, + getLogoWindows11, + getLogoWindows8, + getLogoWindows, getLogoNixOS, getLogoNixOsOld, getLogoNixOsSmall, diff --git a/src/logo/logo.c b/src/logo/logo.c index e0340d571..93667ddd3 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -2,7 +2,7 @@ #include "common/io.h" #include "common/printing.h" #include "detection/os/os.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include #include diff --git a/src/modules/bios.c b/src/modules/bios.c new file mode 100644 index 000000000..3e443a389 --- /dev/null +++ b/src/modules/bios.c @@ -0,0 +1,42 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "common/caching.h" +#include "detection/bios/bios.h" + +#define FF_BIOS_MODULE_NAME "Bios" +#define FF_BIOS_NUM_FORMAT_ARGS 4 + +void ffPrintBios(FFinstance* instance) +{ + if(ffPrintFromCache(instance, FF_BIOS_MODULE_NAME, &instance->config.bios, FF_BIOS_NUM_FORMAT_ARGS)) + return; + + FFBiosResult result; + ffDetectBios(&result); + + if(result.error.length > 0) + { + ffPrintError(instance, FF_BIOS_MODULE_NAME, 0, &instance->config.bios, "%*s", result.error.length, result.error.chars); + goto exit; + } + + if(result.biosRelease.length == 0) + { + ffPrintError(instance, FF_BIOS_MODULE_NAME, 0, &instance->config.bios, "bios_release is not set."); + goto exit; + } + + ffPrintAndWriteToCache(instance, FF_BIOS_MODULE_NAME, &instance->config.bios, &result.biosRelease, FF_BIOS_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosDate}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosRelease}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.biosVersion}, + }); + +exit: + ffStrbufDestroy(&result.biosDate); + ffStrbufDestroy(&result.biosRelease); + ffStrbufDestroy(&result.biosVendor); + ffStrbufDestroy(&result.biosVersion); + ffStrbufDestroy(&result.error); +} diff --git a/src/modules/board.c b/src/modules/board.c new file mode 100644 index 000000000..8fe85735d --- /dev/null +++ b/src/modules/board.c @@ -0,0 +1,40 @@ +#include "fastfetch.h" +#include "common/printing.h" +#include "common/caching.h" +#include "detection/board/board.h" + +#define FF_BOARD_MODULE_NAME "Board" +#define FF_BOARD_NUM_FORMAT_ARGS 3 + +void ffPrintBoard(FFinstance* instance) +{ + if(ffPrintFromCache(instance, FF_BOARD_MODULE_NAME, &instance->config.board, FF_BOARD_NUM_FORMAT_ARGS)) + return; + + FFBoardResult result; + ffDetectBoard(&result); + + if(result.error.length > 0) + { + ffPrintError(instance, FF_BOARD_MODULE_NAME, 0, &instance->config.board, "%*s", result.error.length, result.error.chars); + goto exit; + } + + if(result.boardName.length == 0) + { + ffPrintError(instance, FF_BOARD_MODULE_NAME, 0, &instance->config.board, "board_name is not set."); + goto exit; + } + + ffPrintAndWriteToCache(instance, FF_BOARD_MODULE_NAME, &instance->config.board, &result.boardName, FF_BOARD_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &result.boardName}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.boardVendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.boardVersion}, + }); + +exit: + ffStrbufDestroy(&result.boardName); + ffStrbufDestroy(&result.boardVendor); + ffStrbufDestroy(&result.boardVersion); + ffStrbufDestroy(&result.error); +} diff --git a/src/modules/cpuUsage.c b/src/modules/cpuUsage.c index a41735c82..db8ea2e5d 100644 --- a/src/modules/cpuUsage.c +++ b/src/modules/cpuUsage.c @@ -1,56 +1,21 @@ #include "fastfetch.h" #include "common/printing.h" #include "detection/cpuUsage/cpuUsage.h" -#include -#include #define FF_CPU_USAGE_MODULE_NAME "CPU Usage" #define FF_CPU_USAGE_NUM_FORMAT_ARGS 1 -time_t getTimeInMs() -{ - struct timeval timeNow; - gettimeofday(&timeNow, NULL); - return (timeNow.tv_sec * 1000) + (timeNow.tv_usec / 1000); -} - -static long inUseAll1, totalAll1, startTime; - -void ffPrepareCPUUsage() -{ - startTime = getTimeInMs(); - ffGetCpuUsageInfo(&inUseAll1, &totalAll1); -} - void ffPrintCPUUsage(FFinstance* instance) { - const char* error = NULL; - if(startTime == 0) - { - error = ffGetCpuUsageInfo(&inUseAll1, &totalAll1); - if(error) - goto error; - nanosleep(&(struct timespec){ 1, 0 }, NULL); - } - else - { - time_t duration = getTimeInMs() - startTime; - if(duration < 1000) - nanosleep(&(struct timespec){ 0, (1000 - duration) * 1000000L }, NULL); - } - - long inUseAll2, totalAll2; - error = ffGetCpuUsageInfo(&inUseAll2, &totalAll2); + double cpuPercent = 0.0/0.0; + const char* error = ffGetCpuUsageResult(&cpuPercent); if(error) { - error: ffPrintError(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpu, "%s", error); return; } - double cpuPercent = (double)(inUseAll2 - inUseAll1) / (double)(totalAll2 - totalAll1) * 100; - if(instance->config.cpuUsage.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_CPU_USAGE_MODULE_NAME, 0, &instance->config.cpuUsage.key); diff --git a/src/modules/cursor.c b/src/modules/cursor.c index c0f572823..e6d50d333 100644 --- a/src/modules/cursor.c +++ b/src/modules/cursor.c @@ -11,7 +11,7 @@ #define FF_CURSOR_MODULE_NAME "Cursor" #define FF_CURSOR_NUM_FORMAT_ARGS 2 -#if !(defined(__ANDROID__) || defined(__APPLE__)) +#if !(defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) || defined(__MSYS__)) static void printCursor(FFinstance* instance, FFstrbuf* cursorTheme, const FFstrbuf* cursorSize) { @@ -196,7 +196,7 @@ static bool printCursorFromEnv(FFinstance* instance) void ffPrintCursor(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) + #if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) || defined(__MSYS__) FF_UNUSED(instance); ffPrintError(instance, FF_CURSOR_MODULE_NAME, 0, &instance->config.cursor, "Cursor detection is not supported"); diff --git a/src/modules/disk.c b/src/modules/disk.c index ea92f7e89..8f97f54ba 100644 --- a/src/modules/disk.c +++ b/src/modules/disk.c @@ -8,111 +8,76 @@ #define FF_DISK_MODULE_NAME "Disk" #define FF_DISK_NUM_FORMAT_ARGS 4 -static void createKey(FFinstance* instance, const char* folderPath, FFstrbuf* key) +static void printFolder(FFinstance* instance, FFDiskResult* folder) { + FFstrbuf key; + ffStrbufInit(&key); + if(instance->config.disk.key.length == 0) { - ffStrbufAppendS(key, FF_DISK_MODULE_NAME); - if(folderPath != NULL) - ffStrbufAppendF(key, " (%s)", folderPath); + ffStrbufAppendF(&key, "%s (%*s)", FF_DISK_MODULE_NAME, folder->path.length, folder->path.chars); } else { - ffParseFormatString(key, &instance->config.disk.key, 1, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, folderPath} + ffParseFormatString(&key, &instance->config.disk.key, 1, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &folder->path} }); } -} -static void printStatvfs(FFinstance* instance, const FFstrbuf* key, const char* folderPath, struct statvfs* fs) -{ - uint64_t total = fs->f_blocks * fs->f_frsize; - - if(total == 0) - { - ffPrintErrorString(instance, key->chars, 0, NULL, &instance->config.disk.errorFormat, "statvfs for %s returned size 0", folderPath); - return; - } - - uint64_t used = total - (fs->f_bavail * fs->f_frsize); - uint32_t files = (uint32_t) (fs->f_files - fs->f_ffree); - uint8_t percentage = (uint8_t) ((used / (long double) total) * 100.0); + uint8_t percentage = (uint8_t) ((folder->used / (long double) folder->total) * 100.0); FFstrbuf usedPretty; ffStrbufInit(&usedPretty); - ffParseSize(used, instance->config.binaryPrefixType, &usedPretty); + ffParseSize(folder->used, instance->config.binaryPrefixType, &usedPretty); FFstrbuf totalPretty; ffStrbufInit(&totalPretty); - ffParseSize(total, instance->config.binaryPrefixType, &totalPretty); + ffParseSize(folder->total, instance->config.binaryPrefixType, &totalPretty); if(instance->config.disk.outputFormat.length == 0) { - ffPrintLogoAndKey(instance, key->chars, 0, NULL); - printf("%s / %s (%u%%)\n", usedPretty.chars, totalPretty.chars, percentage); + ffPrintLogoAndKey(instance, key.chars, 0, NULL); + printf("%s / %s (%u%%)%s\n", usedPretty.chars, totalPretty.chars, percentage, folder->removable ? " [Removable]" : ""); } else { - ffPrintFormatString(instance, key->chars, 0, NULL, &instance->config.disk.outputFormat, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){ + ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.disk.outputFormat, FF_DISK_NUM_FORMAT_ARGS, (FFformatarg[]){ {FF_FORMAT_ARG_TYPE_STRBUF, &usedPretty}, {FF_FORMAT_ARG_TYPE_STRBUF, &totalPretty}, {FF_FORMAT_ARG_TYPE_UINT8, &percentage}, - {FF_FORMAT_ARG_TYPE_UINT, &files}, + {FF_FORMAT_ARG_TYPE_UINT, &folder->files}, }); } + ffStrbufDestroy(&key); ffStrbufDestroy(&totalPretty); ffStrbufDestroy(&usedPretty); } -static void printFolder(FFinstance* instance, const char* folderPath) -{ - FFstrbuf key; - ffStrbufInit(&key); - createKey(instance, folderPath, &key); - - struct statvfs fs; - int ret = statvfs(folderPath, &fs); - if(ret != 0) - { - ffPrintErrorString(instance, key.chars, 0, NULL, &instance->config.disk.errorFormat, "statvfs(\"%s\", &fs) != 0 (%i)", folderPath, ret); - ffStrbufDestroy(&key); - return; - } - - printStatvfs(instance, &key, folderPath, &fs); - ffStrbufDestroy(&key); -} - void ffPrintDisk(FFinstance* instance) { - ffStrbufTrim(&instance->config.diskFolders, ':'); + FFlist folders; + ffListInit(&folders, sizeof(FFDiskResult)); - if(instance->config.diskFolders.length == 0) + const char* error = NULL; + + if(!ffDiskDetectDiskFolders(instance, &folders)) + error = ffDiskAutodetectFolders(instance, &folders); + + if(error) + { + ffPrintError(instance, FF_DISK_MODULE_NAME, 0, &instance->config.disk, "%s", error); + } + else { - FFlist folders; - ffListInit(&folders, sizeof(FFstrbuf)); - const char* error = ffDiskAutodetectFolders(instance, &folders); - if(error) - ffPrintError(instance, FF_DISK_MODULE_NAME, 0, &instance->config.disk, "%s", error); for(uint32_t i = 0; i < folders.length; ++i) { - FFstrbuf* folder = (FFstrbuf*)ffListGet(&folders, i); - printFolder(instance, folder->chars); - ffStrbufDestroy(folder); + FFDiskResult* folder = (FFDiskResult*)ffListGet(&folders, i); + printFolder(instance, folder); + ffStrbufDestroy(&folder->path); + ffStrbufDestroy(&folder->error); } - ffListDestroy(&folders); - return; } - uint32_t startIndex = 0; - while(startIndex < instance->config.diskFolders.length) - { - uint32_t colonIndex = ffStrbufNextIndexC(&instance->config.diskFolders, startIndex, ':'); - instance->config.diskFolders.chars[colonIndex] = '\0'; - - printFolder(instance, instance->config.diskFolders.chars + startIndex); - - startIndex = colonIndex + 1; - } + ffListDestroy(&folders); } diff --git a/src/modules/font.c b/src/modules/font.c index bcf8fd14d..176243adf 100644 --- a/src/modules/font.c +++ b/src/modules/font.c @@ -40,6 +40,16 @@ static void printFont(const FFFontResult* font) printf("%s [User]", font->fonts[1].chars); } +#elif defined(_WIN32) || defined(__MSYS__) + +static void printFont(const FFFontResult* font) +{ + if(font->fonts[0].length > 0) + { + printf("%s [Desktop]", font->fonts[0].chars); + } +} + #else static void printFont(const FFFontResult* font) diff --git a/src/modules/gpu.c b/src/modules/gpu.c index c98ca3af8..11507191a 100644 --- a/src/modules/gpu.c +++ b/src/modules/gpu.c @@ -14,7 +14,7 @@ static void printGPUResult(FFinstance* instance, uint8_t index, FFcache* cache, FFstrbuf output; ffStrbufInitA(&output, gpu->vendor.length + 1 + gpu->name.length); - if(gpu->vendor.length > 0) + if(gpu->vendor.length > 0 && !ffStrbufStartsWith(&gpu->name, &gpu->vendor)) { ffStrbufAppend(&output, &gpu->vendor); ffStrbufAppendC(&output, ' '); diff --git a/src/modules/host.c b/src/modules/host.c index 1a5b8dae6..8c3ec65d7 100644 --- a/src/modules/host.c +++ b/src/modules/host.c @@ -4,7 +4,7 @@ #include "detection/host/host.h" #define FF_HOST_MODULE_NAME "Host" -#define FF_HOST_NUM_FORMAT_ARGS 15 +#define FF_HOST_NUM_FORMAT_ARGS 8 void ffPrintHost(FFinstance* instance) { @@ -13,6 +13,12 @@ void ffPrintHost(FFinstance* instance) const FFHostResult* host = ffDetectHost(); + if(host->error.length > 0) + { + ffPrintError(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host, "%*s", host->error.length, host->error.chars); + return; + } + if(host->productFamily.length == 0 && host->productName.length == 0) { ffPrintError(instance, FF_HOST_MODULE_NAME, 0, &instance->config.host, "neither product_family nor product_name is set by O.E.M."); @@ -38,13 +44,6 @@ void ffPrintHost(FFinstance* instance) {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->biosDate}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosRelease}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosVendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->biosVersion}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardName}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardVendor}, - {FF_FORMAT_ARG_TYPE_STRBUF, &host->boardVersion}, {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisType}, {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisVendor}, {FF_FORMAT_ARG_TYPE_STRBUF, &host->chassisVersion}, diff --git a/src/modules/icons.c b/src/modules/icons.c index 38d0823fb..c489b9a86 100644 --- a/src/modules/icons.c +++ b/src/modules/icons.c @@ -10,7 +10,7 @@ void ffPrintIcons(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) + #if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) || defined(__MSYS__) FF_UNUSED(instance); ffPrintError(instance, FF_ICONS_MODULE_NAME, 0, &instance->config.icons, "Icons detection is not supported"); diff --git a/src/modules/kernel.c b/src/modules/kernel.c index 062b04f42..02cece513 100644 --- a/src/modules/kernel.c +++ b/src/modules/kernel.c @@ -1,22 +1,38 @@ #include "fastfetch.h" #include "common/printing.h" +#include "detection/kernel/kernel.h" #define FF_KERNEL_MODULE_NAME "Kernel" #define FF_KERNEL_NUM_FORMAT_ARGS 3 void ffPrintKernel(FFinstance* instance) { + FFKernelResult result; + ffDetectKernel(instance, &result); + + if(result.error.length > 0) + { + ffPrintError(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernel, "%*s", result.error.length, result.error.chars); + goto exit; + } + if(instance->config.kernel.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernel.key); - puts(instance->state.utsname.release); + ffStrbufPutTo(&result.release, stdout); } else { ffPrintFormat(instance, FF_KERNEL_MODULE_NAME, 0, &instance->config.kernel, FF_KERNEL_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.sysname}, - {FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.release}, - {FF_FORMAT_ARG_TYPE_STRING, instance->state.utsname.version} + {FF_FORMAT_ARG_TYPE_STRBUF, &result.sysname}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.release}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.version} }); } + +exit: + ffStrbufDestroy(&result.error); + ffStrbufDestroy(&result.sysname); + ffStrbufDestroy(&result.release); + ffStrbufDestroy(&result.version); } diff --git a/src/modules/locale.c b/src/modules/locale.c index 56ec93831..c5dd9e1a4 100644 --- a/src/modules/locale.c +++ b/src/modules/locale.c @@ -2,7 +2,6 @@ #include "common/properties.h" #include "common/printing.h" #include "common/caching.h" -#include "common/processing.h" #include "common/parsing.h" #include diff --git a/src/modules/localip.c b/src/modules/localip.c index 9308d246a..07508f3bd 100644 --- a/src/modules/localip.c +++ b/src/modules/localip.c @@ -1,90 +1,66 @@ #include "fastfetch.h" #include "common/printing.h" +#include "detection/localip/localip.h" #define FF_LOCALIP_MODULE_NAME "Local IP" -#define FF_LOCALIP_NUM_FORMAT_ARGS 1 - -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ - #include // FreeBSD needs this for AF_INET -#endif - -static void printValue(FFinstance* instance, const char* ifaName, const char* addressBuffer) -{ - FFstrbuf key; - ffStrbufInit(&key); - - if (instance->config.localIP.key.length == 0) - { - ffStrbufAppendF(&key, FF_LOCALIP_MODULE_NAME " (%s)", ifaName); - } - else - { - ffParseFormatString(&key, &instance->config.localIP.key, 1, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, ifaName} - }); - } - - if (instance->config.localIP.outputFormat.length == 0) - { - ffPrintLogoAndKey(instance, key.chars, 0, NULL); - puts(addressBuffer); - } - else - { - ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.localIP.outputFormat, FF_LOCALIP_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_STRING, addressBuffer} - }); - } - - ffStrbufDestroy(&key); -} +#define FF_LOCALIP_NUM_FORMAT_ARGS 2 void ffPrintLocalIp(FFinstance* instance) { - struct ifaddrs* ifAddrStruct = NULL; - int ret = getifaddrs(&ifAddrStruct); - if (ret < 0) { - ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIP, "getifaddrs(&ifAddrStruct) < 0 (%i)", ret); - return; + FFlist results; + ffListInit(&results, sizeof(FFLocalIpResult)); + + const char* error = ffDetectLocalIps(instance, &results); + + if(error) + { + ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIP, "%s", error); + goto exit; } - for (struct ifaddrs* ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr) - continue; + if(results.length == 0) + { + ffPrintError(instance, FF_LOCALIP_MODULE_NAME, 0, &instance->config.localIP, "Failed to detect any IPs"); + goto exit; + } - // loop back - if (strncmp(ifa->ifa_name, "lo", 2) == 0 && (ifa->ifa_name[2] == '\0' || isdigit(ifa->ifa_name[2])) && !instance->config.localIpShowLoop) - continue; + FFstrbuf key; + ffStrbufInit(&key); - if (instance->config.localIpNamePrefix.length && strncmp(ifa->ifa_name, instance->config.localIpNamePrefix.chars, instance->config.localIpNamePrefix.length) != 0) - continue; + for(uint32_t i = 0; i < results.length; ++i) + { + FFLocalIpResult* ip = (FFLocalIpResult*) ffListGet(&results, i); - if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 - // is a valid IP4 Address - if (!instance->config.localIpShowIpV4) - continue; - - void* tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; - char addressBuffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); - printValue(instance, ifa->ifa_name, addressBuffer); - } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 - // is a valid IP6 Address - if (!instance->config.localIpShowIpV6) - continue; - - void* tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; - char addressBuffer[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); - printValue(instance, ifa->ifa_name, addressBuffer); + if(instance->config.localIP.key.length == 0) + { + ffStrbufSetF(&key, FF_LOCALIP_MODULE_NAME " (%*s)", ip->name.length, ip->name.chars); } + else + { + ffParseFormatString(&key, &instance->config.localIP.key, 1, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &ip->name} + }); + } + + if(instance->config.localIP.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, key.chars, 0, NULL); + ffStrbufPutTo(&ip->addr, stdout); + } + else + { + ffPrintFormatString(instance, key.chars, 0, NULL, &instance->config.localIP.outputFormat, FF_LOCALIP_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRBUF, &ip->addr}, + {FF_FORMAT_ARG_TYPE_STRING, ip->ipv6 ? "IPv6" : "IPv4"} + }); + } + + ffStrbufDestroy(&ip->name); + ffStrbufDestroy(&ip->addr); } - if (ifAddrStruct) freeifaddrs(ifAddrStruct); + ffStrbufDestroy(&key); + +exit: + ffListDestroy(&results); } diff --git a/src/modules/opencl.c b/src/modules/opencl.c index 7f2ce1acb..debfcd45e 100644 --- a/src/modules/opencl.c +++ b/src/modules/opencl.c @@ -83,7 +83,7 @@ static const char* printOpenCL(FFinstance* instance) { OpenCLData data; - FF_LIBRARY_LOAD(opencl, &instance->config.libOpenCL, "dlopen libOpenCL.so failed", "libOpenCL.so", 1); + FF_LIBRARY_LOAD(opencl, &instance->config.libOpenCL, "dlopen libOpenCL"FF_LIBRARY_EXTENSION" failed", "libOpenCL"FF_LIBRARY_EXTENSION, 1); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opencl, data, clGetPlatformIDs); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opencl, data, clGetDeviceIDs); FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(opencl, data, clGetDeviceInfo); diff --git a/src/modules/opengl.c b/src/modules/opengl.c index 78d0b4846..61e78451b 100644 --- a/src/modules/opengl.c +++ b/src/modules/opengl.c @@ -1,457 +1,42 @@ #include "fastfetch.h" #include "common/printing.h" -#include "common/parsing.h" - -#include +#include "detection/opengl/opengl.h" #define FF_OPENGL_MODULE_NAME "OpenGL" #define FF_OPENGL_NUM_FORMAT_ARGS 4 -#if defined(FF_HAVE_EGL) || defined(FF_HAVE_GLX) || defined(FF_HAVE_OSMESA) || defined(__APPLE__) -#define FF_HAVE_GL 1 - -#include "common/library.h" - -#ifdef __APPLE__ - #define GL_SILENCE_DEPRECATION - #include -#else - #include -#endif - -#define FF_OPENGL_BUFFER_WIDTH 1 -#define FF_OPENGL_BUFFER_HEIGHT 1 - -typedef struct GLData +void ffPrintOpenGL(FFinstance* instance) { - FF_LIBRARY_SYMBOL(glGetString) -} GLData; + FFOpenGLResult result; + ffStrbufInit(&result.version); + ffStrbufInit(&result.renderer); + ffStrbufInit(&result.vendor); + ffStrbufInit(&result.slv); -static const char* glHandlePrint(FFinstance* instance, const GLData* data) -{ - const char* version = (const char*) data->ffglGetString(GL_VERSION); - if(!ffStrSet(version)) - return "glGetString(GL_VERSION) returned NULL"; - - const char* renderer = (const char*) data->ffglGetString(GL_RENDERER); - const char* vendor = (const char*) data->ffglGetString(GL_VENDOR); - const char* slv = (const char*) data->ffglGetString(GL_SHADING_LANGUAGE_VERSION); + const char* error = ffDetectOpenGL(instance, &result); + if(error) + { + ffPrintError(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL, "%s", error); + return; + } if(instance->config.openGL.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL.key); - puts(version); + puts(result.version.chars); } else { ffPrintFormat(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL, FF_OPENGL_NUM_FORMAT_ARGS, (FFformatarg[]) { - {FF_FORMAT_ARG_TYPE_STRING, version}, - {FF_FORMAT_ARG_TYPE_STRING, renderer}, - {FF_FORMAT_ARG_TYPE_STRING, vendor}, - {FF_FORMAT_ARG_TYPE_STRING, slv} + {FF_FORMAT_ARG_TYPE_STRBUF, &result.version}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.renderer}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.vendor}, + {FF_FORMAT_ARG_TYPE_STRBUF, &result.slv} }); } - return NULL; -} - -#endif // FF_HAVE_GL - -#ifdef FF_HAVE_EGL -#include - -typedef struct EGLData -{ - GLData glData; - - FF_LIBRARY_SYMBOL(eglGetProcAddress) - FF_LIBRARY_SYMBOL(eglGetDisplay) - FF_LIBRARY_SYMBOL(eglInitialize) - FF_LIBRARY_SYMBOL(eglBindAPI) - FF_LIBRARY_SYMBOL(eglGetConfigs) - FF_LIBRARY_SYMBOL(eglCreatePbufferSurface) - FF_LIBRARY_SYMBOL(eglCreateContext) - FF_LIBRARY_SYMBOL(eglMakeCurrent) - FF_LIBRARY_SYMBOL(eglDestroyContext) - FF_LIBRARY_SYMBOL(eglDestroySurface) - FF_LIBRARY_SYMBOL(eglTerminate) - - EGLDisplay display; - EGLConfig config; - EGLSurface surface; - EGLContext context; -} EGLData; - -static const char* eglHandleContext(FFinstance* instance, EGLData* data) -{ - if(data->ffeglMakeCurrent(data->display, data->surface, data->surface, data->context) != EGL_TRUE) - return "eglMakeCurrent returned EGL_FALSE"; - - return glHandlePrint(instance, &data->glData); -} - -static const char* eglHandleSurface(FFinstance* instance, EGLData* data) -{ - data->context = data->ffeglCreateContext(data->display, data->config, EGL_NO_CONTEXT, (EGLint[]){EGL_NONE}); - if(data->context == EGL_NO_CONTEXT) - return "eglCreateContext returned EGL_NO_CONTEXT"; - - const char* error = eglHandleContext(instance, data); - data->ffeglDestroyContext(data->display, data->context); - return error; -} - -static const char* eglHandleDisplay(FFinstance* instance, EGLData* data) -{ - if(data->ffeglBindAPI(EGL_OPENGL_API) != EGL_TRUE) - return "eglBindAPI returned EGL_FALSE"; - - EGLint eglConfigCount; - data->ffeglGetConfigs(data->display, &data->config, 1, &eglConfigCount); - if(eglConfigCount == 0) - return "eglGetConfigs returned 0 configs"; - - data->surface = data->ffeglCreatePbufferSurface(data->display, data->config, (EGLint[]){ - EGL_WIDTH, FF_OPENGL_BUFFER_WIDTH, - EGL_HEIGHT, FF_OPENGL_BUFFER_HEIGHT, - EGL_NONE - }); - - if(data->surface == EGL_NO_SURFACE) - return "eglCreatePbufferSurface returned EGL_NO_SURFACE"; - - const char* error = eglHandleSurface(instance, data); - data->ffeglDestroySurface(data->display, data->surface); - return error; -} - -static const char* eglHandleData(FFinstance* instance, EGLData* data) -{ - data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) data->ffeglGetProcAddress("glGetString"); - if(!data->glData.ffglGetString) - return "eglGetProcAddress(glGetString) returned NULL"; - - data->display = data->ffeglGetDisplay(EGL_DEFAULT_DISPLAY); - if(data->display == EGL_NO_DISPLAY) - return "eglGetDisplay returned EGL_NO_DISPLAY"; - - EGLint major, minor; - if(data->ffeglInitialize(data->display, &major, &minor) == EGL_FALSE) - return "eglInitialize returned EGL_FALSE"; - - const char* error = eglHandleDisplay(instance, data); - data->ffeglTerminate(data->display); - return error; -} - -static const char* eglPrint(FFinstance* instance) -{ - EGLData eglData; - - FF_LIBRARY_LOAD(egl, &instance->config.libEGL, "dlopen egl failed", "libEGL.so", 1); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetProcAddress); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetDisplay); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglInitialize); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglBindAPI); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglGetConfigs); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreatePbufferSurface); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglCreateContext); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglMakeCurrent); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroyContext); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglDestroySurface); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(egl, eglData, eglTerminate); - - const char* error = eglHandleData(instance, &eglData); - dlclose(egl); - return error; -} - -#endif //FF_HAVE_EGL - -#ifdef FF_HAVE_GLX -#include - -typedef struct GLXData -{ - GLData glData; - - FF_LIBRARY_SYMBOL(glXGetProcAddress) - FF_LIBRARY_SYMBOL(XOpenDisplay) - FF_LIBRARY_SYMBOL(glXChooseVisual) - FF_LIBRARY_SYMBOL(XCreatePixmap); - FF_LIBRARY_SYMBOL(glXCreateGLXPixmap) - FF_LIBRARY_SYMBOL(glXCreateContext) - FF_LIBRARY_SYMBOL(glXMakeCurrent) - FF_LIBRARY_SYMBOL(glXDestroyContext) - FF_LIBRARY_SYMBOL(glXDestroyGLXPixmap) - FF_LIBRARY_SYMBOL(XFreePixmap) - FF_LIBRARY_SYMBOL(XCloseDisplay) - - Display* display; - XVisualInfo* visualInfo; - Pixmap pixmap; - GLXPixmap glxPixmap; - GLXContext context; -} GLXData; - -static const char* glxHandleContext(FFinstance* instance, GLXData* data) -{ - if(data->ffglXMakeCurrent(data->display, data->glxPixmap, data->context) != True) - return "glXMakeCurrent returned False"; - - return glHandlePrint(instance, &data->glData); -} - -static const char* glxHandleGLXPixmap(FFinstance* instance, GLXData* data) -{ - data->context = data->ffglXCreateContext(data->display, data->visualInfo, NULL, True); - if(data->context == NULL) - return "glXCreateContext returned NULL"; - - const char* error = glxHandleContext(instance, data); - data->ffglXDestroyContext(data->display, data->context); - return error; -} - -static const char* glxHandlePixmap(FFinstance* instance, GLXData* data) -{ - data->glxPixmap = data->ffglXCreateGLXPixmap(data->display, data->visualInfo, data->pixmap); - if(data->glxPixmap == None) - return "glXCreateGLXPixmap returned None"; - - const char* error = glxHandleGLXPixmap(instance, data); - data->ffglXDestroyGLXPixmap(data->display, data->glxPixmap); - return error; -} - -static const char* glxHandleVisualInfo(FFinstance* instance, GLXData* data) -{ - data->pixmap = data->ffXCreatePixmap(data->display, DefaultRootWindow(data->display), FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT, (unsigned int) data->visualInfo->depth); - if(data->pixmap == None) - return "XCreatePixmap returned None"; - - const char* error = glxHandlePixmap(instance, data); - data->ffXFreePixmap(data->display, data->pixmap); - return error; -} - -static const char* glxHandleDisplay(FFinstance* instance, GLXData* data) -{ - data->visualInfo = data->ffglXChooseVisual(data->display, DefaultScreen(data->display), (int[]){None}); - if(data->visualInfo == NULL) - return "glXChooseVisual returned NULL"; - - return glxHandleVisualInfo(instance, data); -} - -static const char* glxHandleData(FFinstance* instance, GLXData* data) -{ - data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) data->ffglXGetProcAddress((const GLubyte*) "glGetString"); - if(data->glData.ffglGetString == NULL) - return "glXGetProcAddress(glGetString) returned NULL"; - - data->display = data->ffXOpenDisplay(NULL); - if(data->display == NULL) - return "XOpenDisplay returned NULL"; - - const char* error = glxHandleDisplay(instance, data); - data->ffXCloseDisplay(data->display); - return error; -} - -static const char* glxPrint(FFinstance* instance) -{ - GLXData data; - - FF_LIBRARY_LOAD(glx, &instance->config.libGLX, "dlopen glx failed", "libGLX.so", 1); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXGetProcAddress); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XOpenDisplay); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXChooseVisual); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCreatePixmap); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateGLXPixmap); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXCreateContext); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXMakeCurrent); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyContext); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, glXDestroyGLXPixmap); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XFreePixmap); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(glx, data, XCloseDisplay); - - const char* error = glxHandleData(instance, &data); - dlclose(glx); - return error; -} - -#endif //FF_HAVE_GLX - -#ifdef FF_HAVE_OSMESA -#include - -typedef struct OSMesaData -{ - GLData glData; - - FF_LIBRARY_SYMBOL(OSMesaGetProcAddress) - FF_LIBRARY_SYMBOL(OSMesaCreateContext) - FF_LIBRARY_SYMBOL(OSMesaMakeCurrent) - FF_LIBRARY_SYMBOL(OSMesaDestroyContext) - - OSMesaContext context; -} OSMesaData; - -static const char* osMesaHandleContext(FFinstance* instance, OSMesaData* data) -{ - unsigned char buffer[FF_OPENGL_BUFFER_WIDTH * FF_OPENGL_BUFFER_HEIGHT * sizeof(uint32_t)]; // 4 bytes per pixel (RGBA) - - if(data->ffOSMesaMakeCurrent(data->context, buffer, GL_UNSIGNED_BYTE, FF_OPENGL_BUFFER_WIDTH, FF_OPENGL_BUFFER_HEIGHT) != GL_TRUE) - return "OSMesaMakeCurrent returned GL_FALSE"; - - return glHandlePrint(instance, &data->glData); -} - -static const char* osMesaHandleData(FFinstance* instance, OSMesaData* data) -{ - //The case to void* is required here, because OSMESAproc can't be cast to (__typeof__(data->glData.ffglGetString)) without a warning, even though it is the actual type. - data->glData.ffglGetString = (__typeof__(data->glData.ffglGetString)) (void*) data->ffOSMesaGetProcAddress("glGetString"); - if(data->glData.ffglGetString == NULL) - return "OSMesaGetProcAddress(glGetString) returned NULL"; - - data->context = data->ffOSMesaCreateContext(OSMESA_RGBA, NULL); - if(data->context == NULL) - return "OSMesaCreateContext returned NULL"; - - const char* error = osMesaHandleContext(instance, data); - data->ffOSMesaDestroyContext(data->context); - return error; -} - -static const char* osMesaPrint(FFinstance* instance) -{ - OSMesaData data; - - FF_LIBRARY_LOAD(osmesa, &instance->config.libOSMesa, "dlopen osmesa failed", "libOSMesa.so", 8); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaGetProcAddress); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaCreateContext); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaMakeCurrent); - FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(osmesa, data, OSMesaDestroyContext); - - const char* error = osMesaHandleData(instance, &data); - dlclose(osmesa); - return error; -} - -#endif //FF_HAVE_OSMESA - -#ifdef __APPLE__ -#include // This brings in CGL, not GL - -typedef struct CGLData -{ - CGLPixelFormatObj pixelFormat; - CGLContextObj context; -} CGLData; - -static const char* cglHandleContext(FFinstance* instance, CGLData* data) -{ - if(CGLSetCurrentContext(data->context) != kCGLNoError) - return "CGLSetCurrentContext() failed"; - - GLData glData; - glData.ffglGetString = glGetString; - - return glHandlePrint(instance, &glData); -} - -static const char* cglHandlePixelFormat(FFinstance* instance, CGLData* data) -{ - if(CGLCreateContext(data->pixelFormat, NULL, &data->context) != kCGLNoError) - return "CGLCreateContext() failed"; - - const char* error = cglHandleContext(instance, data); - CGLDestroyContext(data->context); - return error; -} - -static const char* cglPrint(FFinstance* instance) -{ - CGLData data; - - CGLPixelFormatAttribute attrs[] = { - kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core, - kCGLPFAAccelerated, - 0 - }; - - GLint num; - if (CGLChoosePixelFormat(attrs, &data.pixelFormat, &num) != kCGLNoError) - return "CGLChoosePixelFormat() failed"; - - const char* error = cglHandlePixelFormat(instance, &data); - CGLDestroyPixelFormat(data.pixelFormat); - return error; -} - -#elif FF_HAVE_GL - -static const char* glPrint(FFinstance* instance) -{ - if(instance->config.glType == FF_GL_TYPE_GLX) - { - #ifdef FF_HAVE_GLX - return glxPrint(instance); - #else - return "fastfetch was compiled without glx support"; - #endif - } - - if(instance->config.glType == FF_GL_TYPE_EGL) - { - #ifdef FF_HAVE_EGL - return eglPrint(instance); - #else - return "fastfetch was compiled without egl support"; - #endif - } - - if(instance->config.glType == FF_GL_TYPE_OSMESA) - { - #ifdef FF_HAVE_OSMESA - return osMesaPrint(instance); - #else - return "fastfetch was compiled without osmesa support"; - #endif - } - - const char* error = ""; // not NULL dummy value - - #ifdef FF_HAVE_EGL - error = eglPrint(instance); - #endif - - #ifdef FF_HAVE_GLX - if(error != NULL) - error = glxPrint(instance); - #endif - - //We don't use osmesa in auto mode here, because it is a software implementation, - //that doesn't reflect the opengl supported by the hardware - - return error; -} - -#endif // !__APPLE__ && FF_HAVE_GL - -void ffPrintOpenGL(FFinstance* instance) -{ - const char* error; - - #ifndef FF_HAVE_GL - error = "Fastfetch was built without gl support."; - #elif __APPLE__ - error = cglPrint(instance); - #else - error = glPrint(instance); - #endif - - if(error != NULL) - ffPrintError(instance, FF_OPENGL_MODULE_NAME, 0, &instance->config.openGL, "%s", error); + ffStrbufDestroy(&result.version); + ffStrbufDestroy(&result.renderer); + ffStrbufDestroy(&result.vendor); + ffStrbufDestroy(&result.slv); } diff --git a/src/modules/packages.c b/src/modules/packages.c index 62d5b9344..5e653bcd7 100644 --- a/src/modules/packages.c +++ b/src/modules/packages.c @@ -1,473 +1,17 @@ #include "fastfetch.h" -#include "common/io.h" -#include "common/properties.h" #include "common/printing.h" -#include "common/settings.h" -#include "common/processing.h" -#include "common/parsing.h" -#include "detection/os/os.h" - -#include -#include -#include -#include +#include "detection/packages/packages.h" #define FF_PACKAGES_MODULE_NAME "Packages" -#define FF_PACKAGES_NUM_FORMAT_ARGS 16 - -typedef struct PackageCounts -{ - uint32_t pacman; - uint32_t dpkg; - uint32_t rpm; - uint32_t emerge; - uint32_t xbps; - uint32_t nixSystem; - uint32_t nixDefault; - uint32_t apk; - uint32_t pkg; - uint32_t flatpak; - uint32_t snap; - uint32_t brew; - uint32_t port; - - FFstrbuf pacmanBranch; -} PackageCounts; - -static uint32_t getNumElements(const char* dirname, unsigned char type) -{ - DIR* dirp = opendir(dirname); - if(dirp == NULL) - return 0; - - uint32_t num_elements = 0; - - struct dirent *entry; - while((entry = readdir(dirp)) != NULL) { - if(entry->d_type == type) - ++num_elements; - } - - if(type == DT_DIR) - num_elements -= 2; // accounting for . and .. - - closedir(dirp); - - return num_elements; -} - -#ifndef __APPLE__ - -static uint32_t getNumStrings(const char* filename, const char* needle) -{ - FILE* file = fopen(filename, "r"); - if(file == NULL) - return 0; - - uint32_t count = 0; - - char* line = NULL; - size_t len = 0; - - while(getline(&line, &len, file) != EOF) - { - if(strstr(line, needle) != NULL) - ++count; - } - - if(line != NULL) - free(line); - - fclose(file); - - return count; -} - -#ifndef __ANDROID__ - -static uint32_t countFilesRecursive(FFstrbuf* baseDirPath, const char* filename) -{ - uint32_t baseDirPathLength = baseDirPath->length; - - ffStrbufAppendC(baseDirPath, '/'); - ffStrbufAppendS(baseDirPath, filename); - bool exists = ffFileExists(baseDirPath->chars, S_IFREG); - ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); - if(exists) - return 1; - - DIR* dirp = opendir(baseDirPath->chars); - if(dirp == NULL) - return 0; - - ffStrbufAppendC(baseDirPath, '/'); - baseDirPathLength = baseDirPath->length; - - uint32_t sum = 0; - - struct dirent *entry; - while((entry = readdir(dirp)) != NULL) { - // According to the PMS, neither category nor package name can begin with '.', so no need to check for . or .. specifically - if(entry->d_type != DT_DIR || entry->d_name[0] == '.') - continue; - - ffStrbufAppendS(baseDirPath, entry->d_name); - sum += countFilesRecursive(baseDirPath, filename); - ffStrbufSubstrBefore(baseDirPath, baseDirPathLength); - } - - closedir(dirp); - return sum; -} - -static uint32_t getNixPackages(char* path) -{ - //Nix detection is kinda slow, so we only do it if the dir exists - if(!ffFileExists(path, S_IFDIR)) - return 0; - - FFstrbuf output; - ffStrbufInitA(&output, 128); - - //https://github.com/LinusDierheimer/fastfetch/issues/195#issuecomment-1191748222 - FFstrbuf command; - ffStrbufInitA(&command, 255); - ffStrbufAppendS(&command, "for x in $(nix-store --query --requisites "); - ffStrbufAppendS(&command, path); - ffStrbufAppendS(&command, "); do if [ -d $x ]; then echo $x ; fi ; done | cut -d- -f2- | egrep '([0-9]{1,}\\.)+[0-9]{1,}' | egrep -v '\\-doc$|\\-man$|\\-info$|\\-dev$|\\-bin$|^nixos-system-nixos-' | uniq"); - - ffProcessAppendStdOut(&output, (char* const[]) { - "sh", - "-c", - command.chars, - NULL - }); - - //Each package is a new line in the output. If at least one line is found, add 1 for the last line. - uint32_t result = ffStrbufCountC(&output, '\n'); - if(result > 0) - result++; - - ffStrbufDestroy(&output); - return result; -} - -static uint32_t getXBPS(FFstrbuf* baseDir) -{ - DIR* dir = opendir(baseDir->chars); - if(dir == NULL) - return 0; - - uint32_t result = 0; - - struct dirent *entry; - while((entry = readdir(dir)) != NULL) - { - if(entry->d_type != DT_REG || strncasecmp(entry->d_name, "pkgdb-", 6) != 0) - continue; - - ffStrbufAppendC(baseDir, '/'); - ffStrbufAppendS(baseDir, entry->d_name); - result = getNumStrings(baseDir->chars, "installed"); - break; - } - - closedir(dir); - return result; -} - -#endif // !__ANDROID__ - -#else // !__APPLE__ - -static uint32_t countBrewPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/Caskroom"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - ffStrbufAppendS(baseDir, "/Cellar"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t getBrewPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - const char* prefix = getenv("HOMEBREW_PREFIX"); - bool prefixSet = ffStrSet(prefix); - - if(prefixSet) - { - ffStrbufAppendS(baseDir, prefix); - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - ffStrbufAppendS(baseDir, "/opt/homebrew"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - ffStrbufAppendS(baseDir, "/usr/local"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countBrewPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t countMacPortsPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/var/macports/software"); - result += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -static uint32_t getMacPortsPackages(FFstrbuf* baseDir) -{ - uint32_t result = 0; - uint32_t baseDirLength = baseDir->length; - - const char* prefix = getenv("MACPORTS_PREFIX"); - bool prefixSet = ffStrSet(prefix); - - if(prefixSet) - { - ffStrbufAppendS(baseDir, prefix); - result += countMacPortsPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - ffStrbufAppendS(baseDir, "/opt/local"); - if(!prefixSet || strcasecmp(baseDir->chars, prefix) != 0) - result += countMacPortsPackages(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - return result; -} - -#endif // __APPLE__ - -#ifdef FF_HAVE_RPM -#include "common/library.h" -#include -#include -#include -#include - -static uint32_t getRpmFromLibrpm(const FFinstance* instance) -{ - FF_LIBRARY_LOAD(rpm, &instance->config.librpm, 0, "librpm.so", 12) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmReadConfigFiles, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsCreate, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsInitIterator, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbGetIteratorCount, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmdbFreeIterator, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmtsFree, 0) - FF_LIBRARY_LOAD_SYMBOL(rpm, rpmlogSetMask, 0) - - // Don't print any error messages - ffrpmlogSetMask(RPMLOG_MASK(RPMLOG_EMERG)); - - if(ffrpmReadConfigFiles(NULL, NULL) != 0) - { - dlclose(rpm); - return 0; - } - - rpmts ts = ffrpmtsCreate(); - if(ts == NULL) - { - dlclose(rpm); - return 0; - } - - rpmdbMatchIterator mi = ffrpmtsInitIterator(ts, RPMDBI_LABEL, NULL, 0); - if(mi == NULL) - { - ffrpmtsFree(ts); - dlclose(rpm); - return 0; - } - - int count = ffrpmdbGetIteratorCount(mi); - - ffrpmdbFreeIterator(mi); - ffrpmtsFree(ts); - dlclose(rpm); - - return count > 0 ? (uint32_t) count : 0; -} - -#endif //FF_HAVE_RPM - -static void getPackageCounts(const FFinstance* instance, FFstrbuf* baseDir, PackageCounts* packageCounts) -{ - #if defined(__APPLE__) || defined(__ANDROID__) - FF_UNUSED(instance); - #endif - - #ifndef __APPLE__ //Linux desktop and Android - - uint32_t baseDirLength = baseDir->length; - - //pacman - ffStrbufAppendS(baseDir, "/var/lib/pacman/local"); - packageCounts->pacman += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //dpkg - ffStrbufAppendS(baseDir, "/var/lib/dpkg/status"); - packageCounts->dpkg += getNumStrings(baseDir->chars, "Status: "); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #ifndef __ANDROID__ //Linux Desktop - - //rpm - ffStrbufAppendS(baseDir, "/var/lib/rpm/rmpdb.sqlite"); - packageCounts->rpm += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(blob) FROM Packages"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //emerge - ffStrbufAppendS(baseDir, "/var/db/pkg"); - packageCounts->emerge += countFilesRecursive(baseDir, "SIZE"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //xps - ffStrbufAppendS(baseDir, "/var/db/xbps"); - packageCounts->xbps += getXBPS(baseDir); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //nix system - ffStrbufAppendS(baseDir, "/run/current-system"); - packageCounts->nixSystem += getNixPackages(baseDir->chars); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //nix default - ffStrbufAppendS(baseDir, "/nix/var/nix/profiles/default"); - packageCounts->nixDefault += getNixPackages(baseDir->chars); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //apk - ffStrbufAppendS(baseDir, "/lib/apk/db/installed"); - packageCounts->apk += getNumStrings(baseDir->chars, "C:Q"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //flatpak - ffStrbufAppendS(baseDir, "/var/lib/flatpak/app"); - packageCounts->flatpak += getNumElements(baseDir->chars, DT_DIR); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //snap - ffStrbufAppendS(baseDir, "/snap"); - uint32_t snap = getNumElements(baseDir->chars, DT_DIR); - if(snap > 0) - packageCounts->snap += (snap - 1); //Accounting for the /snap/bin folder - ffStrbufSubstrBefore(baseDir, baseDirLength); - - //pacman branch - ffStrbufAppendS(baseDir, FASTFETCH_TARGET_DIR_ETC"/pacman-mirrors.conf"); - if(ffParsePropFile(baseDir->chars, "Branch =", &packageCounts->pacmanBranch) && packageCounts->pacmanBranch.length == 0) - ffStrbufAppendS(&packageCounts->pacmanBranch, "stable"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #endif // !__ANDROID__ - - #else // !__APPLE__ - - //brew - packageCounts->brew += getBrewPackages(baseDir); - packageCounts->port += getMacPortsPackages(baseDir); - - #endif // __APPLE__ - - #ifdef __FreeBSD__ - - ffStrbufAppendS(baseDir, "/var/db/pkg/local.sqlite"); - packageCounts->pkg += (uint32_t) ffSettingsGetSQLite3Int(instance, baseDir->chars, "SELECT count(id) FROM packages"); - ffStrbufSubstrBefore(baseDir, baseDirLength); - - #endif // __FreeBSD__ -} - -static void getPackageCountsBedrock(const FFinstance* instance, FFstrbuf* baseDir, PackageCounts* packageCounts) -{ - uint32_t baseDirLength = baseDir->length; - - ffStrbufAppendS(baseDir, "/bedrock/strata"); - - DIR* dir = opendir(baseDir->chars); - if(dir == NULL) - { - ffStrbufSubstrBefore(baseDir, baseDirLength); - getPackageCounts(instance, baseDir, packageCounts); - return; - } - - ffStrbufAppendC(baseDir, '/'); - baseDirLength = baseDir->length; - - struct dirent* entry; - while((entry = readdir(dir)) != NULL) - { - if(entry->d_type != DT_DIR) - continue; - - ffStrbufAppendS(baseDir, entry->d_name); - getPackageCounts(instance, baseDir, packageCounts); - ffStrbufSubstrBefore(baseDir, baseDirLength); - } - - closedir(dir); -} +#define FF_PACKAGES_NUM_FORMAT_ARGS 17 void ffPrintPackages(FFinstance* instance) { - PackageCounts counts = {0}; + FFPackageCounts counts = {0}; ffStrbufInit(&counts.pacmanBranch); + ffDetectPackages(instance, &counts); - FFstrbuf baseDir; - ffStrbufInitA(&baseDir, 512); - ffStrbufAppendS(&baseDir, FASTFETCH_TARGET_DIR_ROOT); - - if(ffStrbufIgnCaseCompS(&ffDetectOS(instance)->id, "bedrock") == 0) - getPackageCountsBedrock(instance, &baseDir, &counts); - else - getPackageCounts(instance, &baseDir, &counts); - - // If SQL failed, we can still try with librpm. - // This is needed on openSUSE, which seems to use a proprietary database file - // This method doesn't work on bedrock, so we do it here. - #ifdef FF_HAVE_RPM - if(counts.rpm == 0) - counts.rpm = getRpmFromLibrpm(instance); - #endif - - #if !defined(__ANDROID__) && !defined(__APPLE__) - //nix user - ffStrbufSetS(&baseDir, instance->state.passwd->pw_dir); - ffStrbufAppendS(&baseDir, "/.nix-profile"); - uint32_t nixUser = getNixPackages(baseDir.chars); - #else - uint32_t nixUser = 0; - #endif - - ffStrbufDestroy(&baseDir); - - uint32_t all = counts.pacman + counts.dpkg + counts.rpm + counts.emerge + counts.xbps + counts.nixSystem + nixUser + counts.nixDefault + counts.apk + counts.pkg + counts.flatpak + counts.snap + counts.brew + counts.port; + uint32_t all = counts.pacman + counts.dpkg + counts.rpm + counts.emerge + counts.xbps + counts.nixSystem + counts.nixUser + counts.nixDefault + counts.apk + counts.pkg + counts.flatpak + counts.snap + counts.brew + counts.port + counts.scoop; if(all == 0) { ffPrintError(instance, FF_PACKAGES_MODULE_NAME, 0, &instance->config.packages, "No packages from known package managers found"); @@ -507,10 +51,10 @@ void ffPrintPackages(FFinstance* instance) printf(", "); } - if(nixUser > 0) + if(counts.nixUser > 0) { - printf("%u (nix-user)", nixUser); - if((all = all - nixUser) > 0) + printf("%u (nix-user)", counts.nixUser); + if((all = all - counts.nixUser) > 0) printf(", "); } @@ -527,6 +71,7 @@ void ffPrintPackages(FFinstance* instance) FF_PRINT_PACKAGE(snap) FF_PRINT_PACKAGE(brew) FF_PRINT_PACKAGE(port) + FF_PRINT_PACKAGE(scoop) //Fix linter warning of unused value of all (void) all; @@ -546,14 +91,15 @@ void ffPrintPackages(FFinstance* instance) {FF_FORMAT_ARG_TYPE_UINT, &counts.emerge}, {FF_FORMAT_ARG_TYPE_UINT, &counts.xbps}, {FF_FORMAT_ARG_TYPE_UINT, &counts.nixSystem}, - {FF_FORMAT_ARG_TYPE_UINT, &nixUser}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.nixUser}, {FF_FORMAT_ARG_TYPE_UINT, &counts.nixDefault}, {FF_FORMAT_ARG_TYPE_UINT, &counts.apk}, {FF_FORMAT_ARG_TYPE_UINT, &counts.pkg}, {FF_FORMAT_ARG_TYPE_UINT, &counts.flatpak}, {FF_FORMAT_ARG_TYPE_UINT, &counts.snap}, {FF_FORMAT_ARG_TYPE_UINT, &counts.brew}, - {FF_FORMAT_ARG_TYPE_UINT, &counts.port} + {FF_FORMAT_ARG_TYPE_UINT, &counts.port}, + {FF_FORMAT_ARG_TYPE_UINT, &counts.scoop} }); } diff --git a/src/modules/processes.c b/src/modules/processes.c index d424487d8..fa71a7611 100644 --- a/src/modules/processes.c +++ b/src/modules/processes.c @@ -1,47 +1,34 @@ #include "fastfetch.h" #include "common/printing.h" +#include "detection/processes/processes.h" #define FF_PROCESSES_MODULE_NAME "Processes" #define FF_PROCESSES_NUM_FORMAT_ARGS 1 -#ifdef __APPLE__ - #include -#endif - void ffPrintProcesses(FFinstance* instance) { - #if __APPLE__ - int request[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; - size_t length; + FFstrbuf error; + ffStrbufInit(&error); + uint32_t numProcesses = ffDetectProcesses(instance, &error); - if(sysctl(request, sizeof(request) / sizeof(*request), NULL, &length, NULL, 0) != 0) - { - ffPrintError(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, "sysctl() failed"); - return; - } - uint16_t numProcesses = (uint16_t)(length / sizeof(struct kinfo_proc)); - #elif FF_HAVE_SYSINFO_H - uint16_t numProcesses = (uint16_t) instance->state.sysinfo.procs; - #else - uint16_t numProcesses = 0; - #endif - - if(numProcesses == 0) + if(error.length > 0) { - ffPrintError(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, "Could not get number of processes"); + ffPrintError(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, "%*s", error.length, error.chars); + ffStrbufDestroy(&error); return; } + ffStrbufDestroy(&error); if(instance->config.processes.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes.key); - printf("%hu\n", numProcesses); + printf("%u\n", numProcesses); } else { ffPrintFormat(instance, FF_PROCESSES_MODULE_NAME, 0, &instance->config.processes, FF_PROCESSES_NUM_FORMAT_ARGS, (FFformatarg[]){ - {FF_FORMAT_ARG_TYPE_UINT16, &numProcesses} + {FF_FORMAT_ARG_TYPE_UINT, &numProcesses} }); } } diff --git a/src/modules/shell.c b/src/modules/shell.c index 3ea71f05e..607f5ec69 100644 --- a/src/modules/shell.c +++ b/src/modules/shell.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "common/printing.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #define FF_SHELL_MODULE_NAME "Shell" #define FF_SHELL_NUM_FORMAT_ARGS 7 @@ -18,7 +18,7 @@ void ffPrintShell(FFinstance* instance) if(instance->config.shell.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_SHELL_MODULE_NAME, 0, &instance->config.shell.key); - fputs(result->shellExeName, stdout); + ffStrbufWriteTo(&result->shellPrettyName, stdout); if(result->shellVersion.length > 0) { diff --git a/src/modules/terminal.c b/src/modules/terminal.c index 4d930a445..469ece23b 100644 --- a/src/modules/terminal.c +++ b/src/modules/terminal.c @@ -1,6 +1,6 @@ #include "fastfetch.h" #include "common/printing.h" -#include "detection/terminalshell.h" +#include "detection/terminalshell/terminalshell.h" #include @@ -20,11 +20,7 @@ void ffPrintTerminal(FFinstance* instance) if(instance->config.terminal.outputFormat.length == 0) { ffPrintLogoAndKey(instance, FF_TERMINAL_MODULE_NAME, 0, &instance->config.terminal.key); - - if(strncmp(result->terminalExeName, result->terminalProcessName.chars, result->terminalProcessName.length) == 0) // if exeName starts with processName, print it. Otherwise print processName - puts(result->terminalExeName); - else - ffStrbufPutTo(&result->terminalProcessName, stdout); + ffStrbufPutTo(&result->terminalPrettyName, stdout); } else { diff --git a/src/modules/theme.c b/src/modules/theme.c index 612bb6b96..5d294c196 100644 --- a/src/modules/theme.c +++ b/src/modules/theme.c @@ -10,7 +10,7 @@ void ffPrintTheme(FFinstance* instance) { - #if defined(__ANDROID__) || defined(__APPLE__) + #if defined(__ANDROID__) || defined(__APPLE__) || defined(_WIN32) || defined(__MSYS__) FF_UNUSED(instance); ffPrintError(instance, FF_THEME_MODULE_NAME, 0, &instance->config.theme, "Theme detection is not supported"); diff --git a/src/modules/users.c b/src/modules/users.c index c1b85f8da..1c78571b2 100644 --- a/src/modules/users.c +++ b/src/modules/users.c @@ -1,48 +1,25 @@ #include "fastfetch.h" #include "common/printing.h" - -#if FF_HAVE_UTMPX_H - #include -#else - //for Android compatibility - #include - #define utmpx utmp - #define setutxent setutent - #define getutxent getutent -#endif +#include "detection/users/users.h" #define FF_USERS_MODULE_NAME "Users" #define FF_USERS_NUM_FORMAT_ARGS 1 void ffPrintUsers(FFinstance* instance) { - struct utmpx* n = NULL; - setutxent(); - FFlist users; - ffListInit(&users, sizeof(n->ut_user) + 1); + ffListInit(&users, sizeof(FFstrbuf)); -next: - while((n = getutxent())) - { - if(n->ut_type == USER_PROCESS) - { - for(uint32_t i = 0; i < users.length; ++i) - { - if(strcmp((const char*)ffListGet(&users, i), n->ut_user) == 0) - goto next; - } - - char* dest = ffListAdd(&users); - strncpy(dest, n->ut_user, sizeof(n->ut_user)); - dest[sizeof(n->ut_user)] = '\0'; - } - } - - if(users.length == 0) + FFstrbuf error; + ffStrbufInit(&error); + + ffDetectUsers(&users, &error); + + if(error.length > 0) { + ffPrintError(instance, FF_USERS_MODULE_NAME, 0, &instance->config.users, "%*s", error.length, error.chars); ffListDestroy(&users); - ffPrintError(instance, FF_USERS_MODULE_NAME, 0, &instance->config.users, "Unable to detect users"); + ffStrbufDestroy(&error); return; } @@ -52,8 +29,11 @@ next: { if(i > 0) ffStrbufAppendS(&result, ", "); - ffStrbufAppendS(&result, (const char*)ffListGet(&users, i)); + FFstrbuf* user = (FFstrbuf*)ffListGet(&users, i); + ffStrbufAppend(&result, user); + ffStrbufDestroy(user); } + ffListDestroy(&users); if(instance->config.users.outputFormat.length == 0) diff --git a/src/util/windows/wmi.cpp b/src/util/windows/wmi.cpp new file mode 100644 index 000000000..845a3dda4 --- /dev/null +++ b/src/util/windows/wmi.cpp @@ -0,0 +1,305 @@ +#include "wmi.hpp" + +#include +#include + +//https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--getting-wmi-data-from-the-local-computer +//https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/computer-system-hardware-classes +static void CoUninitializeWrap() +{ + CoUninitialize(); +} + +static BOOL CALLBACK InitHandleFunction(PINIT_ONCE, PVOID, PVOID *lpContext) +{ + static char error[128]; + *((char**)lpContext) = error; + + HRESULT hres; + + // Initialize COM + hres = CoInitializeEx(nullptr, COINIT_MULTITHREADED); + + // Set general COM security levels + hres = CoInitializeSecurity( + nullptr, + -1, // COM authentication + nullptr, // Authentication services + nullptr, // Reserved + RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication + RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation + nullptr, // Authentication info + EOAC_NONE, // Additional capabilities + nullptr // Reserved + ); + + if (FAILED(hres)) + { + CoUninitialize(); + snprintf(error, sizeof(error), "Failed to initialize security. Error code = 0x%X", hres); + return FALSE; + } + + // Obtain the initial locator to WMI + IWbemLocator* pLoc = nullptr; + hres = CoCreateInstance( + CLSID_WbemLocator, + nullptr, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (LPVOID*) &pLoc); + + if (FAILED(hres)) + { + CoUninitialize(); + snprintf(error, sizeof(error), "Failed to create IWbemLocator object. Error code = 0x%X", hres); + return FALSE; + } + + // Connect to WMI through the IWbemLocator::ConnectServer method + IWbemServices* pSvc = nullptr; + + // Connect to the root\cimv2 namespace with + // the current user and obtain pointer pSvc + // to make IWbemServices calls. + hres = pLoc->ConnectServer( + bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace + nullptr, // User name. nullptr = current user + nullptr, // User password. nullptr = current + 0, // Locale. nullptr indicates current + 0, // Security flags. + 0, // Authority (for example, Kerberos) + 0, // Context object + &pSvc // pointer to IWbemServices proxy + ); + pLoc->Release(); + pLoc = nullptr; + + if (FAILED(hres)) + { + CoUninitialize(); + snprintf(error, sizeof(error), "Could not connect WMI server. Error code = 0x%X", hres); + return FALSE; + } + + // Set security levels on the proxy ------------------------- + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + nullptr, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + nullptr, // client identity + EOAC_NONE // proxy capabilities + ); + + if (FAILED(hres)) + { + pSvc->Release(); + CoUninitialize(); + snprintf(error, sizeof(error), "Could not set proxy blanket. Error code = 0x%X", hres); + return FALSE; + } + + *((IWbemServices**)lpContext) = pSvc; + atexit(CoUninitializeWrap); + return TRUE; +} + + +IEnumWbemClassObject* ffQueryWmi(const wchar_t* queryStr, FFstrbuf* error) +{ + static INIT_ONCE s_InitOnce = INIT_ONCE_STATIC_INIT; + const char* context; + if (InitOnceExecuteOnce(&s_InitOnce, &InitHandleFunction, nullptr, (void**)&context) == FALSE) + { + if(error) + ffStrbufInitS(error, context); + return nullptr; + } + + // Use the IWbemServices pointer to make requests of WMI + IEnumWbemClassObject* pEnumerator = nullptr; + HRESULT hres; + + hres = ((IWbemServices*)context)->ExecQuery( + bstr_t(L"WQL"), + bstr_t(queryStr), + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + nullptr, + &pEnumerator); + + if (FAILED(hres)) + { + if(error) + ffStrbufAppendF(error, "Query for '%ls' failed. Error code = 0x%X", queryStr, hres); + return nullptr; + } + + return pEnumerator; +} + +void ffBstrToStrbuf(BSTR bstr, FFstrbuf* strbuf) { + int len = (int)SysStringLen(bstr); + int size_needed = WideCharToMultiByte(CP_UTF8, 0, bstr, len, nullptr, 0, nullptr, nullptr); + ffStrbufEnsureFree(strbuf, (uint32_t)size_needed); + WideCharToMultiByte(CP_UTF8, 0, bstr, len, strbuf->chars, size_needed, nullptr, nullptr); + strbuf->length = (uint32_t)size_needed; + strbuf->chars[size_needed] = '\0'; +} + +bool ffGetWmiObjString(IWbemClassObject* obj, const wchar_t* key, FFstrbuf* strbuf) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + CIMTYPE type; + if(FAILED(obj->Get(key, 0, &vtProp, &type, nullptr)) || vtProp.vt != VT_BSTR) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: + if(type == CIM_DATETIME) + { + ISWbemDateTime *pDateTime; + BSTR dateStr; + if(FAILED(CoCreateInstance(__uuidof(SWbemDateTime), 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDateTime)))) + result = false; + else if(FAILED(pDateTime->put_Value(vtProp.bstrVal))) + result = false; + else if(FAILED(pDateTime->GetFileTime(VARIANT_TRUE, &dateStr))) + result = false; + else + ffBstrToStrbuf(dateStr, strbuf); + } + else + { + ffBstrToStrbuf(vtProp.bstrVal, strbuf); + } + break; + + case VT_LPSTR: + ffStrbufAppendS(strbuf, vtProp.pcVal); + break; + + case VT_LPWSTR: // TODO + default: + result = false; + break; + } + } + VariantClear(&vtProp); + return result; +} + +bool ffGetWmiObjSigned(IWbemClassObject* obj, const wchar_t* key, int64_t* integer) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + CIMTYPE type; + if(FAILED(obj->Get(key, 0, &vtProp, &type, nullptr))) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: *integer = wcstoll(vtProp.bstrVal, nullptr, 10); break; + case VT_I1: *integer = vtProp.cVal; break; + case VT_I2: *integer = vtProp.iVal; break; + case VT_INT: + case VT_I4: *integer = vtProp.intVal; break; + case VT_I8: *integer = vtProp.llVal; break; + case VT_UI1: *integer = (int64_t)vtProp.bVal; break; + case VT_UI2: *integer = (int64_t)vtProp.uiVal; break; + case VT_UINT: + case VT_UI4: *integer = (int64_t)vtProp.uintVal; break; + case VT_UI8: *integer = (int64_t)vtProp.ullVal; break; + case VT_BOOL: *integer = vtProp.boolVal != VARIANT_FALSE; break; + default: result = false; + } + } + VariantClear(&vtProp); + return result; +} + +bool ffGetWmiObjUnsigned(IWbemClassObject* obj, const wchar_t* key, uint64_t* integer) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + if(FAILED(obj->Get(key, 0, &vtProp, nullptr, nullptr))) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: *integer = wcstoull(vtProp.bstrVal, nullptr, 10); break; + case VT_I1: *integer = (uint64_t)vtProp.cVal; break; + case VT_I2: *integer = (uint64_t)vtProp.iVal; break; + case VT_INT: + case VT_I4: *integer = (uint64_t)vtProp.intVal; break; + case VT_I8: *integer = (uint64_t)vtProp.llVal; break; + case VT_UI1: *integer = vtProp.bVal; break; + case VT_UI2: *integer = vtProp.uiVal; break; + case VT_UINT: + case VT_UI4: *integer = vtProp.uintVal; break; + case VT_UI8: *integer = vtProp.ullVal; break; + case VT_BOOL: *integer = vtProp.boolVal != VARIANT_FALSE; break; + default: result = false; + } + } + VariantClear(&vtProp); + return result; +} + +bool ffGetWmiObjReal(IWbemClassObject* obj, const wchar_t* key, double* real) +{ + bool result = true; + + VARIANT vtProp; + VariantInit(&vtProp); + + if(FAILED(obj->Get(key, 0, &vtProp, nullptr, nullptr))) + { + result = false; + } + else + { + switch(vtProp.vt) + { + case VT_BSTR: *real = wcstod(vtProp.bstrVal, nullptr); break; + case VT_I1: *real = vtProp.cVal; break; + case VT_I2: *real = vtProp.iVal; break; + case VT_INT: + case VT_I4: *real = vtProp.intVal; break; + case VT_I8: *real = (double)vtProp.llVal; break; + case VT_UI1: *real = vtProp.bVal; break; + case VT_UI2: *real = vtProp.uiVal; break; + case VT_UINT: + case VT_UI4: *real = vtProp.uintVal; break; + case VT_UI8: *real = (double)vtProp.ullVal; break; + case VT_R4: *real = vtProp.fltVal; break; + case VT_R8: *real = vtProp.dblVal; break; + case VT_BOOL: *real = vtProp.boolVal != VARIANT_FALSE; break; + default: result = false; + } + } + VariantClear(&vtProp); + return result; +} diff --git a/src/util/windows/wmi.hpp b/src/util/windows/wmi.hpp new file mode 100644 index 000000000..53122dfa2 --- /dev/null +++ b/src/util/windows/wmi.hpp @@ -0,0 +1,46 @@ +#pragma once + +#ifndef FF_INCLUDED_util_windows_wmi +#define FF_INCLUDED_util_windows_wmi + +#ifdef __cplusplus + +extern "C" { + #include "util/FFstrbuf.h" +} + +#include + +// is not usable in MSYS, so provide our simple bstr_t implementation +struct bstr_t +{ + explicit bstr_t(const wchar_t* str) noexcept: _bstr(SysAllocString(str)) {} + + ~bstr_t() noexcept { SysFreeString(_bstr); } + + explicit operator const wchar_t*() const noexcept { + return _bstr; + } + + operator BSTR() const noexcept { + return _bstr; + } + +private: + BSTR _bstr; +}; + +void ffBstrToStrbuf(BSTR bstr, FFstrbuf* strbuf); + +IEnumWbemClassObject* ffQueryWmi(const wchar_t* queryStr, FFstrbuf* error); +bool ffGetWmiObjString(IWbemClassObject* obj, const wchar_t* key, FFstrbuf* strbuf); +bool ffGetWmiObjSigned(IWbemClassObject* obj, const wchar_t* key, int64_t* integer); +bool ffGetWmiObjUnsigned(IWbemClassObject* obj, const wchar_t* key, uint64_t* integer); +bool ffGetWmiObjReal(IWbemClassObject* obj, const wchar_t* key, double* real); + +#else + // Win32 COM headers requires C++ compiler + #error Must be included in C++ source file +#endif //__cplusplus + +#endif //FF_INCLUDED_util_windows_wmi