fixes, added window size and position persistation

This commit is contained in:
Radek Davidek 2026-03-05 16:44:58 +01:00
parent 50c7ebc2e2
commit 6253507917
7 changed files with 208 additions and 25 deletions

View File

@ -7,6 +7,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED gtk4)
pkg_check_modules(CAIRO REQUIRED cairo)
find_package(X11)
# Find glib-compile-resources tool
find_program(GLIB_COMPILE_RESOURCES glib-compile-resources REQUIRED)
@ -52,6 +53,10 @@ target_link_libraries(temp-monitor
m
)
if(X11_FOUND)
target_link_libraries(temp-monitor ${X11_LIBRARIES})
endif()
target_compile_options(temp-monitor PRIVATE ${GTK_CFLAGS_OTHER})
target_compile_options(temp-monitor PRIVATE ${CAIRO_CFLAGS_OTHER})

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -17,11 +17,17 @@ public:
// Getters and setters
int getWindowWidth() const { return windowWidth; }
int getWindowHeight() const { return windowHeight; }
int getWindowX() const { return windowX; }
int getWindowY() const { return windowY; }
bool hasWindowPosition() const { return hasSavedWindowPosition; }
int getPollingTime() const { return pollingTime; }
int getHistoryLength() const { return historyLength; }
void setWindowWidth(int w) { windowWidth = w; }
void setWindowHeight(int h) { windowHeight = h; }
void setWindowX(int x) { windowX = x; hasSavedWindowPosition = true; }
void setWindowY(int y) { windowY = y; hasSavedWindowPosition = true; }
void clearWindowPosition() { hasSavedWindowPosition = false; }
void setPollingTime(int t) { pollingTime = t; }
void setHistoryLength(int m) { historyLength = m; }
@ -39,6 +45,9 @@ private:
mutable std::string cachedConfigPath;
int windowWidth;
int windowHeight;
int windowX;
int windowY;
bool hasSavedWindowPosition;
int pollingTime;
int historyLength;

View File

@ -27,9 +27,13 @@ private:
static void onHistoryLengthChanged(GtkSpinButton *spinButton, gpointer userData);
static void onClearButtonClicked(GtkButton *button, gpointer userData);
static void onQuitButtonClicked(GtkButton *button, gpointer userData);
static void onWindowMap(GtkWidget *widget, gpointer userData);
static void onColorSet(GObject *object, GParamSpec *pspec, gpointer userData);
static void onNameChanged(GtkEditable *editable, gpointer userData);
static void onVisibilityToggled(GtkCheckButton *checkButton, gpointer userData);
bool getWindowPosition(int &x, int &y) const;
void applySavedWindowPosition();
GtkWidget *window;
GtkWidget *statusLabel;
@ -42,6 +46,7 @@ private:
int refreshRateSec;
GtkWidget *legendBox;
std::map<std::string, GtkWidget*> tempLabels;
bool restoreWindowPositionPending;
};
// Global main loop reference for proper shutdown

View File

