From e6f33aac7b03de5f380f9b612cfe4f9c51ada94c Mon Sep 17 00:00:00 2001 From: Murad Date: Mon, 25 May 2026 11:27:12 +0300 Subject: [PATCH] add Thinkpad fan module --- Makefile.am | 2 ++ linux/Platform.c | 20 ++++++++++++++++++++ linux/Platform.h | 2 ++ linux/ThinkpadFan.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ linux/ThinkpadFan.h | 15 +++++++++++++++ 5 files changed, 83 insertions(+) create mode 100644 linux/ThinkpadFan.c create mode 100644 linux/ThinkpadFan.h diff --git a/Makefile.am b/Makefile.am index 52134df64..9d27d8019 100644 --- a/Makefile.am +++ b/Makefile.am @@ -204,6 +204,7 @@ linux_platform_headers = \ linux/ProcessField.h \ linux/SELinuxMeter.h \ linux/SystemdMeter.h \ + linux/ThinkpadFan.h \ linux/ZramMeter.h \ linux/ZramStats.h \ linux/ZswapStats.h \ @@ -230,6 +231,7 @@ linux_platform_sources = \ linux/PressureStallMeter.c \ linux/SELinuxMeter.c \ linux/SystemdMeter.c \ + linux/ThinkpadFan.c \ linux/ZramMeter.c \ zfs/ZfsArcMeter.c \ zfs/ZfsCompressedArcMeter.c diff --git a/linux/Platform.c b/linux/Platform.c index cecaf3b5d..87ac982d7 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -56,6 +56,7 @@ in the source distribution for its full text. #include "linux/OpenRCMeter.h" #include "linux/SELinuxMeter.h" #include "linux/SystemdMeter.h" +#include "linux/ThinkpadFan.h" #include "linux/ZramMeter.h" #include "linux/ZramStats.h" #include "linux/ZswapStats.h" @@ -277,6 +278,7 @@ const MeterClass* const Platform_meterTypes[] = { &OpenRCUserMeter_class, &FileDescriptorMeter_class, &GPUMeter_class, + &ThinkpadFanMeter_class, NULL }; @@ -991,6 +993,24 @@ void Platform_getBattery(double* percent, ACPresence* isOnAC) { Platform_Battery_cacheTime = now; } +int Platform_getThinkpadFan(void) { + char fandata[256]; + ssize_t fanread = Compat_readfile(PROCDIR "/acpi/ibm/fan", fandata, sizeof(fandata)); + if (fanread < 1) + return -1; + + const char* speed = strstr(fandata, "speed:"); + if (!speed) + return -1; + + int rpm; + int n = sscanf(speed, "speed: %d", &rpm); + if (n != 1) + return -1; + + return rpm; +} + void Platform_longOptionsUsage(const char* name) { #ifdef HAVE_LIBCAP diff --git a/linux/Platform.h b/linux/Platform.h index d91856342..e3e52d056 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -94,6 +94,8 @@ bool Platform_getNetworkIO(NetworkIOData* data); void Platform_getBattery(double* percent, ACPresence* isOnAC); +int Platform_getThinkpadFan(void); + static inline void Platform_getHostname(char* buffer, size_t size) { Generic_hostname(buffer, size); } diff --git a/linux/ThinkpadFan.c b/linux/ThinkpadFan.c new file mode 100644 index 000000000..a429179c9 --- /dev/null +++ b/linux/ThinkpadFan.c @@ -0,0 +1,44 @@ +/* +htop - ThinkpadFan.c +(C) 2026 Murad Karammaev +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "linux/ThinkpadFan.h" + +#include "CRT.h" +#include "Object.h" +#include "XUtils.h" +#include "linux/Platform.h" + + +static const int ThinkpadFanMeter_attributes[] = { + METER_VALUE +}; + +static void ThinkpadFanMeter_updateValues(Meter* this) { + int fanspeed = Platform_getThinkpadFan(); + if (fanspeed < 0) + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A"); + else + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%d RPM", fanspeed); +} + +const MeterClass ThinkpadFanMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete + }, + .updateValues = ThinkpadFanMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .supportedModes = (1 << TEXT_METERMODE) | (1 << LED_METERMODE), + .maxItems = 0, + .total = 0.0, + .attributes = ThinkpadFanMeter_attributes, + .name = "ThinkpadFan", + .uiName = "Thinkpad fan speed", + .caption = "Thinkpad fan: ", +}; diff --git a/linux/ThinkpadFan.h b/linux/ThinkpadFan.h new file mode 100644 index 000000000..e6b30f75f --- /dev/null +++ b/linux/ThinkpadFan.h @@ -0,0 +1,15 @@ +#ifndef HEADER_ThinkpadFan +#define HEADER_ThinkpadFan +/* +htop - ThinkpadFan.h +(C) 2026 Murad Karammaev +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Meter.h" + + +extern const MeterClass ThinkpadFanMeter_class; + +#endif