diff --git a/CHANGELOG.md b/CHANGELOG.md index e6cc76664..674e5b3f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Features: * Bluetooth module * Sound module +* Gamepad module * Support colored percentage numbers output Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index fcff26c2c..0d1c0eae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,6 +274,7 @@ set(LIBFASTFETCH_SRC src/modules/gpu.c src/modules/host.c src/modules/icons.c + src/modules/gamepad.c src/modules/kernel.c src/modules/locale.c src/modules/localip.c @@ -336,6 +337,7 @@ if(LINUX) src/detection/gtk.c src/detection/host/host_linux.c src/detection/localip/localip_linux.c + src/detection/gamepad/gamepad_linux.c src/detection/media/media_linux.c src/detection/memory/memory_linux.c src/detection/opengl/opengl_linux.c @@ -375,6 +377,7 @@ elseif(ANDROID) src/detection/gpu/gpu_nosupport.c src/detection/host/host_android.c src/detection/localip/localip_linux.c + src/detection/gamepad/gamepad_nosupport.c src/detection/media/media_nosupport.c src/detection/memory/memory_linux.c src/detection/opengl/opengl_linux.c @@ -420,6 +423,7 @@ elseif(BSD) src/detection/gtk.c src/detection/host/host_bsd.c src/detection/localip/localip_linux.c + src/detection/gamepad/gamepad_nosupport.c src/detection/media/media_linux.c src/detection/memory/memory_bsd.c src/detection/opengl/opengl_linux.c @@ -461,6 +465,7 @@ elseif(APPLE) src/detection/gpu/gpu_apple.c src/detection/host/host_apple.c src/detection/localip/localip_linux.c + src/detection/gamepad/gamepad_apple.c src/detection/media/media_apple.m src/detection/memory/memory_apple.c src/detection/opengl/opengl_apple.c @@ -501,6 +506,7 @@ elseif(WIN32) src/detection/gpu/gpu_windows.cpp src/detection/host/host_windows.c src/detection/localip/localip_windows.c + src/detection/gamepad/gamepad_windows.cpp src/detection/media/media_nosupport.c src/detection/memory/memory_windows.c src/detection/opengl/opengl_windows.c @@ -713,6 +719,7 @@ elseif(WIN32) PRIVATE "version" PRIVATE "setupapi" PRIVATE "dxgi" + PRIVATE "dinput8" PRIVATE "wtsapi32" PRIVATE "powrprof" ) diff --git a/README.md b/README.md index 34ba5e726..192c498c3 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ All categories not listed here should work without needing a specific implementa ##### Available Modules ``` -Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, GPU, Host, Icons, Kernel, Locale, LocalIP, Media, Memory, OpenCL, OpenGL, Packages, Player, Power Adapter, Processes, PublicIP, Separator, OS, Shell, Sound, Swap, Terminal, Terminal Font, Theme, Time, Title, Uptime, Vulkan, Wifi, WM, WMTheme +Battery, Bios, Bluetooth, Board, Break, Brightness, Colors, Command, CPU, CPUUsage, Cursor, Custom, Date, DateTime, DE, Disk, Display, Font, Gamepad, GPU, Host, Icons, Kernel, Locale, LocalIP, Media, Memory, OpenCL, OpenGL, Packages, Player, Power Adapter, Processes, PublicIP, Separator, OS, Shell, Sound, Swap, Terminal, Terminal Font, Theme, Time, Title, Uptime, Vulkan, Wifi, WM, WMTheme ``` ##### Builtin logos diff --git a/presets/all b/presets/all index dfcaccc44..e8b10e197 100644 --- a/presets/all +++ b/presets/all @@ -1 +1 @@ ---structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Sound:Weather:Break:Colors +--structure Title:Separator:OS:Host:Bios:Board:Chassis:Kernel:Uptime:Processes:Packages:Shell:Display:Brightness:DE:WM:WMTheme:Theme:Icons:Font:Cursor:Terminal:TerminalFont:CPU:CPUUsage:GPU:Memory:Swap:Disk:Battery:PowerAdapter:Player:Media:PublicIP:LocalIP:Wifi:DateTime:Locale:Vulkan:OpenGL:OpenCL:Users:Bluetooth:Sound:Gamepad:Weather:Break:Colors diff --git a/src/common/init.c b/src/common/init.c index 0125c4b75..592ba17df 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -117,6 +117,7 @@ static void defaultConfig(FFinstance* instance) initModuleArg(&instance->config.users); initModuleArg(&instance->config.bluetooth); initModuleArg(&instance->config.sound); + initModuleArg(&instance->config.gamepad); ffStrbufInitA(&instance->config.libPCI, 0); ffStrbufInitA(&instance->config.libVulkan, 0); @@ -385,6 +386,8 @@ static void destroyConfig(FFinstance* instance) destroyModuleArg(&instance->config.openCL); destroyModuleArg(&instance->config.users); destroyModuleArg(&instance->config.bluetooth); + destroyModuleArg(&instance->config.sound); + destroyModuleArg(&instance->config.gamepad); ffStrbufDestroy(&instance->config.libPCI); ffStrbufDestroy(&instance->config.libVulkan); diff --git a/src/data/config_user.txt b/src/data/config_user.txt index e4fcef289..62aef8ad1 100644 --- a/src/data/config_user.txt +++ b/src/data/config_user.txt @@ -255,9 +255,9 @@ # Sound show all option # Sets if all sound devices should be printed -# Must be either true or false -# Default is false ( only main device should be printed ). -#--sound-show-all false +# Must be either main, active or all. Default is main +# Default is main. +#--sound-type main # Percentage output type option # Applies to all modules that prints percentage values. Currently memory, swap, disk, battery and CPU usage are supported. @@ -309,6 +309,7 @@ #--users-key Users #--bluetooth-key Bluetooth #--sound-key Sound +#--gamepad-key Gamepad # Format options: # Sets the format string for module values. @@ -355,6 +356,7 @@ #--users-format #--bluetooth-format #--sound-format +#--gamepad-format # Error options: # Sets the format string to use if an error occured @@ -401,6 +403,7 @@ #--users-error #--bluetooth-error #--sound-error +#--gamepad-error # Library options: # Sets an user specific path to a library to load. diff --git a/src/detection/gamepad/gamepad.h b/src/detection/gamepad/gamepad.h new file mode 100644 index 000000000..1def1b430 --- /dev/null +++ b/src/detection/gamepad/gamepad.h @@ -0,0 +1,16 @@ +#pragma once + +#ifndef FF_INCLUDED_detection_gamepad_gamepad +#define FF_INCLUDED_detection_gamepad_gamepad + +#include "fastfetch.h" + +typedef struct FFGamepadDevice +{ + FFstrbuf identifier; + FFstrbuf name; +} FFGamepadDevice; + +const char* ffDetectGamepad(const FFinstance* instance, FFlist* devices /* List of FFGamepadDevice */); + +#endif diff --git a/src/detection/gamepad/gamepad_apple.c b/src/detection/gamepad/gamepad_apple.c new file mode 100644 index 000000000..154f1c587 --- /dev/null +++ b/src/detection/gamepad/gamepad_apple.c @@ -0,0 +1,41 @@ +#include "gamepad.h" +#include "util/apple/cf_helpers.h" + +#include +#include + +static void enumSet(const void* value, void *context) +{ + CFStringRef product = IOHIDDeviceGetProperty((IOHIDDeviceRef) value, CFSTR(kIOHIDProductKey)); + CFStringRef serialNumber = IOHIDDeviceGetProperty((IOHIDDeviceRef) value, CFSTR(kIOHIDSerialNumberKey)); + FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd((FFlist*) context); + ffStrbufInit(&device->identifier); + ffCfStrGetString(serialNumber, &device->identifier); + ffStrbufInit(&device->name); + ffCfStrGetString(product, &device->name); +} + +const char* ffDetectGamepad(FF_MAYBE_UNUSED const FFinstance* instance, FFlist* devices /* List of FFGamepadDevice */) +{ + IOHIDManagerRef FF_CFTYPE_AUTO_RELEASE manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone) != kIOReturnSuccess) + return "IOHIDManagerOpen() failed"; + + CFNumberRef FF_CFTYPE_AUTO_RELEASE genericDesktop = ffCfCreateInt(kHIDPage_GenericDesktop); + CFNumberRef FF_CFTYPE_AUTO_RELEASE gamePad = ffCfCreateInt(kHIDUsage_GD_GamePad); + CFDictionaryRef FF_CFTYPE_AUTO_RELEASE matching = CFDictionaryCreate(kCFAllocatorDefault, (const void **)(CFStringRef[]){ + CFSTR(kIOHIDDeviceUsagePageKey), + CFSTR(kIOHIDDeviceUsageKey) + }, (const void **)(CFNumberRef[]){ + genericDesktop, + gamePad + }, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + IOHIDManagerSetDeviceMatching(manager, matching); + + CFSetRef FF_CFTYPE_AUTO_RELEASE set = IOHIDManagerCopyDevices(manager); + if (set) + CFSetApplyFunction(set, &enumSet, devices); + IOHIDManagerClose(manager, 0); + + return NULL; +} diff --git a/src/detection/gamepad/gamepad_linux.c b/src/detection/gamepad/gamepad_linux.c new file mode 100644 index 000000000..ce61a5711 --- /dev/null +++ b/src/detection/gamepad/gamepad_linux.c @@ -0,0 +1,42 @@ +#include "gamepad.h" +#include "common/io/io.h" + +#include +#include + +const char* ffDetectGamepad(FF_MAYBE_UNUSED const FFinstance* instance, FFlist* devices /* List of FFGamepadDevice */) +{ + DIR* dirp = opendir("/sys/class/input/"); + if(dirp == NULL) + return "opendir(\"/sys/class/input/\") == NULL"; + + FF_STRBUF_AUTO_DESTROY path; + ffStrbufInitS(&path, "/sys/class/input/"); + uint32_t baseLen = path.length; + + struct dirent* entry; + while((entry = readdir(dirp)) != NULL) + { + if(strncmp(entry->d_name, "js", 2) != 0) + continue; + if(!isdigit(entry->d_name[2])) + continue; + + ffStrbufAppendS(&path, entry->d_name); + ffStrbufAppendS(&path, "/device/name"); + + if (ffPathExists(path.chars, FF_PATHTYPE_FILE)) + { + FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices); + ffStrbufInitS(&device->identifier, entry->d_name); + ffStrbufInit(&device->name); + ffAppendFileBuffer(path.chars, &device->name); + } + + ffStrbufSubstrBefore(&path, baseLen); + } + + closedir(dirp); + + return NULL; +} diff --git a/src/detection/gamepad/gamepad_nosupport.c b/src/detection/gamepad/gamepad_nosupport.c new file mode 100644 index 000000000..d921d3552 --- /dev/null +++ b/src/detection/gamepad/gamepad_nosupport.c @@ -0,0 +1,6 @@ +#include "gamepad.h" + +const char* ffDetectGamepad(FF_MAYBE_UNUSED const FFinstance* instance, FF_MAYBE_UNUSED FFlist* devices /* List of FFGamepadDevice */) +{ + return "Not supported on this platform"; +} diff --git a/src/detection/gamepad/gamepad_windows.cpp b/src/detection/gamepad/gamepad_windows.cpp new file mode 100644 index 000000000..c30ef9fe4 --- /dev/null +++ b/src/detection/gamepad/gamepad_windows.cpp @@ -0,0 +1,32 @@ +extern "C" { + #include "gamepad.h" +} + +#include +#define DIRECTINPUT_VERSION 0x0800 +#include + +CALLBACK BOOL DIEnumDevicesCallback(LPCDIDEVICEINSTANCEA lpddi, LPVOID pvRef) +{ + FFlist* devices = (FFlist*) pvRef; + + FFGamepadDevice* device = (FFGamepadDevice*) ffListAdd(devices); + ffStrbufInitF(&device->identifier, "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}", + lpddi->guidInstance.Data1, lpddi->guidInstance.Data2, lpddi->guidInstance.Data3, + lpddi->guidInstance.Data4[0], lpddi->guidInstance.Data4[1], lpddi->guidInstance.Data4[2], lpddi->guidInstance.Data4[3], + lpddi->guidInstance.Data4[4], lpddi->guidInstance.Data4[5], lpddi->guidInstance.Data4[6], lpddi->guidInstance.Data4[7]); + ffStrbufInitS(&device->name, lpddi->tszInstanceName); + + return DIENUM_CONTINUE; +} + +extern "C" +const char* ffDetectGamepad(FF_MAYBE_UNUSED const FFinstance* instance, FFlist* devices /* List of FFGamepadDevice */) +{ + IDirectInput8A* dinput; + if (!SUCCEEDED(DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8A, (void**)&dinput, NULL))) + return "DirectInput8Create() failed"; + dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, DIEnumDevicesCallback, devices, DIEDFL_ALLDEVICES); + dinput->Release(); + return NULL; +} diff --git a/src/fastfetch.c b/src/fastfetch.c index c107f4768..a977d3c47 100644 --- a/src/fastfetch.c +++ b/src/fastfetch.c @@ -464,6 +464,13 @@ static inline void printCommandHelp(const char* command) "Identifier" ); } + else if(strcasecmp(command, "gamepad-format") == 0) + { + constructAndPrintCommandHelpFormat("gamepad", "{1}", 1, + "Name", + "Identifier" + ); + } else fprintf(stderr, "No specific help for command %s provided\n", command); } @@ -1167,6 +1174,7 @@ static void parseOption(FFinstance* instance, FFdata* data, const char* key, con else if(optionParseModuleArgs(key, value, "users", &instance->config.users)) {} else if(optionParseModuleArgs(key, value, "bluetooth", &instance->config.bluetooth)) {} else if(optionParseModuleArgs(key, value, "sound", &instance->config.sound)) {} + else if(optionParseModuleArgs(key, value, "gamepad", &instance->config.gamepad)) {} /////////////////// //Library options// @@ -1479,6 +1487,8 @@ static void parseStructureCommand(FFinstance* instance, FFdata* data, const char ffPrintBluetooth(instance); else if(strcasecmp(line, "sound") == 0) ffPrintSound(instance); + else if(strcasecmp(line, "gamepad") == 0) + ffPrintGamepad(instance); else ffPrintErrorString(instance, line, 0, NULL, NULL, ""); } diff --git a/src/fastfetch.h b/src/fastfetch.h index 0d57902c6..b625611b1 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -148,6 +148,7 @@ typedef struct FFconfig FFModuleArgs users; FFModuleArgs bluetooth; FFModuleArgs sound; + FFModuleArgs gamepad; FFstrbuf libPCI; FFstrbuf libVulkan; @@ -326,5 +327,6 @@ void ffPrintUsers(FFinstance* instance); void ffPrintCommand(FFinstance* instance); void ffPrintBluetooth(FFinstance* instance); void ffPrintSound(FFinstance* instance); +void ffPrintGamepad(FFinstance* instance); #endif diff --git a/src/flashfetch.c b/src/flashfetch.c index 0c24732e3..8737c8fcf 100644 --- a/src/flashfetch.c +++ b/src/flashfetch.c @@ -63,6 +63,9 @@ int main(int argc, char** argv) //ffPrintOpenCL(&instance); //ffPrintUsers(&instance); //ffPrintWeather(&instance); + //ffPrintBluetooth(&instance); + //ffPrintSound(&instance); + //ffPrintGamepad(&instance); ffPrintBreak(&instance); ffPrintColors(&instance); diff --git a/src/modules/gamepad.c b/src/modules/gamepad.c new file mode 100644 index 000000000..7b935a972 --- /dev/null +++ b/src/modules/gamepad.c @@ -0,0 +1,48 @@ +#include "common/printing.h" +#include "detection/gamepad/gamepad.h" + +#define FF_GAMEPAD_MODULE_NAME "Gamepad" +#define FF_GAMEPAD_NUM_FORMAT_ARGS 2 + +static void printDevice(FFinstance* instance, const FFGamepadDevice* device, uint8_t index) +{ + if(instance->config.sound.outputFormat.length == 0) + { + ffPrintLogoAndKey(instance, FF_GAMEPAD_MODULE_NAME, index, &instance->config.sound.key); + ffStrbufPutTo(&device->name, stdout); + } + else + { + ffPrintFormat(instance, FF_GAMEPAD_MODULE_NAME, index, &instance->config.sound, FF_GAMEPAD_NUM_FORMAT_ARGS, (FFformatarg[]) { + {FF_FORMAT_ARG_TYPE_STRBUF, &device->name}, + {FF_FORMAT_ARG_TYPE_STRBUF, &device->identifier}, + }); + } +} + +void ffPrintGamepad(FFinstance* instance) +{ + FF_LIST_AUTO_DESTROY result; + ffListInit(&result, sizeof(FFGamepadDevice)); + const char* error = ffDetectGamepad(instance, &result); + + if(error) + { + ffPrintError(instance, FF_GAMEPAD_MODULE_NAME, 0, &instance->config.sound, "%s", error); + return; + } + + if(!result.length) + { + ffPrintError(instance, FF_GAMEPAD_MODULE_NAME, 0, &instance->config.sound, "No devices detected"); + return; + } + + uint8_t index = 0; + FF_LIST_FOR_EACH(FFGamepadDevice, device, result) + { + printDevice(instance, device, result.length > 1 ? ++index : 0); + ffStrbufDestroy(&device->identifier); + ffStrbufDestroy(&device->name); + } +} diff --git a/src/util/apple/cf_helpers.h b/src/util/apple/cf_helpers.h index d3cb66d43..52755bb8b 100644 --- a/src/util/apple/cf_helpers.h +++ b/src/util/apple/cf_helpers.h @@ -13,4 +13,17 @@ const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result); const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result); +static inline CFNumberRef ffCfCreateInt(int value) +{ + return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); +} + +static inline void cfReleaseWrapper(void* type) +{ + if (*(CFTypeRef*) type) + CFRelease(*(CFTypeRef*) type); +} + +#define FF_CFTYPE_AUTO_RELEASE __attribute__((__cleanup__(cfReleaseWrapper))) + #endif