2011-03-25 18:45:13 +00:00
/* apm/acpi dockapp - phear it 1.34
* Copyright ( C ) 2000 , 2001 , 2002 timecop @ japan . co . jp
*
* 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
2014-08-18 22:56:10 +00:00
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
2011-03-25 18:45:13 +00:00
*/
# define _GNU_SOURCE
2014-08-18 22:56:21 +00:00
# include <dockapp.h>
2011-03-25 18:45:13 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <unistd.h>
# include <time.h>
# include <X11/X.h>
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/extensions/shape.h>
# include <X11/xpm.h>
2014-08-18 22:56:10 +00:00
# include "libacpi.h"
2014-08-18 22:56:14 +00:00
# include "wmacpi.h"
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:28 +00:00
# define WMACPI_VER "2.2rc3"
2011-03-25 18:45:13 +00:00
/* main pixmap */
# ifdef LOW_COLOR
# include "master_low.xpm"
2014-08-18 22:56:21 +00:00
static char * * master_xpm = master_low_xpm ;
2011-03-25 18:45:13 +00:00
# else
# include "master.xpm"
# endif
2014-08-18 22:56:21 +00:00
struct dockapp {
Display * display ; /* display */
Window win ; /* main window */
Pixmap pixmap ; /* main pixmap */
Pixmap mask ; /* mask pixmap */
2011-03-25 18:45:13 +00:00
Pixmap text ; /* pixmap for text scroller */
2014-08-18 22:56:24 +00:00
unsigned short width ; /* width of pixmap */
unsigned short height ; /* height of pixmap */
2014-08-18 22:56:21 +00:00
int screen ; /* current screen */
2011-03-25 18:45:13 +00:00
int tw ; /* text width inside text pixmap */
int update ; /* need to redraw? */
2014-08-18 22:56:11 +00:00
int blink ; /* should we blink the LED? (critical battery) */
2014-08-18 22:56:16 +00:00
int bell ; /* bell on critical low, or not? */
2014-08-18 22:56:18 +00:00
int scroll ; /* scroll message text? */
2014-08-18 22:56:20 +00:00
int scroll_reset ; /* reset the scrolling text */
2014-08-18 22:56:28 +00:00
int percent ;
2014-08-18 22:56:21 +00:00
} ;
2011-03-25 18:45:13 +00:00
/* globals */
2014-08-18 22:56:21 +00:00
struct dockapp * dockapp ;
/* global_t *globals; */
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:19 +00:00
/* this gives us a variable scroll rate, depending on the importance of the
* message . . . */
# define DEFAULT_SCROLL_RESET 150;
int scroll_reset = DEFAULT_SCROLL_RESET ;
2014-08-18 22:56:11 +00:00
2014-08-18 22:56:21 +00:00
/* copy a chunk of pixmap around the app */
static void copy_xpm_area ( int x , int y , int w , int h , int dx , int dy )
{
XCopyArea ( DADisplay , dockapp - > pixmap , dockapp - > pixmap ,
DAGC , x , y , w , h , dx , dy ) ;
dockapp - > update = 1 ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:10 +00:00
/* display AC power symbol */
static void display_power_glyph ( void )
{
copy_xpm_area ( 67 , 38 , 12 , 7 , 6 , 17 ) ;
}
/* get rid of AC power symbol */
static void kill_power_glyph ( void )
{
copy_xpm_area ( 67 , 48 , 12 , 7 , 6 , 17 ) ;
}
/* display battery symbol */
static void display_battery_glyph ( void )
{
copy_xpm_area ( 82 , 38 , 12 , 7 , 20 , 17 ) ;
}
/* get rid of battery symbol */
static void kill_battery_glyph ( void )
{
copy_xpm_area ( 82 , 48 , 12 , 7 , 20 , 17 ) ;
}
2014-08-18 22:56:11 +00:00
/* clear the time display */
static void clear_time_display ( void )
{
copy_xpm_area ( 114 , 76 , 31 , 11 , 7 , 32 ) ;
}
/* set time display to -- -- */
static void invalid_time_display ( void )
{
2014-08-18 22:56:28 +00:00
copy_xpm_area ( 122 , 14 , 31 , 11 , 7 , 32 ) ;
2014-08-18 22:56:11 +00:00
}
2014-08-18 22:56:20 +00:00
static void reset_scroll ( void ) {
dockapp - > scroll_reset = 1 ;
}
2014-08-18 22:56:21 +00:00
static void clear_text_area ( void ) {
copy_xpm_area ( 66 , 9 , 52 , 7 , 6 , 50 ) ;
}
2011-03-25 18:45:13 +00:00
static void redraw_window ( void )
{
if ( dockapp - > update ) {
XCopyArea ( dockapp - > display , dockapp - > pixmap , dockapp - > win ,
2014-08-18 22:56:21 +00:00
DAGC , 0 , 0 , 64 , 64 , 0 , 0 ) ;
2011-03-25 18:45:13 +00:00
dockapp - > update = 0 ;
}
}
2014-08-18 22:56:21 +00:00
static void new_window ( char * display , char * name , int argc , char * * argv )
2011-03-25 18:45:13 +00:00
{
2014-08-18 22:56:27 +00:00
XSizeHints * hints ;
2014-08-18 22:56:21 +00:00
/* Initialise the dockapp window and appicon */
2014-08-18 22:56:26 +00:00
DAOpenDisplay ( display , argc , argv ) ;
DACreateIcon ( name , 64 , 64 , argc , argv ) ;
2014-08-18 22:56:21 +00:00
dockapp - > display = DADisplay ;
dockapp - > win = DAWindow ;
2011-03-25 18:45:13 +00:00
XSelectInput ( dockapp - > display , dockapp - > win ,
2014-08-18 22:56:21 +00:00
ExposureMask | ButtonPressMask | ButtonReleaseMask |
StructureNotifyMask ) ;
/* create the main pixmap . . . */
DAMakePixmapFromData ( master_xpm , & dockapp - > pixmap , & dockapp - > mask ,
& dockapp - > width , & dockapp - > height ) ;
DASetPixmap ( dockapp - > pixmap ) ;
DASetShape ( dockapp - > mask ) ;
2011-03-25 18:45:13 +00:00
/* text area is 318x7, or 53 characters long */
dockapp - > text = XCreatePixmap ( dockapp - > display , dockapp - > win , 318 , 7 ,
DefaultDepth ( dockapp - > display ,
dockapp - > screen ) ) ;
if ( ! dockapp - > text ) {
2014-08-18 22:56:14 +00:00
pfatal ( " FATAL: Cannot create text scroll pixmap! \n " ) ;
2011-03-25 18:45:13 +00:00
exit ( 1 ) ;
}
2014-08-18 22:56:27 +00:00
/* force the window to stay this size - otherwise the user could
* resize us and see our panties ^ Wmaster pixmap . . . */
hints = XAllocSizeHints ( ) ;
if ( hints ) {
hints - > flags | = PMinSize | PMaxSize ;
hints - > min_width = 64 ;
hints - > max_width = 64 ;
hints - > min_height = 64 ;
hints - > max_height = 64 ;
XSetWMNormalHints ( dockapp - > display , dockapp - > win , hints ) ;
XFree ( hints ) ;
}
2014-08-18 22:56:21 +00:00
DAShow ( ) ;
}
static void copy_to_text_buffer ( int sx , int sy , int w , int h , int dx , int dy )
{
XCopyArea ( dockapp - > display , dockapp - > pixmap , dockapp - > text ,
DAGC , sx , sy , w , h , dx , dy ) ;
}
static void copy_to_text_area ( int sx , int sy , int w , int h , int dx , int dy )
{
XCopyArea ( dockapp - > display , dockapp - > text , dockapp - > pixmap ,
DAGC , sx , sy , w , h , dx , dy ) ;
}
static void scroll_text ( void )
{
static int start , end , stop ;
int x = 6 ; /* x coord of the start of the text area */
int y = 50 ; /* y coord */
2014-08-18 22:56:28 +00:00
int width = 51 ; /* width of the text area */
2014-08-18 22:56:21 +00:00
int height = 7 ; /* height of the text area */
int tw = dockapp - > tw ; /* width of the rendered text */
int sx , dx , w ;
if ( ! dockapp - > scroll )
return ;
/*
* Conceptually this is viewing the text through a scrolling
* window - the window starts out with the end immediately before
* the text , and stops when the start of the window is immediately
* after the end of the text .
*
* We begin with the start of the window at pixel ( 0 - width ) and
* as we scroll we render only the portion of the window above
* pixel 0. The destination of the copy during this period starts
* out at the end of the text area and works left as more of the
* text is being copied , until a full window is being copied .
*
* As the end of the window moves out past the end of the text , we
* want to keep the destination at the beginning of the text area ,
* but copy a smaller and smaller chunk of the text . Eventually the
* start of the window will scroll past the end of the text , at
* which point we stop doing any work and wait to be reset .
*/
if ( dockapp - > scroll_reset ) {
start = 0 - width ;
end = 0 ;
stop = 0 ;
clear_text_area ( ) ;
dockapp - > scroll_reset = 0 ;
}
if ( stop )
return ;
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:21 +00:00
w = 52 ;
if ( end < 52 )
w = end ;
else if ( end > tw )
w = 52 - ( end - tw ) ;
dx = x + 52 - w ;
if ( end > tw )
dx = x ;
sx = start ;
if ( start < 0 )
sx = 0 ;
if ( start > tw )
stop = 1 ;
clear_text_area ( ) ;
copy_to_text_area ( sx , 0 , w , height , dx , y ) ;
start + = 2 ;
end + = 2 ;
dockapp - > update = 1 ;
2011-03-25 18:45:13 +00:00
}
static void render_text ( char * string )
{
int i , c , k ;
2014-08-18 22:56:18 +00:00
/* drop out immediately if scrolling is disabled - we don't render
* any text at all , since there ' s not much else we could do
* sensibly without scrolling . */
if ( ! dockapp - > scroll )
return ;
2011-03-25 18:45:13 +00:00
if ( strlen ( string ) > 53 )
return ;
/* prepare the text area by clearing it */
for ( i = 0 ; i < 54 ; i + + ) {
2014-08-18 22:56:21 +00:00
copy_to_text_buffer ( 133 , 57 , 6 , 8 , i * 6 , 0 ) ;
2011-03-25 18:45:13 +00:00
}
k = 0 ;
for ( i = 0 ; string [ i ] ; i + + ) {
c = toupper ( string [ i ] ) ;
if ( c > = ' A ' & & c < = ' Z ' ) { /* letter */
c = c - ' A ' ;
2014-08-18 22:56:21 +00:00
copy_to_text_buffer ( c * 6 , 67 , 6 , 7 , k , 0 ) ;
2011-03-25 18:45:13 +00:00
} else if ( c > = ' 0 ' & & c < = ' 9 ' ) { /* number */
c = c - ' 0 ' ;
2014-08-18 22:56:21 +00:00
copy_to_text_buffer ( c * 6 + 66 , 58 , 6 , 7 , k , 0 ) ;
2011-03-25 18:45:13 +00:00
} else if ( c = = ' . ' ) {
2014-08-18 22:56:21 +00:00
copy_to_text_buffer ( 140 , 58 , 6 , 7 , k , 0 ) ;
2011-03-25 18:45:13 +00:00
} else if ( c = = ' - ' ) {
2014-08-18 22:56:21 +00:00
copy_to_text_buffer ( 126 , 58 , 6 , 7 , k , 0 ) ;
2011-03-25 18:45:13 +00:00
}
k + = 6 ;
}
dockapp - > tw = k ; /* length of text segment */
/* re-scroll the message */
2014-08-18 22:56:20 +00:00
reset_scroll ( ) ;
scroll_text ( ) ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:28 +00:00
static void clear_percentage ( void )
{
/* clear the number */
copy_xpm_area ( 95 , 47 , 21 , 9 , 37 , 16 ) ;
/* clear the bar */
copy_xpm_area ( 66 , 18 , 54 , 8 , 5 , 5 ) ;
dockapp - > percent = - 1 ;
}
2011-03-25 18:45:13 +00:00
static void display_percentage ( int percent )
{
unsigned int bar ;
2014-08-18 22:56:21 +00:00
int width = 54 ; /* width of the bar */
float ratio = 100.0 / width ; /* ratio between the current percentage
* and the number of pixels in the bar */
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:11 +00:00
if ( percent = = - 1 )
percent = 0 ;
2014-08-18 22:56:28 +00:00
if ( dockapp - > percent = = percent )
2011-03-25 18:45:13 +00:00
return ;
if ( percent < 0 )
percent = 0 ;
if ( percent > 100 )
percent = 100 ;
2014-08-18 22:56:28 +00:00
if ( dockapp - > percent = = - 1 )
copy_xpm_area ( 127 , 28 , 5 , 7 , 52 , 17 ) ;
2011-03-25 18:45:13 +00:00
if ( percent < 100 ) { /* 0 - 99 */
copy_xpm_area ( 95 , 48 , 8 , 7 , 37 , 17 ) ;
if ( percent > = 10 )
copy_xpm_area ( ( percent / 10 ) * 6 + 67 , 28 , 5 , 7 , 40 , 17 ) ;
copy_xpm_area ( ( percent % 10 ) * 6 + 67 , 28 , 5 , 7 , 46 , 17 ) ;
} else
copy_xpm_area ( 95 , 37 , 21 , 9 , 37 , 16 ) ; /* 100% */
2014-08-18 22:56:28 +00:00
dockapp - > percent = percent ;
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:21 +00:00
bar = ( int ) ( ( float ) percent / ratio ) ;
2011-03-25 18:45:13 +00:00
copy_xpm_area ( 66 , 0 , bar , 8 , 5 , 5 ) ;
if ( bar < 54 )
copy_xpm_area ( 66 + bar , 18 , 54 - bar , 8 , bar + 5 , 5 ) ;
}
static void display_time ( int minutes )
{
static int ohour = - 1 , omin = - 1 ;
int hour , min , tmp ;
2014-08-18 22:56:11 +00:00
if ( minutes < = 0 ) { /* error - clear the display */
invalid_time_display ( ) ;
2011-03-25 18:45:13 +00:00
ohour = omin = - 1 ;
return ;
}
/* render time on the display */
hour = minutes / 60 ;
2014-08-18 22:56:11 +00:00
/* our display area only fits %2d:%2d, so we need to make sure
* what we ' re displaying will fit in those constraints . I don ' t
* think we ' re likely to see any batteries that do more than
* 100 hours any time soon , so it ' s fairly safe . */
if ( hour > = 100 ) {
hour = 99 ;
min = 59 ;
} else
min = minutes % 60 ;
2011-03-25 18:45:13 +00:00
if ( hour = = ohour & & min = = omin )
return ;
tmp = hour / 10 ;
copy_xpm_area ( tmp * 7 + 1 , 76 , 6 , 11 , 7 , 32 ) ;
tmp = hour % 10 ;
copy_xpm_area ( tmp * 7 + 1 , 76 , 6 , 11 , 14 , 32 ) ;
tmp = min / 10 ;
copy_xpm_area ( tmp * 7 + 1 , 76 , 6 , 11 , 25 , 32 ) ;
tmp = min % 10 ;
copy_xpm_area ( tmp * 7 + 1 , 76 , 6 , 11 , 32 , 32 ) ;
copy_xpm_area ( 71 , 76 , 3 , 11 , 21 , 32 ) ;
ohour = hour ;
omin = min ;
}
2014-08-18 22:56:10 +00:00
/*
* The reworked state handling stuff .
*/
/* set the current state of the power panel */
enum panel_states {
PS_AC ,
PS_BATT ,
PS_NULL ,
} ;
2014-08-18 22:56:11 +00:00
static void really_blink_power_glyph ( void )
{
static int counter = 0 ;
if ( counter = = 10 )
display_power_glyph ( ) ;
else if ( counter = = 20 )
kill_power_glyph ( ) ;
else if ( counter > 30 )
counter = 0 ;
counter + + ;
}
static void blink_power_glyph ( void )
{
if ( dockapp - > blink )
really_blink_power_glyph ( ) ;
}
static void really_blink_battery_glyph ( void )
{
static int counter = 0 ;
if ( counter = = 10 )
display_battery_glyph ( ) ;
else if ( counter = = 20 )
kill_battery_glyph ( ) ;
else if ( counter > 30 )
counter = 0 ;
counter + + ;
}
static void blink_battery_glyph ( void )
{
if ( dockapp - > blink )
really_blink_battery_glyph ( ) ;
}
2014-08-18 22:56:21 +00:00
static void set_power_panel ( global_t * globals )
2011-03-25 18:45:13 +00:00
{
2014-08-18 22:56:27 +00:00
static enum panel_states power = PS_NULL ;
2014-08-18 22:56:13 +00:00
battery_t * binfo = globals - > binfo ;
adapter_t * ap = & globals - > adapter ;
2014-08-18 22:56:10 +00:00
2014-08-18 22:56:11 +00:00
if ( ap - > power = = AC ) {
2014-08-18 22:56:10 +00:00
if ( power ! = PS_AC ) {
power = PS_AC ;
kill_battery_glyph ( ) ;
display_power_glyph ( ) ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:11 +00:00
} else if ( ap - > power = = BATT ) {
2014-08-18 22:56:10 +00:00
if ( power ! = PS_BATT ) {
power = PS_BATT ;
kill_power_glyph ( ) ;
display_battery_glyph ( ) ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:10 +00:00
}
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count > 0 ) {
if ( binfo - > charge_state = = CHARGE )
blink_power_glyph ( ) ;
2014-08-18 22:56:11 +00:00
2014-08-18 22:56:28 +00:00
if ( ( binfo - > state = = CRIT ) & & ( ap - > power = = BATT ) )
blink_battery_glyph ( ) ;
2014-08-18 22:56:16 +00:00
}
2014-08-18 22:56:10 +00:00
}
2014-08-18 22:56:19 +00:00
void scroll_faster ( double factor ) {
scroll_reset = scroll_reset * factor ;
}
void scroll_slower ( double factor ) {
scroll_reset = scroll_reset * factor ;
}
2014-08-18 22:56:20 +00:00
void reset_scroll_speed ( void ) {
2014-08-18 22:56:19 +00:00
scroll_reset = DEFAULT_SCROLL_RESET ;
}
2014-08-18 22:56:10 +00:00
/*
* The message that needs to be displayed needs to be decided
* according to a heirarchy : a message like not present needs to take
* precedence over a global thing like the current power status , and
* something like a low battery warning should take precedence over
* the " on battery " message . Likewise , a battery charging message
* needs to take precedence over the on ac power message . The other
* question is how much of a precedence local messages should take
* over global ones . . .
*
* So , there are three possible sets of messages : not present , on - line
* and off - line messages . We need to decide which of those sets is
* appropriate right now , and then decide within them .
*/
enum messages {
2014-08-18 22:56:28 +00:00
M_NB , /* no batteries */
2014-08-18 22:56:10 +00:00
M_NP , /* not present */
M_AC , /* on ac power */
M_CH , /* battery charging */
M_BATT , /* on battery */
M_LB , /* low battery */
M_CB , /* critical low battery */
2014-08-18 22:56:11 +00:00
M_HCB , /* battery reported critical capacity state */
2014-08-18 22:56:10 +00:00
M_NULL , /* empty starting state */
} ;
2014-08-18 22:56:21 +00:00
static void set_message ( global_t * globals )
2014-08-18 22:56:10 +00:00
{
static enum messages state = M_NULL ;
2014-08-18 22:56:13 +00:00
battery_t * binfo = globals - > binfo ;
adapter_t * ap = & globals - > adapter ;
2014-08-18 22:56:10 +00:00
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count = = 0 ) {
if ( state ! = M_NB ) {
state = M_NB ;
reset_scroll_speed ( ) ;
render_text ( " no batteries " ) ;
}
return ;
}
2014-08-18 22:56:10 +00:00
/* battery not present case */
if ( ! binfo - > present ) {
if ( state ! = M_NP ) {
state = M_NP ;
2014-08-18 22:56:20 +00:00
reset_scroll_speed ( ) ;
2014-08-18 22:56:10 +00:00
render_text ( " not present " ) ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:11 +00:00
} else if ( ap - > power = = AC ) {
if ( binfo - > charge_state = = CHARGE ) {
2014-08-18 22:56:10 +00:00
if ( state ! = M_CH ) {
state = M_CH ;
2014-08-18 22:56:20 +00:00
reset_scroll_speed ( ) ;
2014-08-18 22:56:10 +00:00
render_text ( " battery charging " ) ;
}
} else {
if ( state ! = M_AC ) {
state = M_AC ;
2014-08-18 22:56:20 +00:00
reset_scroll_speed ( ) ;
2014-08-18 22:56:10 +00:00
render_text ( " on ac power " ) ;
}
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:10 +00:00
} else {
if ( binfo - > state = = CRIT ) {
if ( state ! = M_CB ) {
state = M_CB ;
2014-08-18 22:56:19 +00:00
scroll_faster ( 0.75 ) ;
2014-08-18 22:56:10 +00:00
render_text ( " critical low battery " ) ;
}
} else if ( binfo - > state = = LOW ) {
if ( state ! = M_LB ) {
state = M_LB ;
2014-08-18 22:56:19 +00:00
scroll_faster ( 0.85 ) ;
2014-08-18 22:56:10 +00:00
render_text ( " low battery " ) ;
2011-03-25 18:45:13 +00:00
}
} else {
2014-08-18 22:56:10 +00:00
if ( state ! = M_BATT ) {
state = M_BATT ;
2014-08-18 22:56:20 +00:00
reset_scroll_speed ( ) ;
2014-08-18 22:56:10 +00:00
render_text ( " on battery " ) ;
2011-03-25 18:45:13 +00:00
}
}
2014-08-18 22:56:10 +00:00
}
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:21 +00:00
void set_time_display ( global_t * globals )
2014-08-18 22:56:11 +00:00
{
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count = = 0 ) {
invalid_time_display ( ) ;
return ;
}
2014-08-18 22:56:22 +00:00
if ( globals - > binfo - > charge_state = = CHARGE )
display_time ( globals - > binfo - > charge_time ) ;
else if ( globals - > binfo - > charge_state = = DISCHARGE )
2014-08-18 22:56:13 +00:00
display_time ( globals - > rtime ) ;
2014-08-18 22:56:11 +00:00
else
invalid_time_display ( ) ;
}
2014-08-18 22:56:28 +00:00
void clear_batt_id_area ( void )
{
copy_xpm_area ( 125 , 40 , 7 , 11 , 51 , 32 ) ;
}
2014-08-18 22:56:10 +00:00
void set_batt_id_area ( int bno )
{
2014-08-18 22:56:21 +00:00
int w = 7 ; /* Width of the number */
int h = 11 ; /* Height of the number */
int dx = 50 ; /* x coord of the target area */
2014-08-18 22:56:28 +00:00
int dy = 32 ; /* y coord of the target area */
2014-08-18 22:56:21 +00:00
int sx = ( bno + 1 ) * 7 ; /* source x coord */
int sy = 76 ; /* source y coord */
copy_xpm_area ( sx , sy , w , h , dx , dy ) ;
2014-08-18 22:56:10 +00:00
}
2014-08-18 22:56:27 +00:00
# define VERSION "wmacpi version " WMACPI_VER "\nUsing libacpi version " LIBACPI_VER
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:21 +00:00
void cli_wmacpi ( global_t * globals , int samples )
2014-08-18 22:56:14 +00:00
{
2014-08-18 22:56:16 +00:00
int i , j , sleep_time = 0 ;
2014-08-18 22:56:14 +00:00
battery_t * binfo ;
adapter_t * ap ;
2014-08-18 22:56:22 +00:00
pdebug ( " samples: %d \n " , samples ) ;
2014-08-18 22:56:16 +00:00
if ( samples > 1 )
sleep_time = 1000000 / samples ;
2014-08-18 22:56:14 +00:00
/* we want to acquire samples over some period of time, so . . . */
for ( i = 0 ; i < samples + 2 ; i + + ) {
2014-08-18 22:56:19 +00:00
for ( j = 0 ; j < globals - > battery_count ; j + + )
2014-08-18 22:56:21 +00:00
acquire_batt_info ( globals , j ) ;
acquire_global_info ( globals ) ;
2014-08-18 22:56:14 +00:00
usleep ( sleep_time ) ;
}
ap = & globals - > adapter ;
if ( ap - > power = = AC ) {
printf ( " On AC Power " ) ;
2014-08-18 22:56:19 +00:00
for ( i = 0 ; i < globals - > battery_count ; i + + ) {
2014-08-18 22:56:14 +00:00
binfo = & batteries [ i ] ;
if ( binfo - > present & & ( binfo - > charge_state = = CHARGE ) ) {
printf ( " ; Battery %s charging " , binfo - > name ) ;
printf ( " , currently at %2d%% " , binfo - > percentage ) ;
if ( binfo - > charge_time > = 0 )
printf ( " , %2d:%02d remaining " ,
binfo - > charge_time / 60 ,
binfo - > charge_time % 60 ) ;
}
}
printf ( " \n " ) ;
} else if ( ap - > power = = BATT ) {
printf ( " On Battery " ) ;
2014-08-18 22:56:19 +00:00
for ( i = 0 ; i < globals - > battery_count ; i + + ) {
2014-08-18 22:56:14 +00:00
binfo = & batteries [ i ] ;
if ( binfo - > present & & ( binfo - > percentage > = 0 ) )
printf ( " , Battery %s at %d%% " , binfo - > name ,
binfo - > percentage ) ;
}
if ( globals - > rtime > = 0 )
printf ( " ; %d:%02d remaining " , globals - > rtime / 60 ,
globals - > rtime % 60 ) ;
printf ( " \n " ) ;
}
return ;
}
2014-08-18 22:56:28 +00:00
battery_t * switch_battery ( global_t * globals , int battno )
{
globals - > binfo = & batteries [ battno ] ;
pinfo ( " changing to monitor battery %s \n " , globals - > binfo - > name ) ;
set_batt_id_area ( battno ) ;
dockapp - > update = 1 ;
return globals - > binfo ;
}
2011-03-25 18:45:13 +00:00
int main ( int argc , char * * argv )
{
char * display = NULL ;
2014-08-18 22:56:19 +00:00
int sample_count = 0 ;
int batt_reinit , ac_reinit ;
int batt_count = 0 ;
int ac_count = 0 ;
2014-08-18 22:56:27 +00:00
int cli = 0 , samples = 1 , critical = 10 ;
2014-08-18 22:56:19 +00:00
int samplerate = 20 ;
int sleep_rate = 10 ;
int sleep_time = 1000000 / sleep_rate ;
int scroll_count = 0 ;
2014-08-18 22:56:21 +00:00
enum rtime_mode rt_mode = RT_RATE ;
2014-08-18 22:56:22 +00:00
int rt_forced = 0 ;
2014-08-18 22:56:28 +00:00
battery_t * binfo = NULL ;
2014-08-18 22:56:21 +00:00
global_t * globals ;
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:27 +00:00
DAProgramOption options [ ] = {
{ " -r " , " --no-scroll " , " disable scrolling message " , DONone , False , { NULL } } ,
{ " -n " , " --no-blink " , " disable blinking of various UI elements " , DONone , False , { NULL } } ,
{ " -x " , " --cmdline " , " run in command line mode " , DONone , False , { NULL } } ,
{ " -f " , " --force-capacity-mode " , " force the use of capacity mode for calculating time remaining " , DONone , False , { NULL } } ,
{ " -d " , " --display " , " display or remote display " , DOString , False , { & display } } ,
{ " -c " , " --critical " , " set critical low alarm at <number> percent \n (default: 10 percent) " , DONatural , False , { & critical } } ,
{ " -m " , " --battery " , " battery number to monitor " , DONatural , False , { & battery_no } } ,
{ " -s " , " --sample-rate " , " number of times per minute to sample battery information \n default 20 (once every three seconds) " , DONatural , False , { & samplerate } } ,
{ " -V " , " --verbosity " , " Set verbosity " , DONatural , False , { & verbosity } } ,
{ " -a " , " --samples " , " number of samples to average over (cli mode only) " , DONatural , False , { & samples } } ,
} ;
2014-08-18 22:56:21 +00:00
dockapp = calloc ( 1 , sizeof ( struct dockapp ) ) ;
2014-08-18 22:56:13 +00:00
globals = calloc ( 1 , sizeof ( global_t ) ) ;
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:11 +00:00
dockapp - > blink = 1 ;
2014-08-18 22:56:16 +00:00
dockapp - > bell = 0 ;
2014-08-18 22:56:18 +00:00
dockapp - > scroll = 1 ;
2014-08-18 22:56:20 +00:00
dockapp - > scroll_reset = 0 ;
2014-08-18 22:56:13 +00:00
globals - > crit_level = 10 ;
2014-08-18 22:56:10 +00:00
battery_no = 1 ;
2011-03-25 18:45:13 +00:00
2014-08-18 22:56:19 +00:00
/* after this many samples, we reinit the battery and AC adapter
* information .
* XXX : make these configurable . . . */
batt_reinit = 100 ;
ac_reinit = 1000 ;
/* this needs to be up here because we need to know what batteries
* are available / before / we can decide if the battery we want to
* monitor is available . */
2011-03-25 18:45:13 +00:00
/* parse command-line options */
2014-08-18 22:56:27 +00:00
DAParseArguments ( argc , argv , options , 10 ,
" A battery monitor dockapp for ACPI based systems " ,
VERSION ) ;
if ( options [ 0 ] . used )
dockapp - > scroll = 0 ;
if ( options [ 1 ] . used )
dockapp - > blink = 0 ;
if ( options [ 2 ] . used )
cli = 1 ;
if ( options [ 3 ] . used ) {
rt_mode = RT_CAP ;
rt_forced = 1 ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:27 +00:00
if ( samplerate = = 0 ) samplerate = 1 ;
if ( samplerate > 600 ) samplerate = 600 ;
if ( critical > 100 ) {
fprintf ( stderr , " Please use values between 0 and 100%% \n " ) ;
fprintf ( stderr , " Using default value of 10%% \n " ) ;
critical = 10 ;
}
globals - > crit_level = critical ;
if ( battery_no > = MAXBATT ) {
fprintf ( stderr , " Please specify a battery number below %d \n " , MAXBATT ) ;
return 1 ;
}
pinfo ( " Monitoring battery %d \n " , battery_no ) ;
2014-08-18 22:56:21 +00:00
if ( power_init ( globals ) )
2014-08-18 22:56:15 +00:00
/* power_init functions handle printing error messages */
exit ( 1 ) ;
2014-08-18 22:56:21 +00:00
globals - > rt_mode = rt_mode ;
2014-08-18 22:56:22 +00:00
globals - > rt_forced = rt_forced ;
2014-08-18 22:56:21 +00:00
2014-08-18 22:56:19 +00:00
if ( battery_no > globals - > battery_count ) {
2014-08-18 22:56:28 +00:00
pinfo ( " Battery %d not available for monitoring. \n " , battery_no ) ;
2014-08-18 22:56:19 +00:00
}
2014-08-18 22:56:14 +00:00
/* check for cli mode */
if ( cli ) {
2014-08-18 22:56:21 +00:00
cli_wmacpi ( globals , samples ) ;
2014-08-18 22:56:14 +00:00
exit ( 0 ) ;
}
2014-08-18 22:56:22 +00:00
/* check to see if we've got a valid DISPLAY env variable, as a simple check to see if
* we ' re running under X */
if ( ! getenv ( " DISPLAY " ) ) {
pdebug ( " Not running under X - using cli mode \n " ) ;
cli_wmacpi ( globals , samples ) ;
exit ( 0 ) ;
}
2014-08-18 22:56:14 +00:00
2014-08-18 22:56:10 +00:00
battery_no - - ;
2011-03-25 18:45:13 +00:00
/* make new dockapp window */
2014-08-18 22:56:12 +00:00
/* Don't even /think/ of asking me why, but if I set the window name to
2014-08-18 22:56:16 +00:00
* " acpi " , the app refuses to dock properly - it ' s just plain / weird / .
* So , wmacpi it is . . . */
2014-08-18 22:56:21 +00:00
new_window ( display , " wmacpi " , argc , argv ) ;
2011-03-25 18:45:13 +00:00
/* get initial statistics */
2014-08-18 22:56:21 +00:00
acquire_all_info ( globals ) ;
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count > 0 ) {
binfo = & batteries [ battery_no ] ;
globals - > binfo = binfo ;
set_batt_id_area ( battery_no ) ;
pinfo ( " monitoring battery %s \n " , binfo - > name ) ;
}
2014-08-18 22:56:11 +00:00
clear_time_display ( ) ;
2014-08-18 22:56:21 +00:00
set_power_panel ( globals ) ;
set_message ( globals ) ;
2011-03-25 18:45:13 +00:00
/* main loop */
while ( 1 ) {
2014-08-18 22:56:27 +00:00
Atom atom ;
Atom wmdelwin ;
2011-03-25 18:45:13 +00:00
XEvent event ;
while ( XPending ( dockapp - > display ) ) {
XNextEvent ( dockapp - > display , & event ) ;
switch ( event . type ) {
case Expose :
/* update */
dockapp - > update = 1 ;
while ( XCheckTypedEvent ( dockapp - > display , Expose , & event ) ) ;
redraw_window ( ) ;
break ;
case DestroyNotify :
XCloseDisplay ( dockapp - > display ) ;
exit ( 0 ) ;
break ;
case ButtonPress :
break ;
case ButtonRelease :
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count = = 0 )
break ;
2014-08-18 22:56:10 +00:00
/* cycle through the known batteries. */
battery_no + + ;
2014-08-18 22:56:19 +00:00
battery_no = battery_no % globals - > battery_count ;
2014-08-18 22:56:28 +00:00
binfo = switch_battery ( globals , battery_no ) ;
2011-03-25 18:45:13 +00:00
break ;
2014-08-18 22:56:27 +00:00
case ClientMessage :
/* what /is/ this crap?
* Turns out that libdockapp adds the WM_DELETE_WINDOW atom to
* the WM_PROTOCOLS property for the window , which means that
* rather than get a simple DestroyNotify message , we get a
* nice little message from the WM saying " hey, can you delete
* yourself , pretty please ? " . So, when running as a window
* rather than an icon , we ' re impossible to kill in a friendly
* manner , because we ' re expecting to die from a DestroyNotify
* and thus blithely ignoring the WM knocking on our window
* border . . .
*
* This simply checks for that scenario - it may fail oddly if
* something else comes to us via a WM_PROTOCOLS ClientMessage
* event , but I suspect it ' s not going to be an issue . */
wmdelwin = XInternAtom ( dockapp - > display , " WM_DELETE_WINDOW " , 1 ) ;
atom = event . xclient . data . l [ 0 ] ;
if ( atom = = wmdelwin ) {
XCloseDisplay ( dockapp - > display ) ;
exit ( 0 ) ;
}
break ;
2011-03-25 18:45:13 +00:00
}
}
2014-08-18 22:56:12 +00:00
/* XXX: some laptops have problems with sampling the battery
* regularly - apparently , the BIOS disables interrupts while
* reading from the battery , which is generally on a slow bus
* and is a slow device , so you get significant periods without
* interrupts . This causes interactivity to suffer . . .
*
* My proposed workaround is to allow the user to set the sample
* rate - it defaults to ten , but can be set lower ( or higher ) .
*
* The only problem with this is that we need to sample less
* frequently , while still allowing the app to update normally .
* That means calling redraw_window ( ) and all the set_ * ( ) functions
* normally , but only calling acquire_all_info ( ) every so often .
* As it stands , we only call acquire_all_info ( ) once every three
* seconds ( once every thirty updates ) . . . I ' m not entirely sure
* / how / this could cause interactivity problems , but hey . . .
*
* So , given the base rate of once every three seconds , we want to
* change this test to . . . */
2014-08-18 22:56:19 +00:00
/* Okay, this needs /fixing/ - it's ridiculous. We should be giving
* the user the option of saying how many times per minute the
* battery should be sampled , defaulting to 20 times .
*
* We sleep for one tenth of a second at a time , so 60 seconds
* translates to 600 sleeps . So , we change the default sample
* rate to 20 , and the calculation below becomes . . . */
if ( sample_count + + = = ( ( sleep_rate * 60 ) / samplerate ) ) {
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count = = 0 ) {
batt_count = 0 ;
reinit_batteries ( globals ) ;
/* battery appeared */
if ( globals - > battery_count > 0 ) {
if ( battery_no > globals - > battery_count )
battery_no = 0 ;
binfo = switch_battery ( globals , battery_no ) ;
}
}
2014-08-18 22:56:21 +00:00
acquire_all_info ( globals ) ;
2014-08-18 22:56:19 +00:00
/* we need to be able to reinitialise batteries and adapters, because
* they change - you can hotplug batteries on most laptops these days
* and who knows what kind of shit will be happening soon . . . */
if ( batt_count + + > = batt_reinit ) {
2014-08-18 22:56:21 +00:00
if ( reinit_batteries ( globals ) )
2014-08-18 22:56:19 +00:00
pfatal ( " Oh my god, the batteries are gone! \n " ) ;
batt_count = 0 ;
}
if ( ac_count + + > = ac_reinit ) {
2014-08-18 22:56:21 +00:00
if ( reinit_ac_adapters ( globals ) )
2014-08-18 22:56:19 +00:00
pfatal ( " What happened to our AC adapters?!? \n " ) ;
ac_count = 0 ;
}
sample_count = 0 ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:19 +00:00
if ( scroll_count + + > = scroll_reset ) {
2014-08-18 22:56:20 +00:00
reset_scroll ( ) ;
2014-08-18 22:56:19 +00:00
scroll_count = 0 ;
2011-03-25 18:45:13 +00:00
}
2014-08-18 22:56:19 +00:00
/* The old code had some kind of weird crap with timers and the like.
2014-08-18 22:56:10 +00:00
* As far as I can tell , it ' s meaningless - the time we want to display
* is the time calculated from the remaining capacity , as per the
* ACPI spec . The only thing I ' d change is the handling of a charging
* state : my best guess , based on the behaviour I ' m seeing with my
* Lifebook , is that the present rate value when charging is the rate
* at which the batteries are being charged , which would mean I ' d just
* need to reverse the rtime calculation to be able to work out how
* much time remained until the batteries were fully charged . . .
* That would be rather useful , though given it would vary rather a lot
* it seems likely that it ' d be little more than a rough guesstimate . */
2014-08-18 22:56:21 +00:00
set_time_display ( globals ) ;
set_power_panel ( globals ) ;
set_message ( globals ) ;
2014-08-18 22:56:28 +00:00
if ( globals - > battery_count = = 0 ) {
clear_percentage ( ) ;
clear_batt_id_area ( ) ;
} else
display_percentage ( binfo - > percentage ) ;
2014-08-18 22:56:20 +00:00
scroll_text ( ) ;
2011-03-25 18:45:13 +00:00
/* redraw_window, if anything changed - determined inside
* redraw_window . */
redraw_window ( ) ;
2014-08-18 22:56:19 +00:00
usleep ( sleep_time ) ;
2011-03-25 18:45:13 +00:00
}
return 0 ;
}