/* $Id$ */ /*** This file is part of heatload. heatload is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. heatload is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with heatload; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ***/ #include #include #include #include #include #include #define METER_WIDTH 300 #define METER_HEIGHT 100 class MeterStruct { protected: std::deque 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 &d, const Glib::RefPtr &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 &d, const Glib::RefPtr &g) { std::deque 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 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 win = get_window(); Glib::RefPtr 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 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 cmap = win->get_colormap(); cmap->alloc_color(color0); cmap->alloc_color(color1); cmap->alloc_color(color2); } Glib::RefPtr 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(/*"Heatload "VERSION"\n"*/ "Green: CPU Load; " "Red: ACPI CPU Temperature\n" "(Ranges from 0% to 100%, resp. 0°C to 100°C)"); 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), "CPU Load: %i%%", v1); cpu_label.set_markup(txt); ov1 = v1; } if (ov2 != v2) { snprintf(txt, sizeof(txt), "CPU Temperature: %i°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"+txt+"", 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; }