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