/*
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1