# -*-Perl-*- # instances allowed: one # (single instance modules would be silly to use more than one of # anyway, so we use package local storage. This is faster and places # less artificial load on the machine than doing everything through # the object hash) package MGMmodule::battery; use IO::Seekable; use Socket; use vars qw(@percent @battstatus @gstate $ac $lstate $graph $widget $xpath $openp $openf $openapm $openacpi $batteries $ac $active $lastbat @acpi_battery_dirs $acpi_adapter_dir); sub module_init{ my$this=shift; my$xclass=$this->{"xclass"}; my$toplevel=$this->{"toplevel"}; $active=0; $openp=0; $openf=0; $openapm=0; $openacpi=0; $this->read_proc; if($active==0){ $toplevel->optionAdd("$xclass.active", 'false',21); } $toplevel->optionAdd("$xclass.order",201,21); $this; } sub module_instance{ my$this=shift; my$toplevel=$this->{"toplevel"}; return undef if(defined($xpath)); $xpath=$this->{"xpath"}; $lastbat=$batteries; # modify defaults $toplevel->optionAdd("$xpath.order",201,21); # status group $toplevel->optionAdd("$xpath.scalerefresh",4000,21); $toplevel->optionAdd("$xpath.scalereturn" ,1,21); # don't need hyst if($batteries<=1){ $toplevel->optionAdd("$xpath*label", "battery",21); $toplevel->optionAdd("$xpath*lowlabel", "battery low",21); $toplevel->optionAdd("$xpath*nonelabel", "no battery",21); $toplevel->optionAdd("$xpath*chargelabel", "charging",21); $toplevel->optionAdd("$xpath*fulllabel", "ac power",21); }else{ for(my$i=0;$i<$batteries;$i++){ $toplevel->optionAdd("$xpath*$i.label", "battery$i",21); $toplevel->optionAdd("$xpath*$i.lowlabel", "battery$i low",21); $toplevel->optionAdd("$xpath*$i.nonelabel", "no battery$i",21); $toplevel->optionAdd("$xpath*$i.chargelabel", "charging $i",21); $toplevel->optionAdd("$xpath*$i.fulllabel", "ac power",21); } } $toplevel->optionAdd("$xpath.scalewidadj", 80*$batteries,21); # narrower $toplevel->optionAdd("$xpath.scalelenadj", 100,21); my$fg=&main::moption($this,"litforeground"); $toplevel->optionAdd("$xpath*midforeground", $fg,21); $toplevel->optionAdd("$xpath*litbackground", '#60c060',21); $toplevel->optionAdd("$xpath*midbackground", '#d0d060',21); $toplevel->optionAdd("$xpath*lowbackground", '#ff4040',21); $toplevel->optionAdd("$xpath*lowforeground", '#ffffff',21); $toplevel->optionAdd("$xpath*acbackground", '#808080',21); $toplevel->optionAdd("$xpath*acforeground", '#000000',21); $toplevel->optionAdd("$xpath*chargebackground", '#74ade7',21); $toplevel->optionAdd("$xpath*chargeforeground", '#000000',21); my($minx,$miny)=&MGM::Graph::calcxysize($this,100,'%',$batteries); $toplevel->optionAdd("$xpath.minx", $minx,21); $toplevel->optionAdd("$xpath.miny", $miny,21); $this; } sub module_run{ my$this=shift; $graph=MGM::Graph->new($this,num=>$batteries,fixed=>1,rangesetting=>100, rangecurrent=>100,minscale=>0, prompt=>'%'); # color change hack @gstate=map{0}(1...$batteries); $lstate=0; $this->module_update; $widget=$graph->{"widget"}; # must return the widget } my @pmulist=("Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "2400/3400/3500", "G3/Wallstreet", "G3/Lombard", "G4/NewWorld/iBook", "newer than Titanium"); sub read_proc{ my$test; my$this=shift; if($openacpi){return $this->read_proc_acpi;} if($openapm){return $this->read_proc_apm;} if($openp || $openf){return $this->read_proc_mac;} $test=$this->read_proc_acpi; if(defined($test)){return $test;} $test=$this->read_proc_apm; if(defined($test)){return $test;} $this->read_proc_mac; } # Mac style power management sub read_proc_mac{ my $percent; my $line; my $data; # recent 2.4 has gone to a /proc entry for pmu values. Use that # if we can. if(!$openf && !$openp){ if(open(PROC,"/proc/pmu/info")){ $openf=1; } } if($openf){ my %hash; sysseek PROC, 0, SEEK_SET; sysread PROC,$data,4096; map{ my$pos=rindex $_, ":"; if($pos>0){ my$key=substr $_,0,$pos; $key=~s/^\s*(\S+)\s(\S*)\s*/$1$2/; $hash{$key}=substr $_, $pos+1; } } split "\n", $data; $ac=int($hash{'ACPower'}); $batteries=int($hash{'Batterycount'}); for(my$i=0;$i<$batteries;$i++){ if(open(FPROC,"/proc/pmu/battery_$i")){ sysread FPROC,$data,4096; map{ my$pos=rindex $_, ":"; if($pos>0){ my$key=substr $_,0,$pos; $key=~s/^\s*(\S+)\s*/$1/; $hash{$key}=substr $_, $pos+1; } } split "\n", $data; close FPROC; if($hash{'max_charge'}>0){ $percent[$i]=$hash{'charge'}*100./$hash{'max_charge'} }else{ $percent[$i]=0; } $battstatus[$i]=0; $battstatus[$i]=1 if($hash{'current'}>0); $battstatus[$i]=-1 if($hash{'flags'}==0); $active=1; } } return; } # get a connection to pmud if we don't have one if(!$openp){ my $iaddr = inet_aton('localhost'); my $port = 879; my $paddr = sockaddr_in($port,$iaddr); my $proto = getprotobyname('tcp'); if(socket(B_FD, PF_INET, SOCK_STREAM, $proto)){ if(connect(B_FD, $paddr)){ # pmud gives back the raw power status; the status is different # for different powerbooks (grumble, grumble) sysread B_FD,$_,1024; if(m/^\S+\s+\S+\s+(\S+)/){ SWITCH:{ if($1>=10 && $1<=12){ $active=1; $openp=$1; my$foo="\n"; syswrite B_FD,$foo,1; last SWITCH; } print "PowerMac PMU version $1 (". $pmulist[$1]. ") not supported yet.\n"; close B_FD; } } } } } if($openp){ if($openp>=10 && $openp<=12){ sysread B_FD,$line,1024; $line=~m/^S\s+\{([^\}]*)\}\s+\{([^\}]*)\}/; my$l=$1; my$r=$2; my($lac,$lcharge,$lbatt,$lcurrent,$lvoltage)= split ' ',$l; my($rac,$rcharge,$rbatt,$rcurrent,$rvoltage)= split ' ',$r; $ac=0; $ac=1 if($lac>=100); $batteries=0; if(defined($lcurrent)){ $percent[$batteries]=($lcharge / $lbatt)*100.; $battstatus[$batteries]=0; $battstatus[$batteries]=1 if($lcurrent>0); $batteries++; } if(defined($rcurrent)){ $percent[$batteries]=($rcharge / $rbatt)*100.; $battstatus[$batteries]=0; $battstatus[$batteries]=1 if($rcurrent>0); $batteries++; } my$foo="\n"; syswrite B_FD,$foo,1; } } } # PC APM style power management. # /proc/apm only reports one line for all batteries sub read_proc_apm{ my$this=shift; my$percent; # Arguments, with symbols from linux/apm_bios.h #0) Linux driver version (this will change if format changes) #1) APM BIOS Version. Usually 1.0 or 1.1. #2) APM flags from APM Installation Check (0x00): # bit 0: APM_16_BIT_SUPPORT # bit 1: APM_32_BIT_SUPPORT # bit 2: APM_IDLE_SLOWS_CLOCK # bit 3: APM_BIOS_DISABLED # bit 4: APM_BIOS_DISENGAGED #3) AC line status # 0x00: Off-line # 0x01: On-line # 0x02: On backup power (APM BIOS 1.1 only) # 0xff: Unknown #4) Battery status # 0x00: High # 0x01: Low # 0x02: Critical # 0x03: Charging # 0xff: Unknown #5) Battery flag # bit 0: High # bit 1: Low # bit 2: Critical # bit 3: Charging # bit 7: No system battery # 0xff: Unknown #6) Remaining battery life (percentage of charge): # 0-100: valid # -1: Unknown #7) Remaining battery life (time units): # Number of remaining minutes or seconds # -1: Unknown # 8) min = minutes; sec = seconds */ $batteries=0; if(open(PROC,"/proc/apm")){ $active=1; $openapm=1; sysread PROC,$_,1024; if(m/^\S+\s+\S+\s+\S+\s+(\S+)\s+(\S+)\s+\S+\s+([^ \%]+)\%/){ if($1 eq '0xff'){ $batteries=0; }else{ $ac=1; $ac=0 if($1 eq '0x00'); $batteries=1; $battstatus[0]=0; $battstatus[0]=1 if($2 eq '0x03'); $battstatus[0]=-1 if($2 eq '0xff'); $percent[0]=$3; } } close PROC; } $percent; } sub list_subdirs{ my@dirs; my$path=shift; if(opendir(DIR,$path)){ @dirs = map ("$path/$_", grep { /^[^\.]/ && -d "$path/$_" } readdir(DIR)); close DIR; return @dirs; } return; } # PC ACPI style power management. sub read_proc_acpi{ my$this=shift; my$percent; $batteries=0; if($openacpi==0){ # initialize @acpi_battery_dirs=(list_subdirs("/proc/acpi/battery")); my @adapdir=(list_subdirs("/proc/acpi/ac_adapter")); $acpi_adapter_dir=$adapdir[0]; } foreach my$dir (@acpi_battery_dirs) { if(open(PROC,"$dir/info")){ if(open(PROC2,"$dir/state")){ $active=1; $openacpi=1; my$total=0; $battstatus[$batteries]=-1; $percent[$batteries]=0; while(){ if(m/^last full capacity:\s+(\d+)/){ $total=$1; } if(m/^present:\s+yes/){ $battstatus[$batteries] = 0 if($battstatus[$batteries]==-1); } } while(){ if(m/^remaining capacity:\s+(\d+)/){ $percent[$batteries]=$1*100/$total; } if(m/^charging state:\s+charging/){ $battstatus[$batteries]=1; } if(m/^charging state:\s+discharging/){ $battstatus[$batteries]=0; } if(m/^present:\s+yes/){ $battstatus[$batteries] = 0 if($battstatus[$batteries]==-1); } } close PROC2; } close PROC; $batteries++; } } if($batteries && defined($acpi_adapter_dir)){ $ac=0; if(open(PROC,"$acpi_adapter_dir/state")){ while(){ if(m/^state:\s+on-line/){ $ac=1; } } close PROC; } } $batteries; } sub module_update{ my$this=shift; my$toplevel=$this->{"toplevel"}; my$percent; my$maxpercent=-1; my$i=0; read_proc($this); return &reconfig if($lastbat!=$batteries); for($i=0;$i<$batteries;$i++){ my$val=$percent[$i]; # apm, for some reason, seems to occasionally return -1/1. # guard against that if($val>=0){ $maxpercent=$val if($maxpercent<$val); } if($battstatus[$i]<0){ if($gstate[$i]!=6){ $graph-> barconfigure($i,'aforeXr'=>'acforeground', 'abackXr'=>'acbackground', 'labelXr'=>'nonelabel'); $gstate[$i]=6; } }elsif($ac){ if($battstatus[$i]==1){ if($gstate[$i]!=4){ $graph-> barconfigure($i,'aforeXr'=>'chargeforeground', 'abackXr'=>'chargebackground', 'labelXr'=>'chargelabel'); $gstate[$i]=4; } }else{ if($gstate[$i]!=5){ $graph-> barconfigure($i,'aforeXr'=>'acforeground', 'abackXr'=>'acbackground', 'labelXr'=>'fulllabel'); $gstate[$i]=5; } } }else{ if($val<30){ if($val<8){ if($gstate[$i]!=2){ $graph-> barconfigure($i,'aforeXr'=>'lowforeground', 'abackXr'=>'lowbackground', 'labelXr'=>'lowlabel'); $gstate[$i]=2; } }else{ if($gstate[$i]!=1){ $graph-> barconfigure($i,'aforeXr'=>'midforeground', 'abackXr'=>'midbackground', 'labelXr'=>'label'); $gstate[$i]=1; } } }else{ if($gstate[$i]!=0){ $graph-> barconfigure($i,'aforeXr'=>'litforeground', 'abackXr'=>'litbackground', 'labelXr'=>'label'); $gstate[$i]=0; } } $i++; } } $percent=$maxpercent; if($percent>=0){ $graph->set(@percent); if($percent<30){ # below 30%, it would be nice to unfix the scale so # the user can see if($percent<8){ if($lstate!=2){ $graph->configure(fixed=>0,rangesetting=>8); $lstate=2; } }else{ if($lstate!=1){ $graph->configure(fixed=>0,rangesetting=>32); $lstate=1; } } }else{ if($lstate!=0){ $graph->configure(fixed=>1,rangesetting=>100); $lstate=0; } } } } sub destroy{ close B_FD if($openp); close PROC if($openf); $openp=0; $openf=0; undef $xpath; } sub reconfig{ &main::reinstance() if($widget->optionGet("reconfig","") eq 'true'); } sub module_place_p{ $batteries; } bless {};