optimized
This commit is contained in:
parent
c8668e5f00
commit
fc1ca721fc
@ -32,6 +32,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
std::string getConfigFilePath() const;
|
std::string getConfigFilePath() const;
|
||||||
|
|
||||||
|
mutable std::string cachedConfigPath;
|
||||||
int windowWidth;
|
int windowWidth;
|
||||||
int windowHeight;
|
int windowHeight;
|
||||||
int pollingTime;
|
int pollingTime;
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
#define MAINWINDOW_H
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
#include "temp_monitor.h"
|
#include "temp_monitor.h"
|
||||||
#include "temperature_chart.h"
|
#include "temperature_chart.h"
|
||||||
#include "config_manager.h"
|
#include "config_manager.h"
|
||||||
@ -30,9 +32,9 @@ private:
|
|||||||
GtkWidget *window;
|
GtkWidget *window;
|
||||||
GtkWidget *statusLabel;
|
GtkWidget *statusLabel;
|
||||||
GtkSpinButton *refreshRateSpinBox;
|
GtkSpinButton *refreshRateSpinBox;
|
||||||
TemperatureChart *chart;
|
std::unique_ptr<TemperatureChart> chart;
|
||||||
TempMonitor *monitor;
|
std::unique_ptr<TempMonitor> monitor;
|
||||||
ConfigManager *config;
|
std::unique_ptr<ConfigManager> config;
|
||||||
guint timerID;
|
guint timerID;
|
||||||
int refreshRateSec;
|
int refreshRateSec;
|
||||||
GtkWidget *legendBox;
|
GtkWidget *legendBox;
|
||||||
|
|||||||
@ -22,7 +22,7 @@ struct SeriesData {
|
|||||||
|
|
||||||
class TemperatureChart {
|
class TemperatureChart {
|
||||||
public:
|
public:
|
||||||
TemperatureChart(GtkWidget *parent = nullptr);
|
TemperatureChart();
|
||||||
~TemperatureChart();
|
~TemperatureChart();
|
||||||
|
|
||||||
GtkWidget* getWidget() const { return drawingArea; }
|
GtkWidget* getWidget() const { return drawingArea; }
|
||||||
@ -48,7 +48,7 @@ private:
|
|||||||
void redraw();
|
void redraw();
|
||||||
void updateThemeColors();
|
void updateThemeColors();
|
||||||
|
|
||||||
static gboolean onTick(gpointer userData);
|
|
||||||
static void onLeave(GtkEventControllerMotion *motion, gpointer userData);
|
static void onLeave(GtkEventControllerMotion *motion, gpointer userData);
|
||||||
|
|
||||||
struct NearestPoint {
|
struct NearestPoint {
|
||||||
|
|||||||
@ -5,27 +5,20 @@ window {
|
|||||||
|
|
||||||
.small-entry {
|
.small-entry {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
min-height: 0;
|
padding: 1px;
|
||||||
min-width: 0;
|
|
||||||
padding: 0px 1px;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-entry:focus {
|
|
||||||
background: rgba(255,255,255,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.temp-label-small {
|
.temp-label-small {
|
||||||
font-size: 9px;
|
font-size: 10px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-left: 1px;
|
margin-left: 2px;
|
||||||
color: #ccc;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compact box items */
|
|
||||||
.legend-item {
|
.legend-item {
|
||||||
margin: 0 4px;
|
margin: 0 5px;
|
||||||
padding: 0;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,13 +13,18 @@ ConfigManager::ConfigManager()
|
|||||||
|
|
||||||
std::string ConfigManager::getConfigFilePath() const
|
std::string ConfigManager::getConfigFilePath() const
|
||||||
{
|
{
|
||||||
|
if (!cachedConfigPath.empty()) {
|
||||||
|
return cachedConfigPath;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the directory of the executable using /proc/self/exe
|
// Get the directory of the executable using /proc/self/exe
|
||||||
char path[1024];
|
char path[1024];
|
||||||
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
|
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
|
||||||
|
|
||||||
if (len == -1) {
|
if (len == -1) {
|
||||||
// Fallback to current directory
|
// Fallback to current directory
|
||||||
return "./nvme-monitor.conf";
|
cachedConfigPath = "./nvme-monitor.conf";
|
||||||
|
return cachedConfigPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
path[len] = '\0';
|
path[len] = '\0';
|
||||||
@ -29,10 +34,12 @@ std::string ConfigManager::getConfigFilePath() const
|
|||||||
size_t lastSlash = fullPath.find_last_of('/');
|
size_t lastSlash = fullPath.find_last_of('/');
|
||||||
if (lastSlash != std::string::npos) {
|
if (lastSlash != std::string::npos) {
|
||||||
std::string exeDir = fullPath.substr(0, lastSlash);
|
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()
|
void ConfigManager::load()
|
||||||
@ -61,6 +68,7 @@ void ConfigManager::load()
|
|||||||
value.erase(0, value.find_first_not_of(" \t"));
|
value.erase(0, value.find_first_not_of(" \t"));
|
||||||
value.erase(value.find_last_not_of(" \t") + 1);
|
value.erase(value.find_last_not_of(" \t") + 1);
|
||||||
|
|
||||||
|
try {
|
||||||
if (key == "window_width") {
|
if (key == "window_width") {
|
||||||
windowWidth = std::stoi(value);
|
windowWidth = std::stoi(value);
|
||||||
} else if (key == "window_height") {
|
} else if (key == "window_height") {
|
||||||
@ -72,6 +80,9 @@ void ConfigManager::load()
|
|||||||
} else if (key.find("name_") == 0) {
|
} else if (key.find("name_") == 0) {
|
||||||
sensorNames[key.substr(5)] = value;
|
sensorNames[key.substr(5)] = value;
|
||||||
}
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
std::cerr << "Config error: invalid value for '" << key << "': " << value << " (" << e.what() << ")" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|||||||
@ -51,19 +51,13 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
gtk_init();
|
gtk_init();
|
||||||
|
|
||||||
std::cerr << "GTK Initialized" << std::endl;
|
|
||||||
|
|
||||||
// Register resources containing the application icon
|
// Register resources containing the application icon
|
||||||
GResource *resource = resources_get_resource();
|
GResource *resource = resources_get_resource();
|
||||||
g_resources_register(resource);
|
g_resources_register(resource);
|
||||||
|
|
||||||
std::cerr << "Resources registered" << std::endl;
|
|
||||||
|
|
||||||
gMainLoop = g_main_loop_new(nullptr, FALSE);
|
gMainLoop = g_main_loop_new(nullptr, FALSE);
|
||||||
|
|
||||||
std::cerr << "Creating MainWindow" << std::endl;
|
|
||||||
MainWindow *window = new MainWindow();
|
MainWindow *window = new MainWindow();
|
||||||
std::cerr << "MainWindow created" << std::endl;
|
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
// Run the main event loop
|
// Run the main event loop
|
||||||
|
|||||||
@ -9,11 +9,10 @@
|
|||||||
|
|
||||||
MainWindow::MainWindow()
|
MainWindow::MainWindow()
|
||||||
: window(nullptr), statusLabel(nullptr), refreshRateSpinBox(nullptr),
|
: window(nullptr), statusLabel(nullptr), refreshRateSpinBox(nullptr),
|
||||||
chart(nullptr), monitor(nullptr), config(nullptr), timerID(0),
|
timerID(0), refreshRateSec(3)
|
||||||
refreshRateSec(3)
|
|
||||||
{
|
{
|
||||||
monitor = new TempMonitor();
|
monitor = std::make_unique<TempMonitor>();
|
||||||
config = new ConfigManager();
|
config = std::make_unique<ConfigManager>();
|
||||||
config->load();
|
config->load();
|
||||||
|
|
||||||
refreshRateSec = config->getPollingTime();
|
refreshRateSec = config->getPollingTime();
|
||||||
@ -35,15 +34,8 @@ MainWindow::~MainWindow()
|
|||||||
if (timerID) {
|
if (timerID) {
|
||||||
g_source_remove(timerID);
|
g_source_remove(timerID);
|
||||||
}
|
}
|
||||||
if (monitor) {
|
|
||||||
delete monitor;
|
|
||||||
}
|
|
||||||
if (chart) {
|
|
||||||
delete chart;
|
|
||||||
}
|
|
||||||
if (config) {
|
if (config) {
|
||||||
config->save();
|
config->save();
|
||||||
delete config;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +87,8 @@ void MainWindow::setupUI()
|
|||||||
|
|
||||||
// Refresh rate spinner
|
// Refresh rate spinner
|
||||||
GtkAdjustment *adjustment = gtk_adjustment_new(refreshRateSec, 1, 60, 1, 5, 0);
|
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);
|
g_signal_connect(refreshRateSpinBox, "value-changed", G_CALLBACK(onRefreshRateChanged), this);
|
||||||
gtk_box_append(GTK_BOX(controlBox), GTK_WIDGET(refreshRateSpinBox));
|
gtk_box_append(GTK_BOX(controlBox), GTK_WIDGET(refreshRateSpinBox));
|
||||||
|
|
||||||
@ -118,16 +111,17 @@ void MainWindow::setupUI()
|
|||||||
gtk_box_append(GTK_BOX(mainBox), controlBox);
|
gtk_box_append(GTK_BOX(mainBox), controlBox);
|
||||||
|
|
||||||
// Chart
|
// Chart
|
||||||
chart = new TemperatureChart();
|
chart = std::make_unique<TemperatureChart>();
|
||||||
gtk_box_append(GTK_BOX(mainBox), chart->getWidget());
|
gtk_box_append(GTK_BOX(mainBox), chart->getWidget());
|
||||||
gtk_widget_set_vexpand(chart->getWidget(), TRUE);
|
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);
|
legendBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||||
|
|
||||||
GtkWidget *legendScroll = gtk_scrolled_window_new();
|
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_policy(GTK_SCROLLED_WINDOW(legendScroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
|
||||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(legendScroll), legendBox);
|
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(legendScroll), legendBox);
|
||||||
|
gtk_widget_set_vexpand(legendScroll, FALSE);
|
||||||
gtk_widget_set_margin_top(legendScroll, 2);
|
gtk_widget_set_margin_top(legendScroll, 2);
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(mainBox), legendScroll);
|
gtk_box_append(GTK_BOX(mainBox), legendScroll);
|
||||||
@ -199,7 +193,6 @@ void MainWindow::saveWindowState()
|
|||||||
|
|
||||||
void MainWindow::updateTemperatures()
|
void MainWindow::updateTemperatures()
|
||||||
{
|
{
|
||||||
std::cerr << "updateTemperatures starting" << std::endl;
|
|
||||||
auto allTemps = monitor->getAllTemperatures();
|
auto allTemps = monitor->getAllTemperatures();
|
||||||
|
|
||||||
// Get current real time in milliseconds since epoch
|
// Get current real time in milliseconds since epoch
|
||||||
@ -213,14 +206,10 @@ void MainWindow::updateTemperatures()
|
|||||||
const std::string &device = deviceEntry.first;
|
const std::string &device = deviceEntry.first;
|
||||||
const auto &temps = deviceEntry.second;
|
const auto &temps = deviceEntry.second;
|
||||||
|
|
||||||
std::cerr << "Processing device: " << device << " with " << temps.size() << " sensors" << std::endl;
|
|
||||||
|
|
||||||
for (const auto &tempEntry : temps) {
|
for (const auto &tempEntry : temps) {
|
||||||
const std::string &sensorName = tempEntry.first;
|
const std::string &sensorName = tempEntry.first;
|
||||||
double temperature = tempEntry.second;
|
double temperature = tempEntry.second;
|
||||||
|
|
||||||
std::cerr << " Sensor: " << sensorName << " Temp: " << temperature << std::endl;
|
|
||||||
|
|
||||||
if (chart->addTemperatureData(device, sensorName, temperature, currentTime)) {
|
if (chart->addTemperatureData(device, sensorName, temperature, currentTime)) {
|
||||||
needsLegendUpdate = true;
|
needsLegendUpdate = true;
|
||||||
|
|
||||||
@ -297,7 +286,7 @@ void MainWindow::updateLegend()
|
|||||||
GtkColorDialog *dialog = gtk_color_dialog_new();
|
GtkColorDialog *dialog = gtk_color_dialog_new();
|
||||||
GtkWidget *colorButton = gtk_color_dialog_button_new(dialog);
|
GtkWidget *colorButton = gtk_color_dialog_button_new(dialog);
|
||||||
gtk_color_dialog_button_set_rgba(GTK_COLOR_DIALOG_BUTTON(colorButton), &color);
|
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
|
// Store series id in user data
|
||||||
g_object_set_data_full(G_OBJECT(colorButton), "series-name", g_strdup(seriesId.c_str()), g_free);
|
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
|
// Create editable label for series name
|
||||||
GtkWidget *entry = gtk_entry_new();
|
GtkWidget *entry = gtk_entry_new();
|
||||||
gtk_editable_set_text(GTK_EDITABLE(entry), displayName.c_str());
|
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_widget_add_css_class(entry, "small-entry");
|
||||||
|
gtk_editable_set_width_chars(GTK_EDITABLE(entry), 8);
|
||||||
|
|
||||||
// Store series id in user data
|
// Store series id in user data
|
||||||
g_object_set_data_full(G_OBJECT(entry), "series-name", g_strdup(seriesId.c_str()), g_free);
|
g_object_set_data_full(G_OBJECT(entry), "series-name", g_strdup(seriesId.c_str()), g_free);
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
TemperatureChart::TemperatureChart(GtkWidget *parent)
|
TemperatureChart::TemperatureChart()
|
||||||
: drawingArea(nullptr), tooltipWindow(nullptr), tooltipLabel(nullptr),
|
: drawingArea(nullptr), tooltipWindow(nullptr), tooltipLabel(nullptr),
|
||||||
maxDataPoints(600), tickHandler(0),
|
maxDataPoints(600), tickHandler(0),
|
||||||
minTemp(MIN_TEMP), maxTemp(MAX_TEMP), minTime(0), maxTime(0),
|
minTemp(MIN_TEMP), maxTemp(MAX_TEMP), minTime(0), maxTime(0),
|
||||||
@ -52,8 +52,15 @@ TemperatureChart::TemperatureChart(GtkWidget *parent)
|
|||||||
|
|
||||||
setupColors();
|
setupColors();
|
||||||
|
|
||||||
// Setup tick callback for redrawing
|
// Listen for theme changes
|
||||||
tickHandler = g_timeout_add(100, onTick, this);
|
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()
|
TemperatureChart::~TemperatureChart()
|
||||||
@ -203,9 +210,35 @@ bool TemperatureChart::addTemperatureData(const std::string &device, const std::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update temperature range
|
// 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;
|
minTemp = MIN_TEMP;
|
||||||
maxTemp = MAX_TEMP;
|
maxTemp = MAX_TEMP;
|
||||||
|
}
|
||||||
|
|
||||||
// Update time range - keep 10 minute window
|
// Update time range - keep 10 minute window
|
||||||
maxTime = timestamp;
|
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<TemperatureChart*>(userData);
|
|
||||||
gtk_widget_queue_draw(self->drawingArea);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TemperatureChart::drawChart(GtkDrawingArea *area, cairo_t *cr, int width, int height)
|
void TemperatureChart::drawChart(GtkDrawingArea *area, cairo_t *cr, int width, int height)
|
||||||
{
|
{
|
||||||
// Update theme colors before drawing
|
|
||||||
updateThemeColors();
|
|
||||||
|
|
||||||
// Background with theme color
|
// Background with theme color
|
||||||
cairo_set_source_rgb(cr, bgColor.red, bgColor.green, bgColor.blue);
|
cairo_set_source_rgb(cr, bgColor.red, bgColor.green, bgColor.blue);
|
||||||
cairo_paint(cr);
|
cairo_paint(cr);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user