summaryrefslogtreecommitdiffstats
path: root/heatload.cc
diff options
context:
space:
mode:
Diffstat (limited to 'heatload.cc')
-rw-r--r--heatload.cc447
1 files changed, 447 insertions, 0 deletions
diff --git a/heatload.cc b/heatload.cc
new file mode 100644
index 0000000..21b8787
--- /dev/null
+++ b/heatload.cc
@@ -0,0 +1,447 @@
+#include <gtkmm.h>
+#include <gdkmm.h>
+
+#include <deque>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#define METER_WIDTH 300
+#define METER_HEIGHT 100
+
+class MeterStruct {
+
+protected:
+ std::deque<gint> data_deque;
+ glong last_time;
+ bool modified;
+
+public:
+ MeterStruct() : last_time(0), modified(true) {};
+
+ void add_value(gint b);
+ bool is_modified() { return modified; }
+ void reset_modified() { modified = false; }
+
+ void draw(const Glib::RefPtr<Gdk::Drawable> &d, const Glib::RefPtr<const Gdk::GC> &g);
+};
+
+void MeterStruct::add_value(gint b) {
+ GTimeVal tv;
+ glong n;
+
+ g_get_current_time(&tv);
+
+ if (last_time == 0)
+ n = 1;
+ else if (last_time == tv.tv_sec) {
+ data_deque.pop_front();
+ n = 1;
+ } else
+ n = tv.tv_sec - last_time;
+
+ last_time = tv.tv_sec;
+
+ for (; n > 0; n --)
+ data_deque.push_back(b);
+
+ while (data_deque.size() > METER_WIDTH)
+ data_deque.pop_front();
+
+ modified = true;
+}
+
+void MeterStruct::draw(const Glib::RefPtr<Gdk::Drawable> &d, const Glib::RefPtr<const Gdk::GC> &g) {
+ std::deque<gint> copy = data_deque;
+
+ int width, height;
+ d->get_size(width, height);
+
+ gint lx = -1, ly = -1;
+ for (gint x = width-1; x >= 0; x--) {
+ if (copy.size() == 0)
+ break;
+
+ gint b = copy.back();
+ gint y = (100-b)*(height-1)/100;
+
+ if (lx == -1)
+ d->draw_point(g, x, y);
+ else
+ d->draw_line(g, lx, ly, x, y);
+
+ lx = x;
+ ly = y;
+
+ copy.pop_back();
+ }
+}
+
+MeterStruct load_meter;
+MeterStruct temperature_meter;
+
+class GraphDrawingArea : public Gtk::DrawingArea {
+
+public:
+ GraphDrawingArea();
+
+ void refresh_pixmap();
+
+protected:
+ virtual bool on_expose_event(GdkEventExpose* event);
+
+ Glib::RefPtr<Gdk::Pixmap> pixmap;
+
+ Gdk::Color color0;
+ Gdk::Color color1;
+ Gdk::Color color2;
+};
+
+GraphDrawingArea::GraphDrawingArea():
+ Gtk::DrawingArea(), color0("black"), color1("green"), color2("tomato") {
+ set_size_request(METER_WIDTH, METER_HEIGHT);
+}
+
+bool GraphDrawingArea::on_expose_event(GdkEventExpose*) {
+
+ if (!pixmap)
+ refresh_pixmap();
+
+ if (pixmap) {
+ Glib::RefPtr<Gdk::Window> win = get_window();
+ Glib::RefPtr<Gdk::GC> gc = get_style()->get_fg_gc(get_state());
+ win->draw_drawable(gc, pixmap, 0, 0, 0, 0);
+ }
+
+ return true;
+}
+
+void GraphDrawingArea::refresh_pixmap() {
+ Glib::RefPtr<Gdk::Window> win = get_window();
+
+ if (!pixmap || load_meter.is_modified() || temperature_meter.is_modified()) {
+ int width, height, width2, height2;
+
+ load_meter.reset_modified();
+ temperature_meter.reset_modified();
+
+ win->get_size(width, height);
+ if (pixmap)
+ pixmap->get_size(width2, height2);
+
+ if (!pixmap || width != width2 || height != height2) {
+ pixmap = Gdk::Pixmap::create(win, width, height);
+
+ Glib::RefPtr<Gdk::Colormap> cmap = win->get_colormap();
+ cmap->alloc_color(color0);
+ cmap->alloc_color(color1);
+ cmap->alloc_color(color2);
+ }
+
+ Glib::RefPtr<Gdk::GC> gc = Gdk::GC::create(pixmap);
+
+ gc->set_foreground(color0);
+ pixmap->draw_rectangle(gc, TRUE, 0, 0, width, height);
+
+ gc->set_foreground(color1);
+ load_meter.draw(pixmap, gc);
+
+ gc->set_foreground(color2);
+ temperature_meter.draw(pixmap, gc);
+ }
+}
+
+class MainWindow : public Gtk::Window {
+
+protected:
+
+ Gtk::VBox vbox;
+ Gtk::Label label;
+ Gtk::Label cpu_label, temperature_label;
+ Gtk::Frame frame;
+ GraphDrawingArea drawing_area;
+ Gtk::HBox hbox;
+
+public:
+ MainWindow();
+
+ virtual void refresh();
+ virtual void update_values(gint v1, gint v2);
+ virtual bool on_delete_event(GdkEventAny* event);
+};
+
+
+MainWindow::MainWindow():
+ vbox(false, 5),
+ hbox(true, 5) {
+
+ set_border_width(5);
+ set_title("Heatload");
+
+ label.set_markup(/*"<b><span size=\"x-large\" face=\"Serif\">Heatload "VERSION"</span></b>\n"*/
+ "<span foreground=\"darkgreen\">Green</span>: CPU Load; "
+ "<span foreground=\"darkred\">Red</span>: ACPI CPU Temperature\n"
+ "<i>(Ranges from 0% to 100%, resp. 0&#176;C to 100&#176;C)</i>");
+
+ label.set_justify(Gtk::JUSTIFY_CENTER);
+ label.set_alignment(.5, .5);
+
+ cpu_label.set_justify(Gtk::JUSTIFY_CENTER);
+ cpu_label.set_alignment(0, 0);
+
+ temperature_label.set_justify(Gtk::JUSTIFY_LEFT);
+ temperature_label.set_alignment(0, 0);
+
+ frame.set_shadow_type(Gtk::SHADOW_IN);
+ frame.add(drawing_area);
+
+ vbox.pack_start(label, false, true);
+ vbox.pack_start(frame, true, true);
+
+ hbox.pack_start(cpu_label, false, true);
+ hbox.pack_start(temperature_label, false, true);
+
+ vbox.pack_start(hbox, false, true);
+
+ add(vbox);
+
+ show_all();
+}
+
+void MainWindow::refresh() {
+ drawing_area.refresh_pixmap();
+
+ Gdk::Rectangle r;
+ r.set_x(0);
+ r.set_y(0);
+ r.set_width(drawing_area.get_width());
+ r.set_height(drawing_area.get_height());
+
+ drawing_area.get_window()->invalidate_rect(r, false);
+}
+
+bool MainWindow::on_delete_event(GdkEventAny* event) {
+ Gtk::Main::quit();
+ return Gtk::Window::on_delete_event(event);
+}
+
+void MainWindow::update_values(gint v1, gint v2) {
+ char txt[256];
+ static gint ov1 = -1;
+ static gint ov2 = -1;
+
+ if (ov1 != v1) {
+ snprintf(txt, sizeof(txt), "<span foreground=\"darkgreen\">CPU Load</span>: %i%%", v1);
+ cpu_label.set_markup(txt);
+ ov1 = v1;
+ }
+
+ if (ov2 != v2) {
+ snprintf(txt, sizeof(txt), "<span foreground=\"darkred\">CPU Temperature</span>: %i&#176;C", v2);
+ temperature_label.set_markup(txt);
+ ov2 = v2;
+ }
+
+}
+
+void failure(const Glib::ustring &txt) {
+ static Gtk::MessageDialog msgbox("Could not read data from /proc file system:\n<b>"+txt+"</b>", true, Gtk::MESSAGE_ERROR);
+ static bool running = false;
+
+ if (running)
+ return;
+
+ running = true;
+ msgbox.run();
+ running = false;
+}
+
+gint get_load_value() {
+ char txt[256];
+ char*p;
+ static int ct[2][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };
+ static int n = 0;
+ int d[4];
+ int t, fd;
+ float v;
+ static FILE*f = NULL;
+
+ if (!f && !(f = fopen("/proc/stat", "r"))) {
+ failure("Unable to open kernel statistics file.");
+ goto fail;
+ }
+
+ if (!(p = fgets(txt, sizeof(txt), f))) {
+ failure("Unable to read from kernel statistics file.");
+ goto fail;
+ }
+
+ fd = dup(fileno(f));
+ fclose(f);
+ f = fdopen(fd, "r");
+ g_assert(f);
+ fseek(f, 0, SEEK_SET);
+
+ if (strlen(p) <= 5) {
+ failure("Parse failure");
+ goto fail;
+ }
+
+ sscanf(p+5, "%u %u %u %u", &ct[n][0], &ct[n][1], &ct[n][2], &ct[n][3]);
+
+ t = 0;
+
+ for (int i = 0; i < 4; i++)
+ t += (d[i] = abs(ct[n][i] - ct[1-n][i]));
+
+ v = (t - d[3])/(float) t;
+
+ n = 1-n;
+
+ return (gint) (v*100);
+
+fail:
+ if (f) {
+ fclose(f);
+ f = NULL;
+ }
+
+ return -1;
+}
+
+FILE * fopen_first(const char *pfx, const char *sfx, const char *m) {
+ g_assert(pfx);
+ g_assert(sfx);
+ g_assert(m);
+
+ DIR *dir;
+ struct dirent *de;
+ char fn[PATH_MAX];
+
+ if (!(dir = opendir(pfx)))
+ return NULL;
+
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] != '.') {
+ FILE *f;
+ snprintf(fn, sizeof(fn), "%s/%s/%s", pfx, de->d_name, sfx);
+
+ if ((f = fopen(fn, m))) {
+ closedir(dir);
+ return f;
+ }
+
+ break;
+ }
+ }
+
+ closedir(dir);
+ return NULL;
+}
+
+gint get_temperature_value() {
+ char txt[256];
+ char*p;
+ int v, fd;
+ static FILE*f = NULL;
+ static gboolean old_acpi = FALSE;
+
+ if (!f) {
+ if (!(f = fopen_first("/proc/acpi/thermal_zone", "temperature", "r"))) {
+ if (!(f = fopen_first("/proc/acpi/thermal", "status", "r"))) {
+ failure("Unable to open ACPI temperature file.");
+ goto fail;
+ }
+
+ old_acpi = TRUE;
+ }
+ }
+
+ if (!(p = fgets(txt, sizeof(txt), f))) {
+ failure("Unable to read data from ACPI temperature file.");
+ goto fail;
+ }
+
+ fd = dup(fileno(f));
+ fclose(f);
+ f = fdopen(fd, "r");
+ g_assert(f);
+ fseek(f, 0, SEEK_SET);
+
+ if (!old_acpi) {
+ if (strlen(p) > 20)
+ v = atoi(p+20);
+ else
+ v = 0;
+ } else {
+ if (strlen(p) > 15)
+ v = atoi(p+15);
+ else
+ v = 0;
+ v=((v-2732)/10); /* convert from deciKelvin to degrees Celcius */
+ }
+
+ if (v > 100) v = 100;
+ if (v < 0) v = 0;
+
+ return v;
+
+fail:
+ if (f) {
+ fclose(f);
+ f = NULL;
+ }
+
+ return -1;
+}
+
+static MainWindow *mainWindow = NULL;
+
+int get_values() {
+ gint v1, v2;
+ static bool failed = false;
+
+ if (failed ||
+ (v1 = get_load_value()) < 0 ||
+ (v2 = get_temperature_value()) < 0)
+ goto fail;
+
+ load_meter.add_value(v1);
+ temperature_meter.add_value(v2);
+
+ if (mainWindow) {
+ mainWindow->update_values((guint) v1, (guint) v2);
+ mainWindow->refresh();
+ }
+
+ return 0;
+
+fail:
+ failed = true;
+ return -1;
+}
+
+bool timer_proc() {
+ if (get_values() < 0) {
+ Gtk::Main::quit();
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[]) {
+ Gtk::Main kit(argc, argv);
+
+ if (get_values() < 0)
+ return 1;
+
+ MainWindow window;
+ mainWindow = &window;
+ Glib::signal_timeout().connect(sigc::ptr_fun(&timer_proc), 1000);
+
+ kit.run(window);
+
+ return 0;
+}