diff --git a/include/config_manager.h b/include/config_manager.h index 432eead..cf43e48 100644 --- a/include/config_manager.h +++ b/include/config_manager.h @@ -32,6 +32,7 @@ public: private: std::string getConfigFilePath() const; + mutable std::string cachedConfigPath; int windowWidth; int windowHeight; int pollingTime; diff --git a/include/mainwindow.h b/include/mainwindow.h index f73c739..449b597 100644 --- a/include/mainwindow.h +++ b/include/mainwindow.h @@ -2,6 +2,8 @@ #define MAINWINDOW_H #include +#include +#include #include "temp_monitor.h" #include "temperature_chart.h" #include "config_manager.h" @@ -30,9 +32,9 @@ private: GtkWidget *window; GtkWidget *statusLabel; GtkSpinButton *refreshRateSpinBox; - TemperatureChart *chart; - TempMonitor *monitor; - ConfigManager *config; + std::unique_ptr chart; + std::unique_ptr monitor; + std::unique_ptr config; guint timerID; int refreshRateSec; GtkWidget *legendBox; diff --git a/include/temperature_chart.h b/include/temperature_chart.h index a57fc9f..47bd075 100644 --- a/include/temperature_chart.h +++ b/include/temperature_chart.h @@ -22,7 +22,7 @@ struct SeriesData { class TemperatureChart { public: - TemperatureChart(GtkWidget *parent = nullptr); + TemperatureChart(); ~TemperatureChart(); GtkWidget* getWidget() const { return drawingArea; } @@ -48,7 +48,7 @@ private: void redraw(); void updateThemeColors(); - static gboolean onTick(gpointer userData); + static void onLeave(GtkEventControllerMotion *motion, gpointer userData); struct NearestPoint { diff --git a/resources/style.css b/resources/style.css index 285f960..efadaa5 100644 --- a/resources/style.css +++ b/resources/style.css @@ -5,27 +5,20 @@ window { .small-entry { font-size: 9px; - min-height: 0; - min-width: 0; - padding: 0px 1px; + padding: 1px; margin: 0; border: none; background: transparent; } -.small-entry:focus { - background: rgba(255,255,255,0.1); -} - .temp-label-small { - font-size: 9px; + font-size: 10px; font-weight: bold; - margin-left: 1px; - color: #ccc; + margin-left: 2px; + color: #888; } -/* Compact box items */ .legend-item { - margin: 0 4px; - padding: 0; + margin: 0 5px; + padding: 2px; } diff --git a/src/config_manager.cpp b/src/config_manager.cpp index d9e1543..e076709 100644 --- a/src/config_manager.cpp +++ b/src/config_manager.cpp @@ -13,13 +13,18 @@ ConfigManager::ConfigManager() std::string ConfigManager::getConfigFilePath() const { + if (!cachedConfigPath.empty()) { + return cachedConfigPath; + } + // Get the directory of the executable using /proc/self/exe char path[1024]; ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1); if (len == -1) { // Fallback to current directory - return "./nvme-monitor.conf"; + cachedConfigPath = "./nvme-monitor.conf"; + return cachedConfigPath; } path[len] = '\0'; @@ -29,10 +34,12 @@ std::string ConfigManager::getConfigFilePath() const size_t lastSlash = fullPath.find_last_of('/'); if (lastSlash != std::string::npos) { std::string exeDir = fullPath.substr(0, lastSlash); - return exeDir + "/nvme-monitor.conf"; + cachedConfigPath = exeDir + "/nvme-monitor.conf"; + return cachedConfigPath; } - return "./nvme-monitor.conf"; + cachedConfigPath = "./nvme-monitor.conf"; + return cachedConfigPath; } void ConfigManager::load() @@ -61,16 +68,20 @@ void ConfigManager::load() value.erase(0, value.find_first_not_of(" \t")); value.erase(value.find_last_not_of(" \t") + 1); - if (key == "window_width") { - windowWidth = std::stoi(value); - } else if (key == "window_height") { - windowHeight = std::stoi(value); - } else if (key == "polling_time") { - pollingTime = std::stoi(value); - } else if (key.find("color_") == 0) { - sensorColors[key.substr(6)] = value; - } else if (key.find("name_") == 0) { - sensorNames[key.substr(5)] = value; + try { + if (key == "window_width") { + windowWidth = std::stoi(value); + } else if (key == "window_height") { + windowHeight = std::stoi(value); + } else if (key == "polling_time") { + pollingTime = std::stoi(value); + } else if (key.find("color_") == 0) { + sensorColors[key.substr(6)] = value; + } else if (key.find("name_") == 0) { + sensorNames[key.substr(5)] = value; + } + } catch (const std::exception &e) { + std::cerr << "Config error: invalid value for '" << key << "': " << value << " (" << e.what() << ")" << std::endl; } } diff --git a/src/main.cpp b/src/main.cpp index 63fca48..f0006de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,19 +51,13 @@ int main(int argc, char *argv[]) gtk_init(); - std::cerr << "GTK Initialized" << std::endl; - // Register resources containing the application icon GResource *resource = resources_get_resource(); g_resources_register(resource); - std::cerr << "Resources registered" << std::endl; - gMainLoop = g_main_loop_new(nullptr, FALSE); - std::cerr << "Creating MainWindow" << std::endl; MainWindow *window = new MainWindow(); - std::cerr << "MainWindow created" << std::endl; window->show(); // Run the main event loop diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c77d62e..cba75af 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -9,11 +9,10 @@ MainWindow::MainWindow() : window(nullptr), statusLabel(nullptr), refreshRateSpinBox(nullptr), - chart(nullptr), monitor(nullptr), config(nullptr), timerID(0), - refreshRateSec(3) + timerID(0), refreshRateSec(3) { - monitor = new TempMonitor(); - config = new ConfigManager(); + monitor = std::make_unique(); + config = std::make_unique(); config->load(); refreshRateSec = config->getPollingTime(); @@ -35,15 +34,8 @@ MainWindow::~MainWindow() if (timerID) { g_source_remove(timerID); } - if (monitor) { - delete monitor; - } - if (chart) { - delete chart; - } if (config) { config->save(); - delete config; } } @@ -95,7 +87,8 @@ void MainWindow::setupUI() // Refresh rate spinner GtkAdjustment *adjustment = gtk_adjustment_new(refreshRateSec, 1, 60, 1, 5, 0); - refreshRateSpinBox = GTK_SPIN_BUTTON(gtk_spin_button_new(adjustment, 100, 0)); + refreshRateSpinBox = GTK_SPIN_BUTTON(gtk_spin_button_new(adjustment, 1, 0)); + gtk_widget_set_size_request(GTK_WIDGET(refreshRateSpinBox), 80, -1); g_signal_connect(refreshRateSpinBox, "value-changed", G_CALLBACK(onRefreshRateChanged), this); gtk_box_append(GTK_BOX(controlBox), GTK_WIDGET(refreshRateSpinBox)); @@ -118,16 +111,17 @@ void MainWindow::setupUI() gtk_box_append(GTK_BOX(mainBox), controlBox); // Chart - chart = new TemperatureChart(); + chart = std::make_unique(); gtk_box_append(GTK_BOX(mainBox), chart->getWidget()); gtk_widget_set_vexpand(chart->getWidget(), TRUE); - // Legend box with horizontal scrolling only + // Legend box with horizontal scrolling legendBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); GtkWidget *legendScroll = gtk_scrolled_window_new(); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(legendScroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(legendScroll), legendBox); + gtk_widget_set_vexpand(legendScroll, FALSE); gtk_widget_set_margin_top(legendScroll, 2); gtk_box_append(GTK_BOX(mainBox), legendScroll); @@ -199,7 +193,6 @@ void MainWindow::saveWindowState() void MainWindow::updateTemperatures() { - std::cerr << "updateTemperatures starting" << std::endl; auto allTemps = monitor->getAllTemperatures(); // Get current real time in milliseconds since epoch @@ -213,14 +206,10 @@ void MainWindow::updateTemperatures() const std::string &device = deviceEntry.first; const auto &temps = deviceEntry.second; - std::cerr << "Processing device: " << device << " with " << temps.size() << " sensors" << std::endl; - for (const auto &tempEntry : temps) { const std::string &sensorName = tempEntry.first; double temperature = tempEntry.second; - std::cerr << " Sensor: " << sensorName << " Temp: " << temperature << std::endl; - if (chart->addTemperatureData(device, sensorName, temperature, currentTime)) { needsLegendUpdate = true; @@ -297,7 +286,7 @@ void MainWindow::updateLegend() GtkColorDialog *dialog = gtk_color_dialog_new(); GtkWidget *colorButton = gtk_color_dialog_button_new(dialog); gtk_color_dialog_button_set_rgba(GTK_COLOR_DIALOG_BUTTON(colorButton), &color); - gtk_widget_set_size_request(colorButton, 12, 12); + gtk_widget_set_size_request(colorButton, 16, 16); // Store series id in user data g_object_set_data_full(G_OBJECT(colorButton), "series-name", g_strdup(seriesId.c_str()), g_free); @@ -307,8 +296,8 @@ void MainWindow::updateLegend() // Create editable label for series name GtkWidget *entry = gtk_entry_new(); gtk_editable_set_text(GTK_EDITABLE(entry), displayName.c_str()); - gtk_widget_set_size_request(entry, 60, -1); gtk_widget_add_css_class(entry, "small-entry"); + gtk_editable_set_width_chars(GTK_EDITABLE(entry), 8); // Store series id in user data g_object_set_data_full(G_OBJECT(entry), "series-name", g_strdup(seriesId.c_str()), g_free); diff --git a/src/temperature_chart.cpp b/src/temperature_chart.cpp index 71914e0..4e678f3 100644 --- a/src/temperature_chart.cpp +++ b/src/temperature_chart.cpp @@ -6,7 +6,7 @@ #include #include -TemperatureChart::TemperatureChart(GtkWidget *parent) +TemperatureChart::TemperatureChart() : drawingArea(nullptr), tooltipWindow(nullptr), tooltipLabel(nullptr), maxDataPoints(600), tickHandler(0), minTemp(MIN_TEMP), maxTemp(MAX_TEMP), minTime(0), maxTime(0), @@ -51,9 +51,16 @@ TemperatureChart::TemperatureChart(GtkWidget *parent) gtk_widget_add_controller(drawingArea, motion); setupColors(); - - // Setup tick callback for redrawing - tickHandler = g_timeout_add(100, onTick, this); + + // Listen for theme changes + GtkSettings *settings = gtk_settings_get_default(); + if (settings) { + g_signal_connect_swapped(settings, "notify::gtk-theme-name", + G_CALLBACK(+[](TemperatureChart *self) { + self->updateThemeColors(); + gtk_widget_queue_draw(self->drawingArea); + }), this); + } } TemperatureChart::~TemperatureChart() @@ -203,9 +210,35 @@ bool TemperatureChart::addTemperatureData(const std::string &device, const std:: } } - // Update temperature range - minTemp = MIN_TEMP; - maxTemp = MAX_TEMP; + // Update temperature range dynamically + double currentMin = 1000.0; + double currentMax = -1000.0; + bool hasData = false; + + for (const auto &entry : seriesMap) { + for (const auto &p : entry.second.points) { + if (p.temperature < currentMin) currentMin = p.temperature; + if (p.temperature > currentMax) currentMax = p.temperature; + hasData = true; + } + } + + if (hasData) { + // Round down min to nearest 10, round up max to nearest 10 + minTemp = std::floor(currentMin / 10.0) * 10.0; + maxTemp = std::ceil(currentMax / 10.0) * 10.0; + + // Ensure at least 20 degrees range + if (maxTemp - minTemp < 20.0) { + maxTemp = minTemp + 20.0; + } + + // Don't let it be too small + if (minTemp > 30.0) minTemp = 30.0; + } else { + minTemp = MIN_TEMP; + maxTemp = MAX_TEMP; + } // Update time range - keep 10 minute window maxTime = timestamp; @@ -266,18 +299,9 @@ void TemperatureChart::setSeriesName(const std::string &seriesId, const std::str } } -gboolean TemperatureChart::onTick(gpointer userData) -{ - TemperatureChart *self = static_cast(userData); - gtk_widget_queue_draw(self->drawingArea); - return TRUE; -} void TemperatureChart::drawChart(GtkDrawingArea *area, cairo_t *cr, int width, int height) { - // Update theme colors before drawing - updateThemeColors(); - // Background with theme color cairo_set_source_rgb(cr, bgColor.red, bgColor.green, bgColor.blue); cairo_paint(cr);