@ -7,7 +7,9 @@
#include <unistd.h>
ConfigManager::ConfigManager()
: windowWidth(1200), windowHeight(700), pollingTime(1), historyLength(10)
: windowWidth(1200), windowHeight(700),
windowX(0), windowY(0), hasSavedWindowPosition(false),
pollingTime(1), historyLength(10)
{
}
@ -73,6 +75,12 @@ void ConfigManager::load()
windowWidth = std::stoi(value);
} else if (key == "window_height") {
windowHeight = std::stoi(value);
} else if (key == "window_x") {
windowX = std::stoi(value);
hasSavedWindowPosition = true;
} else if (key == "window_y") {
windowY = std::stoi(value);
hasSavedWindowPosition = true;
} else if (key == "polling_time") {
pollingTime = std::stoi(value);
} else if (key == "history_length") {
@ -107,6 +115,10 @@ void ConfigManager::save()
file << "# Auto-generated, do not edit manually\n\n";
file << "window_width = " << windowWidth << "\n";
file << "window_height = " << windowHeight << "\n";
if (hasSavedWindowPosition) {
file << "window_x = " << windowX << "\n";
file << "window_y = " << windowY << "\n";
}
file << "polling_time = " << pollingTime << "\n";
file << "history_length = " << historyLength << "\n";

View File

@ -7,10 +7,18 @@
#include <gio/gio.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#if defined(__has_include)
#if __has_include(<gdk/x11/gdkx.h>)
#include <gdk/x11/gdkx.h>
#include <X11/Xlib.h>
#define TEMP_MONITOR_HAS_GDK_X11 1
#endif
#endif
MainWindow::MainWindow()
: window(nullptr), statusLabel(nullptr),
refreshRateSpinBox(nullptr), historyLengthSpinBox(nullptr),
timerID(0), refreshRateSec(3)
timerID(0), refreshRateSec(3), restoreWindowPositionPending(false)
{
monitor = std::make_unique<TempMonitor>();
config = std::make_unique<ConfigManager>();
@ -28,6 +36,7 @@ MainWindow::MainWindow()
// Set window size from config
gtk_window_set_default_size(GTK_WINDOW(window), config->getWindowWidth(), config->getWindowHeight());
restoreWindowPositionPending = config->hasWindowPosition();
// Start update timer
timerID = g_timeout_add(refreshRateSec * 1000, onUpdateTimer, this);
@ -76,6 +85,7 @@ void MainWindow::setupUI()
gtk_window_set_default_size(GTK_WINDOW(window), 1200, 700);
g_signal_connect(window, "close-request", G_CALLBACK(onDeleteWindow), this);
g_signal_connect(window, "map", G_CALLBACK(onWindowMap), this);
// Main box
GtkWidget *mainBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
@ -208,11 +218,22 @@ void MainWindow::onQuitButtonClicked(GtkButton *button, gpointer userData)
void MainWindow::saveWindowState()
{
int width, height;
gtk_window_get_default_size(GTK_WINDOW(window), &width, &height);
int width = gtk_widget_get_width(window);
int height = gtk_widget_get_height(window);
if (width <= 0 || height <= 0) {
gtk_window_get_default_size(GTK_WINDOW(window), &width, &height);
}
config->setWindowWidth(width > 0 ? width : 1200);
config->setWindowHeight(height > 0 ? height : 700);
int posX = 0;
int posY = 0;
if (getWindowPosition(posX, posY)) {
config->setWindowX(posX);
config->setWindowY(posY);
}
config->setPollingTime(refreshRateSec);
config->save();
}
@ -362,6 +383,81 @@ void MainWindow::show()
gtk_widget_set_visible(window, TRUE);
}
void MainWindow::onWindowMap(GtkWidget *widget, gpointer userData)
{
(void)widget;
MainWindow *self = static_cast<MainWindow*>(userData);
self->applySavedWindowPosition();
}
void MainWindow::applySavedWindowPosition()
{
if (!restoreWindowPositionPending || !config->hasWindowPosition()) {
return;
}
restoreWindowPositionPending = false;
#ifdef TEMP_MONITOR_HAS_GDK_X11
GdkDisplay *display = gtk_widget_get_display(window);
if (!display || !GDK_IS_X11_DISPLAY(display)) {
return;
}
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(window));
if (!surface || !GDK_IS_X11_SURFACE(surface)) {
return;
}
Display *xDisplay = gdk_x11_display_get_xdisplay(GDK_X11_DISPLAY(display));
if (!xDisplay) {
return;
}
Window xid = gdk_x11_surface_get_xid(surface);
XMoveWindow(xDisplay, xid, config->getWindowX(), config->getWindowY());
XFlush(xDisplay);
#endif
}
bool MainWindow::getWindowPosition(int &x, int &y) const
{
#ifdef TEMP_MONITOR_HAS_GDK_X11
GdkDisplay *display = gtk_widget_get_display(window);
if (!display || !GDK_IS_X11_DISPLAY(display)) {
return false;
}
GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(window));
if (!surface || !GDK_IS_X11_SURFACE(surface)) {
return false;
}
Display *xDisplay = gdk_x11_display_get_xdisplay(GDK_X11_DISPLAY(display));
if (!xDisplay) {
return false;
}
Window xid = gdk_x11_surface_get_xid(surface);
Window root = DefaultRootWindow(xDisplay);
Window child = 0;
int absX = 0;
int absY = 0;
if (!XTranslateCoordinates(xDisplay, xid, root, 0, 0, &absX, &absY, &child)) {
return false;
}
x = absX;
y = absY;
return true;
#else
(void)x;
(void)y;
return false;
#endif
}
void MainWindow::onColorSet(GObject *object, GParamSpec *pspec, gpointer userData)
{
MainWindow *self = static_cast<MainWindow*>(userData);

View File

@ -6,17 +6,36 @@
#include <sys/stat.h>
#include <iostream>
#include <algorithm>
#include <cstring>
namespace {
bool isDebugLoggingEnabled()
{
static const bool enabled = [] {
const char* envValue = std::getenv("TEMP_MONITOR_DEBUG");
return envValue && std::strcmp(envValue, "1") == 0;
}();
return enabled;
}
}
TempMonitor::TempMonitor()
{
std::cerr << "[DEBUG] TempMonitor constructor - starting sensor discovery" << std::endl;
if (isDebugLoggingEnabled()) {
std::cerr << "[DEBUG] TempMonitor constructor - starting sensor discovery" << std::endl;
}
discoverSensors();
std::cerr << "[DEBUG] Sensor discovery complete - found " << discoveredSensors.size() << " sensors" << std::endl;
if (isDebugLoggingEnabled()) {
std::cerr << "[DEBUG] Sensor discovery complete - found " << discoveredSensors.size() << " sensors" << std::endl;
}
}
void TempMonitor::discoverSensors()
{
std::cerr << "[DEBUG] discoverSensors() started" << std::endl;
const bool debug = isDebugLoggingEnabled();
if (debug) {
std::cerr << "[DEBUG] discoverSensors() started" << std::endl;
}
discoveredSensors.clear();
DIR* hwmonDir = opendir("/sys/class/hwmon");
if (!hwmonDir) {
@ -24,13 +43,17 @@ void TempMonitor::discoverSensors()
return;
}
std::cerr << "[DEBUG] Opened /sys/class/hwmon successfully" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Opened /sys/class/hwmon successfully" << std::endl;
}
struct dirent* entry;
while ((entry = readdir(hwmonDir)) != nullptr) {
std::string hwmonName = entry->d_name;
if (hwmonName.find("hwmon") != 0) continue;
std::cerr << "[DEBUG] Processing hwmon device: " << hwmonName << std::endl;
if (debug) {
std::cerr << "[DEBUG] Processing hwmon device: " << hwmonName << std::endl;
}
std::string path = "/sys/class/hwmon/" + hwmonName;
// Read "name" from sysfs
@ -42,24 +65,32 @@ void TempMonitor::discoverSensors()
nameFile.close();
}
std::cerr << "[DEBUG] Device name: " << nameFromSysfs << std::endl;
if (debug) {
std::cerr << "[DEBUG] Device name: " << nameFromSysfs << std::endl;
}
// Filter: Only interested in nvme and coretemp (CPU)
if (nameFromSysfs != "nvme" && nameFromSysfs != "coretemp") {
std::cerr << "[DEBUG] Skipped - not nvme or coretemp" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Skipped - not nvme or coretemp" << std::endl;
}
continue;
}
DIR* dDir = opendir(path.c_str());
if (dDir) {
std::cerr << "[DEBUG] Opened directory: " << path << std::endl;
if (debug) {
std::cerr << "[DEBUG] Opened directory: " << path << std::endl;
}
struct dirent* sEntry;
while ((sEntry = readdir(dDir)) != nullptr) {
std::string fname = sEntry->d_name;
// Only temperature sensors (temp*_input)
if (fname.find("temp") == 0 && fname.find("_input") != std::string::npos) {
std::string id = fname.substr(4, fname.find("_input") - 4);
std::cerr << "[DEBUG] Found temperature file: " << fname << " (id: " << id << ")" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Found temperature file: " << fname << " (id: " << id << ")" << std::endl;
}
// Use "label" from sysfs for sensor name if available
std::string labelFromSysfs = "";
@ -70,12 +101,16 @@ void TempMonitor::discoverSensors()
labelFile.close();
}
std::cerr << "[DEBUG] Label: '" << labelFromSysfs << "'" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Label: '" << labelFromSysfs << "'" << std::endl;
}
// For CPU (coretemp), filter only Package id 0 (if label exists)
if (nameFromSysfs == "coretemp") {
if (!labelFromSysfs.empty() && labelFromSysfs != "Package id 0") {
std::cerr << "[DEBUG] Filtered (coretemp, not Package id 0)" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Filtered (coretemp, not Package id 0)" << std::endl;
}
continue;
}
}
@ -83,8 +118,10 @@ void TempMonitor::discoverSensors()
std::string finalSensorName = labelFromSysfs.empty() ? "temp" + id : labelFromSysfs;
std::string deviceDisplayName = nameFromSysfs + " (" + hwmonName + ")";
std::cerr << "[DEBUG] Added sensor: " << finalSensorName << " from " << deviceDisplayName << std::endl;
std::cerr << "[DEBUG] Path: " << path + "/" + fname << std::endl;
if (debug) {
std::cerr << "[DEBUG] Added sensor: " << finalSensorName << " from " << deviceDisplayName << std::endl;
std::cerr << "[DEBUG] Path: " << path + "/" + fname << std::endl;
}
discoveredSensors.push_back({deviceDisplayName, finalSensorName, path + "/" + fname});
}
@ -95,17 +132,24 @@ void TempMonitor::discoverSensors()
}
}
closedir(hwmonDir);
std::cerr << "[DEBUG] discoverSensors() finished - total sensors: " << discoveredSensors.size() << std::endl;
if (debug) {
std::cerr << "[DEBUG] discoverSensors() finished - total sensors: " << discoveredSensors.size() << std::endl;
}
}
std::vector<std::string> TempMonitor::getAvailableDevices()
{
std::cerr << "[DEBUG] getAvailableDevices() called" << std::endl;
const bool debug = isDebugLoggingEnabled();
if (debug) {
std::cerr << "[DEBUG] getAvailableDevices() called" << std::endl;
}
std::vector<std::string> devices;
auto all = getAllTemperatures();
for (auto const& [name, sensors] : all) {
devices.push_back(name);
std::cerr << "[DEBUG] Device: " << name << " with " << sensors.size() << " sensors" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Device: " << name << " with " << sensors.size() << " sensors" << std::endl;
}
}
return devices;
}
@ -123,7 +167,9 @@ double TempMonitor::readTemperatureFromFile(const std::string &filePath)
try {
long tempMilliC = std::stol(line);
double tempC = tempMilliC / 1000.0;
std::cerr << "[DEBUG] Read temperature from " << filePath << ": " << tempC << "°C" << std::endl;
if (isDebugLoggingEnabled()) {
std::cerr << "[DEBUG] Read temperature from " << filePath << ": " << tempC << "°C" << std::endl;
}
return tempC;
} catch (...) {
std::cerr << "[ERROR] Failed to parse temperature value: " << line << std::endl;
@ -134,11 +180,16 @@ double TempMonitor::readTemperatureFromFile(const std::string &filePath)
std::map<std::string, double> TempMonitor::readTemperatures(const std::string &device)
{
std::cerr << "[DEBUG] readTemperatures() called for device: " << device << std::endl;
const bool debug = isDebugLoggingEnabled();
if (debug) {
std::cerr << "[DEBUG] readTemperatures() called for device: " << device << std::endl;
}
auto all = getAllTemperatures();
if (all.count(device)) {
auto sensors = all.at(device);
std::cerr << "[DEBUG] Found " << sensors.size() << " sensors for device" << std::endl;
if (debug) {
std::cerr << "[DEBUG] Found " << sensors.size() << " sensors for device" << std::endl;
}
return sensors;
}
std::cerr << "[ERROR] Device not found: " << device << std::endl;
@ -147,7 +198,10 @@ std::map<std::string, double> TempMonitor::readTemperatures(const std::string &d
std::map<std::string, std::map<std::string, double>> TempMonitor::getAllTemperatures()
{
std::cerr << "[DEBUG] getAllTemperatures() called" << std::endl;
const bool debug = isDebugLoggingEnabled();
if (debug) {
std::cerr << "[DEBUG] getAllTemperatures() called" << std::endl;
}
std::map<std::string, std::map<std::string, double>> allTemperatures;
for (const auto& sensor : discoveredSensors) {
@ -157,6 +211,8 @@ std::map<std::string, std::map<std::string, double>> TempMonitor::getAllTemperat
}
}
std::cerr << "[DEBUG] getAllTemperatures() finished - total devices: " << allTemperatures.size() << std::endl;
if (debug) {
std::cerr << "[DEBUG] getAllTemperatures() finished - total devices: " << allTemperatures.size() << std::endl;
}
return allTemperatures;
}