/* * wmbsdbatt - A dockapp to monitor ACPI battery status on FreeBSD * Copyright (C) 2003 Lukas Ertl <l.ertl@univie.ac.at> * Based on work by Florian Krohs <krohs@uni.de> * Copyright (C) 2003 Florian Krohs <krohs@uni.de> * Based on work by Thomas Nemeth <tnemeth@free.fr> * Copyright (C) 2002 Thomas Nemeth <tnemeth@free.fr> * and on work by Seiichi SATO <ssato@sh.rim.or.jp> * Copyright (C) 2001,2002 Seiichi SATO <ssato@sh.rim.or.jp> * and on work by Mark Staggs <me@markstaggs.net> * Copyright (C) 2002 Mark Staggs <me@markstaggs.net> * 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. * * $Id: wmbsdbatt.c,v 1.1 2003/12/23 18:27:34 le Exp $ */ #include <errno.h> #include <err.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/sysctl.h> #include <X11/Xlib.h> #include <X11/xpm.h> #include <X11/extensions/shape.h> #include "dockapp.h" #include "backlight_on.xpm" #include "backlight_off.xpm" #include "parts.xpm" #define PACKAGE "wmbsdbatt" #define SIZE 58 #define WINDOWED_BG ". c #AEAAAE" #define TOGGLESPEED 2 #define UPDATE_INTERVAL 5 #define RATE 0 #define TEMP 1 #define DISCHARGING 0x0001 #define CHARGING 0x0002 #define CRITICAL 0x0004 #define UNKNOWN 0 struct acpiinfo { int acline; int battery_status; int battery_life; int battery_time; int temperature; int low; #if 0 int capacity; int rate; #endif }; struct acpiinfo acpi_info; static char *sysctl_battery_state = "hw.acpi.battery.state"; static char *sysctl_battery_life = "hw.acpi.battery.life"; static char *sysctl_battery_time = "hw.acpi.battery.time"; static char *sysctl_temperature = "hw.acpi.thermal.tz0.temperature"; static char *sysctl_acline = "hw.acpi.acline"; typedef enum { LIGHTOFF, LIGHTON } light; Pixmap pixmap; Pixmap backdrop_on; Pixmap backdrop_off; Pixmap parts; Pixmap mask; static char *display_name = ""; static char light_color[256] = ""; /* back-light color */ static unsigned update_interval = UPDATE_INTERVAL; static unsigned alarm_level_temp = 70; static int animationspeed = 500; static light backlight = LIGHTOFF; int count; int blink_pos = 0; #if 0 static int mode = TEMP; #endif /* prototypes */ void update(void); void switch_light(void); void draw_remaining_time(void); void draw_batt(void); void draw_low(void); void draw_temp(void); void draw_statusdigit(void); void draw_pcgraph(void); void blink_batt(void); void draw_all(void); int init_stats(void); int acpi_exists(void); #if 0 void draw_rate(void); #endif int main(int argc, char **argv) { int charging, ncolor, show; long timeout; long animation_timeout, update_timeout; struct sigaction sa; XEvent event; XpmColorSymbol colors[2] = { {"Back0", NULL, 0}, {"Back1", NULL, 0} }; ncolor = show = 0; update_timeout = update_interval * 1000; animation_timeout = animationspeed; sa.sa_handler = SIG_IGN; sa.sa_flags = SA_NOCLDWAIT; sigemptyset(&sa.sa_mask); sigaction(SIGCHLD, &sa, NULL); /* Check for ACPI support */ if (!acpi_exists()) { fprintf(stderr, "Unable to access ACPI info\n"); exit(1); } dockapp_open_window(display_name, PACKAGE, SIZE, SIZE, argc, argv); dockapp_set_eventmask(ButtonPressMask); if (strcmp(light_color,"")) { colors[0].pixel = dockapp_getcolor(light_color); colors[1].pixel = dockapp_blendedcolor(light_color, -24, -24, -24, 1.0); ncolor = 2; } /* change raw xpm data to pixmap */ if (dockapp_iswindowed) backlight_on_xpm[1] = backlight_off_xpm[1] = WINDOWED_BG; if (!dockapp_xpm2pixmap(backlight_on_xpm, &backdrop_on, &mask, colors, ncolor)) { fprintf(stderr, "Error initializing backlit background image.\n"); exit(1); } if (!dockapp_xpm2pixmap(backlight_off_xpm, &backdrop_off, NULL, NULL, 0)) { fprintf(stderr, "Error initializing background image.\n"); exit(1); } if (!dockapp_xpm2pixmap(parts_xpm, &parts, NULL, colors, ncolor)) { fprintf(stderr, "Error initializing parts image.\n"); exit(1); } /* shape window */ if (!dockapp_iswindowed) dockapp_setshape(mask, 0, 0); if (mask) XFreePixmap(display, mask); /* pixmap : draw area */ pixmap = dockapp_XCreatePixmap(SIZE, SIZE); /* Initialize pixmap */ if (backlight == LIGHTON) dockapp_copyarea(backdrop_on, pixmap, 0, 0, SIZE, SIZE, 0, 0); else dockapp_copyarea(backdrop_off, pixmap, 0, 0, SIZE, SIZE, 0, 0); dockapp_set_background(pixmap); dockapp_show(); update(); /* Main loop */ while (1) { if (acpi_info.battery_status == CHARGING) charging = 1; else charging = 0; timeout = update_timeout; if (dockapp_nextevent_or_timeout(&event, timeout)) { /* Next Event */ switch (event.type) { case ButtonPress: switch (event.xbutton.button) { case 1: switch_light(); draw_all(); break; #if 0 case 3: mode = !mode; draw_all(); break; #endif default: break; } break; default: break; } } else { /* Time Out */ update_timeout -= timeout; animation_timeout -= timeout; if (animation_timeout <= 0) { animation_timeout = animationspeed; if (charging) { show = 1; } } if (update_timeout <= 0) { update(); show = 1; update_timeout = update_interval * 1000; } if (show) { /* show */ draw_all(); if (charging) { blink_pos--; } dockapp_copy2window(pixmap); show = 0; } } } return 0; } int init_stats(void) { u_long addr; size_t len; len = sizeof(addr); if (sysctlbyname(sysctl_battery_life, &addr, &len, NULL, 0) == -1) err(1, "sysctlbyname(\"%s\")", sysctl_battery_life); acpi_info.battery_life = addr; if (sysctlbyname(sysctl_battery_time, &addr, &len, NULL, 0) == -1) err(1, "sysctlbyname(\"%s\")", sysctl_battery_time); if (addr == -1) acpi_info.battery_time = 0; else acpi_info.battery_time = addr; if (sysctlbyname(sysctl_battery_state, &addr, &len, NULL, 0) == -1) err(1, "sysctlbyname(\"%s\")", sysctl_battery_state); acpi_info.battery_status = addr; if (sysctlbyname(sysctl_temperature, &addr, &len, NULL, 0) == -1) err(1, "sysctlbyname(\"%s\")", sysctl_temperature); /* ACPI temperature comes is tenth of Kelvin. */ addr /= 10; addr -= 273; acpi_info.temperature = addr; if (sysctlbyname(sysctl_acline, &addr, &len, NULL, 0) == -1) err(1, "sysctlbyname(\"%s\")", sysctl_acline); acpi_info.acline = addr; /* XXX: Is there a better way to say "battery is low"? */ if (acpi_info.acline == 0 && acpi_info.battery_life < 5) acpi_info.low = 1; else acpi_info.low = 0; return 0; } int acpi_exists(void) { /* For now. */ return 1; } /* called by timer */ void update(void) { static light pre_backlight; static int in_alarm_mode = 0; /* get current battery usage in percent */ init_stats(); /* alarm mode */ if (acpi_info.low || (acpi_info.temperature > alarm_level_temp)) { if (!in_alarm_mode) { in_alarm_mode = 1; pre_backlight = backlight; } if (backlight != pre_backlight) { switch_light(); return; } } else if (in_alarm_mode) { in_alarm_mode = 0; if (backlight != pre_backlight) { switch_light(); return; } } draw_all(); } /* called when mouse button pressed */ void switch_light(void) { switch (backlight) { case LIGHTOFF: backlight = LIGHTON; dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0); break; case LIGHTON: backlight = LIGHTOFF; dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0); break; } } void draw_all(void) { /* all clear */ if (backlight == LIGHTON) dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0); else dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0); /* draw digit */ draw_remaining_time(); draw_statusdigit(); draw_pcgraph(); #if 0 if (mode == RATE) draw_rate(); else if (mode == TEMP) draw_temp(); #endif draw_temp(); if (acpi_info.battery_status == CHARGING) blink_batt(); if (acpi_info.low) draw_low(); draw_batt(); /* show */ dockapp_copy2window(pixmap); } void draw_batt(void) { int y; if (backlight == LIGHTON) y = 28; else y = 0; if (acpi_info.battery_status == DISCHARGING) dockapp_copyarea(parts, pixmap, 33 + y , 63, 9, 5, 16, 39); } void draw_remaining_time(void) { int hrs_left, min_left, y; hrs_left = acpi_info.battery_time / 60; min_left = acpi_info.battery_time % 60; if (backlight == LIGHTON) y = 20; else y = 0; if (acpi_info.acline == 1 && !(acpi_info.battery_status == CHARGING)) { dockapp_copyarea(parts, pixmap, 0, 68 + 68 + y, 10, 20, 17, 5); dockapp_copyarea(parts, pixmap, 10, 68 + 68 + y, 10, 20, 32, 5); } else { dockapp_copyarea(parts, pixmap, (hrs_left / 10) * 10, 68 + y, 10, 20, 5, 5); dockapp_copyarea(parts, pixmap, (hrs_left % 10) * 10, 68 + y, 10, 20, 17, 5); dockapp_copyarea(parts, pixmap, (min_left / 10) * 10, 68 + y, 10, 20, 32, 5); dockapp_copyarea(parts, pixmap, (min_left % 10) * 10, 68 + y, 10, 20, 44, 5); } } #if 0 void draw_rate(void) { int light_offset; long rate = acpi_info.rate; if (backlight == LIGHTON) light_offset = 50; else light_offset = 0; dockapp_copyarea(parts, pixmap, (rate/10000)*5 + light_offset, 40, 5, 9, 5, 46); dockapp_copyarea(parts, pixmap, ((rate/1000)%10)*5 + light_offset, 40, 5, 9, 11, 46); dockapp_copyarea(parts, pixmap, ((rate/100)%10)*5 + light_offset, 40, 5, 9, 17, 46); dockapp_copyarea(parts, pixmap, ((rate/10)%10)*5 + light_offset, 40, 5, 9, 23, 46); dockapp_copyarea(parts, pixmap, (rate%10)*5 + light_offset, 40, 5, 9, 29, 46); dockapp_copyarea(parts, pixmap, 0 + light_offset, 49, 5, 9, 36, 46); //m dockapp_copyarea(parts, pixmap, 5 + light_offset, 49, 5, 9, 42, 46); //W } #endif void draw_temp(void) { int light_offset=0; int temp = acpi_info.temperature; if (backlight == LIGHTON) light_offset = 50; else light_offset = 0; if (temp < 0 || temp > 99) temp = 0; dockapp_copyarea(parts, pixmap, (temp / 10) * 5 + light_offset, 40, 5, 9, 23, 46); dockapp_copyarea(parts, pixmap, (temp % 10) * 5 + light_offset, 40, 5, 9, 29, 46); /* '°C'. */ dockapp_copyarea(parts, pixmap, 10 + light_offset, 49, 5, 9, 36, 46); dockapp_copyarea(parts, pixmap, 15 + light_offset, 49, 5, 9, 42, 46); } void draw_statusdigit(void) { int light_offset; if (backlight == LIGHTON) light_offset = 28; else light_offset = 0; if (acpi_info.acline == 1) dockapp_copyarea(parts, pixmap, 33 + light_offset, 58, 9, 5, 5, 39); } void draw_pcgraph(void) { int width; int light_offset; int percent; if (backlight == LIGHTON) light_offset = 5; else light_offset = 0; percent = acpi_info.battery_life; width = (percent * 32) / 100; dockapp_copyarea(parts, pixmap, 0, 58 + light_offset, width, 5, 5, 26); /* Don't display leading zero. */ if (percent == 100) dockapp_copyarea(parts, pixmap, 4 * (percent / 100), 126 + light_offset, 3, 5, 38, 26); /* Don't display leading zero. */ if (percent > 9) dockapp_copyarea(parts, pixmap, 4 * ((percent % 100) / 10), 126 + light_offset, 3, 5, 42, 26); dockapp_copyarea(parts, pixmap, 4 * (percent % 10), 126 + light_offset, 3, 5, 46, 26); } void blink_batt(void) { int light_offset = 0; if (backlight == LIGHTON) light_offset = 50; else light_offset = 0; blink_pos = (blink_pos + 1) % 5; if (acpi_info.battery_status == CHARGING) dockapp_copyarea(parts, pixmap, blink_pos * 9 + light_offset, 117, 9, 5, 16, 39); } void draw_low(void) { int y; if (backlight == LIGHTON) y = 28; else y = 0; dockapp_copyarea(parts, pixmap, 42 + y, 58, 17, 7, 38, 38); }