1 /**************************************************************************
5 * Copyright (C) 2009 Sebastian Reichel <elektranox@gmail.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License version 2
9 * or any later version as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **************************************************************************/
24 #include <cairo-xlib.h>
25 #include <pango/pangocairo.h>
27 #if defined(__OpenBSD__) || defined(__NetBSD__)
28 #include <machine/apmvar.h>
30 #include <sys/ioctl.h>
34 #if defined(__FreeBSD__)
35 #include <sys/types.h>
36 #include <sys/sysctl.h>
46 PangoFontDescription
*bat1_font_desc
;
47 PangoFontDescription
*bat2_font_desc
;
48 struct batstate battery_state
;
51 static timeout
* battery_timeout
;
53 static char buf_bat_percentage
[10];
54 static char buf_bat_time
[20];
56 int8_t battery_low_status
;
57 unsigned char battery_low_cmd_send
;
58 char *battery_low_cmd
;
59 char *path_energy_now
;
60 char *path_energy_full
;
61 char *path_current_now
;
64 #if defined(__OpenBSD__) || defined(__NetBSD__)
68 void update_batterys(void* arg
)
70 int old_percentage
= battery_state
.percentage
;
71 int16_t old_hours
= battery_state
.time
.hours
;
72 int8_t old_minutes
= battery_state
.time
.minutes
;
75 if (old_percentage
== battery_state
.percentage
&& old_hours
== battery_state
.time
.hours
&& old_minutes
== battery_state
.time
.minutes
)
79 for (i
=0 ; i
< nb_panel
; i
++) {
80 if (battery_state
.percentage
>= percentage_hide
) {
81 if (panel1
[i
].battery
.area
.on_screen
== 1) {
82 panel1
[i
].battery
.area
.on_screen
= 0;
83 panel1
[i
].area
.resize
= 1;
88 if (panel1
[i
].battery
.area
.on_screen
== 0) {
89 panel1
[i
].battery
.area
.on_screen
= 1;
92 if (panel1
[i
].battery
.area
.on_screen
== 1) {
93 panel1
[i
].battery
.area
.resize
= 1;
99 void default_battery()
102 percentage_hide
= 101;
103 battery_low_cmd_send
= 0;
109 path_energy_full
= 0;
110 path_current_now
= 0;
112 battery_state
.percentage
= 0;
113 battery_state
.time
.hours
= 0;
114 battery_state
.time
.minutes
= 0;
115 #if defined(__OpenBSD__) || defined(__NetBSD__)
120 void cleanup_battery()
122 if (bat1_font_desc
) pango_font_description_free(bat1_font_desc
);
123 if (bat2_font_desc
) pango_font_description_free(bat2_font_desc
);
124 if (path_energy_now
) g_free(path_energy_now
);
125 if (path_energy_full
) g_free(path_energy_full
);
126 if (path_current_now
) g_free(path_current_now
);
127 if (path_status
) g_free(path_status
);
128 if (battery_low_cmd
) g_free(battery_low_cmd
);
129 if (battery_timeout
) stop_timeout(battery_timeout
);
131 #if defined(__OpenBSD__) || defined(__NetBSD__)
132 if ((apm_fd
!= -1) && (close(apm_fd
) == -1))
133 warn("cannot close /dev/apm");
140 if (!battery_enabled
) return;
142 #if defined(__OpenBSD__) || defined(__NetBSD__)
143 apm_fd
= open("/dev/apm", O_RDONLY
);
145 warn("init_battery: failed to open /dev/apm.");
150 #elif !defined(__FreeBSD__)
153 GError
*error
= NULL
;
154 const char *entryname
;
155 char *battery_dir
= 0;
157 directory
= g_dir_open("/sys/class/power_supply", 0, &error
);
161 while ((entryname
=g_dir_read_name(directory
))) {
162 if (strncmp(entryname
,"AC", 2) == 0) continue;
164 char *path1
= g_build_filename("/sys/class/power_supply", entryname
, "present", NULL
);
165 if (g_file_test (path1
, G_FILE_TEST_EXISTS
)) {
167 battery_dir
= g_build_filename("/sys/class/power_supply", entryname
, NULL
);
174 g_dir_close(directory
);
176 fprintf(stderr
, "ERROR: battery applet can't found power_supply\n");
181 char *path1
= g_build_filename(battery_dir
, "energy_now", NULL
);
182 if (g_file_test (path1
, G_FILE_TEST_EXISTS
)) {
183 path_energy_now
= g_build_filename(battery_dir
, "energy_now", NULL
);
184 path_energy_full
= g_build_filename(battery_dir
, "energy_full", NULL
);
187 char *path2
= g_build_filename(battery_dir
, "charge_now", NULL
);
188 if (g_file_test (path2
, G_FILE_TEST_EXISTS
)) {
189 path_energy_now
= g_build_filename(battery_dir
, "charge_now", NULL
);
190 path_energy_full
= g_build_filename(battery_dir
, "charge_full", NULL
);
193 fprintf(stderr
, "ERROR: can't found energy_* or charge_*\n");
197 if (path_energy_now
&& path_energy_full
) {
198 path_current_now
= g_build_filename(battery_dir
, "current_now", NULL
);
199 path_status
= g_build_filename(battery_dir
, "status", NULL
);
202 FILE *fp1
, *fp2
, *fp3
, *fp4
;
203 fp1
= fopen(path_energy_now
, "r");
204 fp2
= fopen(path_energy_full
, "r");
205 fp3
= fopen(path_current_now
, "r");
206 fp4
= fopen(path_status
, "r");
207 if (fp1
== NULL
|| fp2
== NULL
|| fp3
== NULL
|| fp4
== NULL
) {
210 fprintf(stderr
, "ERROR: battery applet can't open energy_now\n");
222 if (battery_enabled
&& battery_timeout
==0)
223 battery_timeout
= add_timeout(10, 10000, update_batterys
, 0);
227 void init_battery_panel(void *p
)
229 Panel
*panel
= (Panel
*)p
;
230 Battery
*battery
= &panel
->battery
;
232 if (!battery_enabled
)
235 battery
->area
.parent
= p
;
236 battery
->area
.panel
= p
;
237 battery
->area
._draw_foreground
= draw_battery
;
238 battery
->area
.size_mode
= SIZE_BY_CONTENT
;
239 battery
->area
._resize
= resize_battery
;
240 battery
->area
.resize
= 1;
241 battery
->area
.redraw
= 1;
242 battery
->area
.on_screen
= 1;
246 void update_battery() {
247 #if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__FreeBSD__)
248 // unused on OpenBSD, silence compiler warnings
251 int64_t current_now
= 0;
253 #if defined(__FreeBSD__)
257 int64_t energy_now
= 0, energy_full
= 0;
259 int8_t new_percentage
= 0;
261 #if defined(__OpenBSD__) || defined(__NetBSD__)
262 struct apm_power_info info
;
263 if (ioctl(apm_fd
, APM_IOC_GETPOWER
, &(info
)) < 0)
264 warn("power update: APM_IOC_GETPOWER");
266 // best attempt at mapping to linux battery states
267 battery_state
.state
= BATTERY_UNKNOWN
;
268 switch (info
.battery_state
) {
269 case APM_BATT_CHARGING
:
270 battery_state
.state
= BATTERY_CHARGING
;
273 battery_state
.state
= BATTERY_DISCHARGING
;
277 if (info
.battery_life
== 100)
278 battery_state
.state
= BATTERY_FULL
;
280 // no mapping for openbsd really
284 if (info
.minutes_left
!= -1)
285 seconds
= info
.minutes_left
* 60;
289 new_percentage
= info
.battery_life
;
291 #elif defined(__FreeBSD__)
292 len
= sizeof(sysctl_out
);
294 if (sysctlbyname("hw.acpi.battery.state", &sysctl_out
, &len
, NULL
, 0) != 0)
295 fprintf(stderr
, "power update: no such sysctl");
297 // attemp to map the battery state to linux
298 battery_state
.state
= BATTERY_UNKNOWN
;
302 battery_state
.state
= BATTERY_DISCHARGING
;
305 battery_state
.state
= BATTERY_CHARGING
;
308 battery_state
.state
= BATTERY_FULL
;
312 // no mapping for freebsd
316 if (sysctlbyname("hw.acpi.battery.time", &sysctl_out
, &len
, NULL
, 0) != 0)
319 seconds
= sysctl_out
* 60;
325 if (sysctlbyname("hw.acpi.battery.life", &sysctl_out
, &len
, NULL
, 0) != 0)
328 new_percentage
= sysctl_out
;
331 fp
= fopen(path_status
, "r");
333 if (fgets(tmp
, sizeof tmp
, fp
)) {
334 battery_state
.state
= BATTERY_UNKNOWN
;
335 if(strcasecmp(tmp
, "Charging\n")==0) battery_state
.state
= BATTERY_CHARGING
;
336 if(strcasecmp(tmp
, "Discharging\n")==0) battery_state
.state
= BATTERY_DISCHARGING
;
337 if(strcasecmp(tmp
, "Full\n")==0) battery_state
.state
= BATTERY_FULL
;
342 fp
= fopen(path_energy_now
, "r");
344 if (fgets(tmp
, sizeof tmp
, fp
)) energy_now
= atoi(tmp
);
348 fp
= fopen(path_energy_full
, "r");
350 if (fgets(tmp
, sizeof tmp
, fp
)) energy_full
= atoi(tmp
);
354 fp
= fopen(path_current_now
, "r");
356 if (fgets(tmp
, sizeof tmp
, fp
)) current_now
= atoi(tmp
);
360 if(current_now
> 0) {
361 switch(battery_state
.state
) {
362 case BATTERY_CHARGING
:
363 seconds
= 3600 * (energy_full
- energy_now
) / current_now
;
365 case BATTERY_DISCHARGING
:
366 seconds
= 3600 * energy_now
/ current_now
;
375 battery_state
.time
.hours
= seconds
/ 3600;
376 seconds
-= 3600 * battery_state
.time
.hours
;
377 battery_state
.time
.minutes
= seconds
/ 60;
378 seconds
-= 60 * battery_state
.time
.minutes
;
379 battery_state
.time
.seconds
= seconds
;
382 new_percentage
= (energy_now
*100)/energy_full
;
384 if(battery_low_status
> new_percentage
&& battery_state
.state
== BATTERY_DISCHARGING
&& !battery_low_cmd_send
) {
385 tint_exec(battery_low_cmd
);
386 battery_low_cmd_send
= 1;
388 if(battery_low_status
< new_percentage
&& battery_state
.state
== BATTERY_CHARGING
&& battery_low_cmd_send
) {
389 battery_low_cmd_send
= 0;
392 battery_state
.percentage
= new_percentage
;
394 // clamp percentage to 100 in case battery is misreporting that its current charge is more than its max
395 if(battery_state
.percentage
> 100) {
396 battery_state
.percentage
= 100;
401 void draw_battery (void *obj
, cairo_t
*c
)
403 Battery
*battery
= obj
;
406 layout
= pango_cairo_create_layout (c
);
409 pango_layout_set_font_description(layout
, bat1_font_desc
);
410 pango_layout_set_width(layout
, battery
->area
.width
* PANGO_SCALE
);
411 pango_layout_set_alignment(layout
, PANGO_ALIGN_CENTER
);
412 pango_layout_set_text(layout
, buf_bat_percentage
, strlen(buf_bat_percentage
));
414 cairo_set_source_rgba(c
, battery
->font
.color
[0], battery
->font
.color
[1], battery
->font
.color
[2], battery
->font
.alpha
);
416 pango_cairo_update_layout(c
, layout
);
417 cairo_move_to(c
, 0, battery
->bat1_posy
);
418 pango_cairo_show_layout(c
, layout
);
420 pango_layout_set_font_description(layout
, bat2_font_desc
);
421 pango_layout_set_indent(layout
, 0);
422 pango_layout_set_text(layout
, buf_bat_time
, strlen(buf_bat_time
));
423 pango_layout_set_width(layout
, battery
->area
.width
* PANGO_SCALE
);
425 pango_cairo_update_layout(c
, layout
);
426 cairo_move_to(c
, 0, battery
->bat2_posy
);
427 pango_cairo_show_layout(c
, layout
);
429 g_object_unref(layout
);
433 int resize_battery(void *obj
)
435 Battery
*battery
= obj
;
436 Panel
*panel
= battery
->area
.panel
;
437 int bat_percentage_height
, bat_percentage_width
, bat_percentage_height_ink
;
438 int bat_time_height
, bat_time_width
, bat_time_height_ink
;
441 battery
->area
.redraw
= 1;
443 snprintf(buf_bat_percentage
, sizeof(buf_bat_percentage
), "%d%%", battery_state
.percentage
);
444 if(battery_state
.state
== BATTERY_FULL
) {
445 strcpy(buf_bat_time
, "Full");
447 snprintf(buf_bat_time
, sizeof(buf_bat_time
), "%02d:%02d", battery_state
.time
.hours
, battery_state
.time
.minutes
);
449 get_text_size2(bat1_font_desc
, &bat_percentage_height_ink
, &bat_percentage_height
, &bat_percentage_width
, panel
->area
.height
, panel
->area
.width
, buf_bat_percentage
, strlen(buf_bat_percentage
));
450 get_text_size2(bat2_font_desc
, &bat_time_height_ink
, &bat_time_height
, &bat_time_width
, panel
->area
.height
, panel
->area
.width
, buf_bat_time
, strlen(buf_bat_time
));
452 if (panel_horizontal
) {
453 int new_size
= (bat_percentage_width
> bat_time_width
) ? bat_percentage_width
: bat_time_width
;
454 new_size
+= (2*battery
->area
.paddingxlr
) + (2*battery
->area
.bg
->border
.width
);
455 if (new_size
> battery
->area
.width
|| new_size
< (battery
->area
.width
-2)) {
456 // we try to limit the number of resize
457 battery
->area
.width
= new_size
;
458 battery
->bat1_posy
= ((battery
->area
.height
- bat_percentage_height
) / 2) - ((bat_time_height_ink
+ 2) / 2);
459 battery
->bat2_posy
= battery
->bat1_posy
+ bat_percentage_height
+ 2 - (bat_percentage_height
- bat_percentage_height_ink
)/2 - (bat_time_height
- bat_time_height_ink
)/2;
464 int new_size
= bat_percentage_height
+ bat_time_height
+ (2 * (battery
->area
.paddingxlr
+ battery
->area
.bg
->border
.width
));
465 if (new_size
!= battery
->area
.height
) {
466 battery
->area
.height
= new_size
;
467 battery
->bat1_posy
= ((battery
->area
.height
- bat_percentage_height
) / 2) - ((bat_time_height_ink
+ 2) / 2);
468 battery
->bat2_posy
= battery
->bat1_posy
+ bat_percentage_height
+ 2 - (bat_percentage_height
- bat_percentage_height_ink
)/2 - (bat_time_height
- bat_time_height_ink
)/2;