From e0d00f97992c9b167b18bfb6601c547a2ae8e0c4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 1 Jan 2005 18:01:20 +0000 Subject: add heatload git-svn-id: file:///home/lennart/svn/public/heatload/heatload@3 3bca8330-beed-0310-b360-ea58cfc96e4b --- Makefile | 46 ++++++ README.in | 71 ++++++++++ RELEASES | 8 ++ heatload.cc | 461 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 586 insertions(+) create mode 100644 Makefile create mode 100644 README.in create mode 100644 RELEASES create mode 100644 heatload.cc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2f96dd2 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +VERSION=0.3 +CXXFLAGS=-Wall -W -pipe -O2 -g -DVERSION=\"$(VERSION)\" `pkg-config --cflags gtkmm-2.4 gdkmm-2.4` +TAR=heatload-$(VERSION).tar.gz +SOURCES=heatload.cc +BINARY=heatload +LIBS=`pkg-config --libs gtkmm-2.4 gdkmm-2.4` +PACKAGE=heatload + +all: $(BINARY) + +$(BINARY): $(SOURCES) + $(CXX) $(CXXFLAGS) $(SOURCES) -o $(BINARY) $(LIBS) + +strip: $(BINARY) + @du -h $(BINARY) + strip -s $(BINARY) + @du -h $(BINARY) + +clean: + rm -f $(BINARY) + rm -f *~ *.bak *.tar.gz + +install: strip + install -g root -o root -m 555 $(BINARY) /usr/local/bin + +deinstall: + rm -f /usr/local/bin/$(BINARY) + +README: README.in + sed s/VERSION/$(VERSION)/ < README.in > README + +index.html: README + txt2html -t "$(PACKAGE) $(VERSION)" < README > index.html + +html: index.html + +$(TAR): clean README + tar -C.. --exclude=$(PACKAGE)/$(TAR) -czvf $(TAR) $(PACKAGE) + +tar: $(TAR) + +web: $(TAR) README + cp README $(TAR) ../../homepage/lennart/projects/$(PACKAGE)/ && $(MAKE) -C ../../homepage/lennart/projects/$(PACKAGE) + +upload: web + $(MAKE) -C ../../homepage/lennart upload diff --git a/README.in b/README.in new file mode 100644 index 0000000..6755576 --- /dev/null +++ b/README.in @@ -0,0 +1,71 @@ +heatload VERSION - CPU Load and ACPI Temperature Monitor +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Copyright 2002 +Lennart Poettering + +--------------------------------------------------------------------- + +This program 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. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +--------------------------------------------------------------------- + +This Gnome utility produces nice looking realtime graphs for the +current CPU load and ACPI temperature of your Linux machine. It is +nice for monitoring the direct relation of the former to the latter. + +The reason I developed this program was that the ACPI implementation +of my laptop seems to be broken, and I needed a simple way to +determine when the machines became too hot. + +Requirements +------------ + +As of version 0.1 you need a Linux kernel 2.4 with a fairly recent +ACPI patch. (I used 20020308) Without this patch, the utility will not +recognize your temperature sensor. Starting with 0.2, I added support +for plain 2.4.18-Kernels (and some kernel versions prior) with ACPI +20011018 based on a patch by Ian Morgan. lm_sensor-devices are not +supported. (Somebody wants to send me a patch?) + +For compiling you need a recent g++ with Gtk-- and Gnome-- +installed. I did my development with 1.2.8 resp. 1.2.2. + +Development +----------- + +Development was done under Debian GNU Linux Woody for i386 from March +2002. The used machine was a Medion 9580-F laptop. + +Installation +------------ + +Run "make" for compiling the program. You might want to install it +permanently on your system by issuing "make install" as root. This +install the heatload executable to /usr/local/bin/. For removing this +installation you might want to try "make deinstall" as root. + +Internet +-------- + +You may find up to date releases of this utility on + http://www.stud.uni-hamburg.de/users/lennart/projects/heatload/ + +You may download this release from + http://www.stud.uni-hamburg.de/users/lennart/projects/heatload/heatload-VERSION.tar.gz + +--------------------------------------------------------------------- + +Lennart Poettering , 2002 diff --git a/RELEASES b/RELEASES new file mode 100644 index 0000000..136e155 --- /dev/null +++ b/RELEASES @@ -0,0 +1,8 @@ +heatload 0.2: + + Compilation fixes for libsigc++ on Ximian + Support for ACPI 20011018 + +heatload 0.1: + + Initial Release diff --git a/heatload.cc b/heatload.cc new file mode 100644 index 0000000..c541e83 --- /dev/null +++ b/heatload.cc @@ -0,0 +1,461 @@ +#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* event) { + + 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, hbox2; + Gtk::Button refresh_button; + Gtk::Button quit_button; + +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), + hbox2(true, 5), + refresh_button("Refresh"), + quit_button("Quit") { + + 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); + + quit_button.signal_clicked().connect(sigc::ptr_fun(&Gtk::Main::quit)); + refresh_button.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::refresh)); + + frame.add(drawing_area); + + vbox.pack_start(label, false, true); + vbox.pack_start(frame, true, true); + + hbox2.pack_start(cpu_label, false, true); + hbox2.pack_start(temperature_label, false, true); + + vbox.pack_start(hbox2, false, true); + + hbox.pack_start(refresh_button, false, true); + hbox.pack_start(quit_button, 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; +} -- cgit From 52d0ea0e808298d15ac8c28b64f346edc8d6c21e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 Jan 2005 18:12:57 +0000 Subject: update heatload git-svn-id: file:///home/lennart/svn/public/heatload/heatload@4 3bca8330-beed-0310-b360-ea58cfc96e4b --- heatload.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/heatload.cc b/heatload.cc index c541e83..5ae65a0 100644 --- a/heatload.cc +++ b/heatload.cc @@ -103,7 +103,7 @@ GraphDrawingArea::GraphDrawingArea(): set_size_request(METER_WIDTH, METER_HEIGHT); } -bool GraphDrawingArea::on_expose_event(GdkEventExpose* event) { +bool GraphDrawingArea::on_expose_event(GdkEventExpose*) { if (!pixmap) refresh_pixmap(); @@ -184,7 +184,7 @@ MainWindow::MainWindow(): set_border_width(5); set_title("Heatload"); - label.set_markup("Heatload "VERSION"\n" + 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)"); -- cgit From 2413614d3bc435cd09e425fa34068e4a333f0495 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Jan 2005 21:50:21 +0000 Subject: remove some useless buttons git-svn-id: file:///home/lennart/svn/public/heatload/heatload@5 3bca8330-beed-0310-b360-ea58cfc96e4b --- heatload.cc | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/heatload.cc b/heatload.cc index 5ae65a0..21b8787 100644 --- a/heatload.cc +++ b/heatload.cc @@ -161,9 +161,7 @@ protected: Gtk::Label cpu_label, temperature_label; Gtk::Frame frame; GraphDrawingArea drawing_area; - Gtk::HBox hbox, hbox2; - Gtk::Button refresh_button; - Gtk::Button quit_button; + Gtk::HBox hbox; public: MainWindow(); @@ -176,10 +174,7 @@ public: MainWindow::MainWindow(): vbox(false, 5), - hbox(true, 5), - hbox2(true, 5), - refresh_button("Refresh"), - quit_button("Quit") { + hbox(true, 5) { set_border_width(5); set_title("Heatload"); @@ -199,23 +194,14 @@ MainWindow::MainWindow(): temperature_label.set_alignment(0, 0); frame.set_shadow_type(Gtk::SHADOW_IN); - - quit_button.signal_clicked().connect(sigc::ptr_fun(&Gtk::Main::quit)); - refresh_button.signal_clicked().connect(sigc::mem_fun(this, &MainWindow::refresh)); - frame.add(drawing_area); vbox.pack_start(label, false, true); vbox.pack_start(frame, true, true); - hbox2.pack_start(cpu_label, false, true); - hbox2.pack_start(temperature_label, false, true); + hbox.pack_start(cpu_label, false, true); + hbox.pack_start(temperature_label, false, true); - vbox.pack_start(hbox2, false, true); - - hbox.pack_start(refresh_button, false, true); - hbox.pack_start(quit_button, false, true); - vbox.pack_start(hbox, false, true); add(vbox); -- cgit