batt: don't fail if there is no battery
[lxde/lxpanel.git] / src / plugins / batt / batt.c
1 /*
2 * ACPI battery monitor plugin for LXPanel
3 *
4 * Copyright (C) 2007 by Greg McNew <gmcnew@gmail.com>
5 * Copyright (C) 2008 by Hong Jen Yee <pcman.tw@gmail.com>
6 * Copyright (C) 2009 by Juergen Hoetzel <juergen@archlinux.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 * This plugin monitors battery usage on ACPI-enabled systems by reading the
24 * battery information found in /sys/class/power_supply. The update interval is
25 * user-configurable and defaults to 3 second.
26 *
27 * The battery's remaining life is estimated from its current charge and current
28 * rate of discharge. The user may configure an alarm command to be run when
29 * their estimated remaining battery life reaches a certain level.
30 */
31
32 /* FIXME:
33 * Here are somethings need to be improvec:
34 * 1. Replace pthread stuff with gthread counterparts for portability.
35 * 3. Add an option to hide the plugin when AC power is used or there is no battery.
36 * 4. Handle failure gracefully under systems other than Linux.
37 */
38
39 #include <glib.h>
40 #include <glib/gi18n.h>
41 #include <pthread.h> /* used by pthread_create() and alarmThread */
42 #include <semaphore.h> /* used by update() and alarmProcess() for alarms */
43 #include <stdlib.h>
44 #include <string.h>
45 #include <stdarg.h>
46
47 #include "dbg.h"
48 #include "batt_sys.h"
49 #include "misc.h" /* used for the line struct */
50 #include "panel.h" /* used to determine panel orientation */
51 #include "plugin.h"
52
53 /* The last MAX_SAMPLES samples are averaged when charge rates are evaluated.
54 This helps prevent spikes in the "time left" values the user sees. */
55 #define MAX_SAMPLES 10
56
57 typedef struct {
58 char *alarmCommand,
59 *backgroundColor,
60 *chargingColor1,
61 *chargingColor2,
62 *dischargingColor1,
63 *dischargingColor2;
64 GdkColor background,
65 charging1,
66 charging2,
67 discharging1,
68 discharging2;
69 cairo_surface_t *pixmap;
70 GtkWidget *drawingArea;
71 int orientation;
72 unsigned int alarmTime,
73 border,
74 height,
75 length,
76 numSamples,
77 requestedBorder,
78 *rateSamples,
79 rateSamplesSum,
80 thickness,
81 timer,
82 state_elapsed_time,
83 info_elapsed_time,
84 wasCharging,
85 width,
86 hide_if_no_battery;
87 sem_t alarmProcessLock;
88 battery* b;
89 gboolean has_ac_adapter;
90 gboolean show_extended_information;
91 } lx_battery;
92
93
94 typedef struct {
95 char *command;
96 sem_t *lock;
97 } Alarm;
98
99 static void destructor(Plugin *p);
100 static void update_display(lx_battery *lx_b, gboolean repaint);
101
102 /* alarmProcess takes the address of a dynamically allocated alarm struct (which
103 it must free). It ensures that alarm commands do not run concurrently. */
104 static void * alarmProcess(void *arg) {
105 Alarm *a = (Alarm *) arg;
106
107 sem_wait(a->lock);
108 system(a->command);
109 sem_post(a->lock);
110
111 g_free(a);
112 return NULL;
113 }
114
115
116 static void append(gchar **tooltip, gchar *fmt, ...)
117 {
118 gchar *old = *tooltip;
119 gchar *new;
120 va_list va;
121
122 va_start(va, fmt);
123 new = g_strdup_vprintf(fmt, va);
124 va_end(va);
125
126 *tooltip = g_strconcat(old, new, NULL);
127
128 g_free(old);
129 g_free(new);
130 }
131
132
133 /* Make a tooltip string, and display remaining charge time if the battery
134 is charging or remaining life if it's discharging */
135 static gchar* make_tooltip(lx_battery* lx_b, gboolean isCharging)
136 {
137 gchar * tooltip;
138 gchar * indent = " ";
139 battery *b = lx_b->b;
140
141 if (b == NULL)
142 return NULL;
143
144 if (isCharging) {
145 int hours = lx_b->b->seconds / 3600;
146 int left_seconds = lx_b->b->seconds - 3600 * hours;
147 int minutes = left_seconds / 60;
148 tooltip = g_strdup_printf(
149 _("Battery: %d%% charged, %d:%02d until full"),
150 lx_b->b->percentage,
151 hours,
152 minutes );
153 } else {
154 /* if we have enough rate information for battery */
155 if (lx_b->b->percentage != 100) {
156 int hours = lx_b->b->seconds / 3600;
157 int left_seconds = lx_b->b->seconds - 3600 * hours;
158 int minutes = left_seconds / 60;
159 tooltip = g_strdup_printf(
160 _("Battery: %d%% charged, %d:%02d left"),
161 lx_b->b->percentage,
162 hours,
163 minutes );
164 } else {
165 tooltip = g_strdup_printf(
166 _("Battery: %d%% charged"),
167 100 );
168 }
169 }
170
171 if (!lx_b->show_extended_information) {
172 return tooltip;
173 }
174
175 if (b->energy_full_design != -1)
176 append(&tooltip, _("\n%sEnergy full design:\t\t%5d mWh"), indent, b->energy_full_design);
177 if (b->energy_full != -1)
178 append(&tooltip, _("\n%sEnergy full:\t\t\t%5d mWh"), indent, b->energy_full);
179 if (b->energy_now != -1)
180 append(&tooltip, _("\n%sEnergy now:\t\t\t%5d mWh"), indent, b->energy_now);
181 if (b->power_now != -1)
182 append(&tooltip, _("\n%sPower now:\t\t\t%5d mW"), indent, b->power_now);
183
184 if (b->charge_full_design != -1)
185 append(&tooltip, _("\n%sCharge full design:\t%5d mAh"), indent, b->charge_full_design);
186 if (b->charge_full != -1)
187 append(&tooltip, _("\n%sCharge full:\t\t\t%5d mAh"), indent, b->charge_full);
188 if (b->charge_now != -1)
189 append(&tooltip, _("\n%sCharge now:\t\t\t%5d mAh"), indent, b->charge_now);
190 if (b->current_now != -1)
191 append(&tooltip, _("\n%sCurrent now:\t\t\t%5d mA"), indent, b->current_now);
192
193 if (b->voltage_now != -1)
194 append(&tooltip, _("\n%sCurrent Voltage:\t\t%.3lf V"), indent, b->voltage_now / 1000.0);
195
196 return tooltip;
197 }
198
199 static void set_tooltip_text(lx_battery* lx_b)
200 {
201 if (lx_b->b == NULL)
202 return;
203 gboolean isCharging = battery_is_charging(lx_b->b);
204 gchar *tooltip = make_tooltip(lx_b, isCharging);
205 gtk_widget_set_tooltip_text(lx_b->drawingArea, tooltip);
206 g_free(tooltip);
207 }
208
209 /* FIXME:
210 Don't repaint if percentage of remaining charge and remaining time aren't changed. */
211 void update_display(lx_battery *lx_b, gboolean repaint) {
212 cairo_t *cr;
213 battery *b = lx_b->b;
214 /* unit: mW */
215 int rate;
216 gboolean isCharging;
217
218 if (! lx_b->pixmap )
219 return;
220
221 cr = cairo_create(lx_b->pixmap);
222 cairo_set_line_width (cr, 1.0);
223
224 /* no battery is found */
225 if( b == NULL )
226 {
227 gtk_widget_set_tooltip_text( lx_b->drawingArea, _("No batteries found") );
228 return;
229 }
230
231 /* draw background */
232 gdk_cairo_set_source_color(cr, &lx_b->background);
233 cairo_rectangle(cr, 0, 0, lx_b->width, lx_b->height);
234 cairo_fill(cr);
235
236 /* fixme: only one battery supported */
237
238 rate = lx_b->b->current_now;
239 isCharging = battery_is_charging ( b );
240
241 /* Consider running the alarm command */
242 if ( !isCharging && rate > 0 &&
243 ( ( battery_get_remaining( b ) / 60 ) < lx_b->alarmTime ) )
244 {
245 /* Shrug this should be done using glibs process functions */
246 /* Alarms should not run concurrently; determine whether an alarm is
247 already running */
248 int alarmCanRun;
249 sem_getvalue(&(lx_b->alarmProcessLock), &alarmCanRun);
250
251 /* Run the alarm command if it isn't already running */
252 if (alarmCanRun) {
253
254 Alarm *a = (Alarm *) malloc(sizeof(Alarm));
255 a->command = lx_b->alarmCommand;
256 a->lock = &(lx_b->alarmProcessLock);
257
258 /* Manage the alarm process in a new thread, which which will be
259 responsible for freeing the alarm struct it's given */
260 pthread_t alarmThread;
261 pthread_create(&alarmThread, NULL, alarmProcess, a);
262
263 }
264 }
265
266 set_tooltip_text(lx_b);
267
268 int chargeLevel = lx_b->b->percentage * (lx_b->length - 2 * lx_b->border) / 100;
269
270 if (lx_b->orientation == ORIENT_HORIZ) {
271
272 /* Draw the battery bar vertically, using color 1 for the left half and
273 color 2 for the right half */
274 gdk_cairo_set_source_color(cr,
275 isCharging ? &lx_b->charging1 : &lx_b->discharging1);
276 cairo_rectangle(cr, lx_b->border,
277 lx_b->height - lx_b->border - chargeLevel, lx_b->width / 2
278 - lx_b->border, chargeLevel);
279 cairo_fill(cr);
280 gdk_cairo_set_source_color(cr,
281 isCharging ? &lx_b->charging2 : &lx_b->discharging2);
282 cairo_rectangle(cr, lx_b->width / 2,
283 lx_b->height - lx_b->border - chargeLevel, (lx_b->width + 1) / 2
284 - lx_b->border, chargeLevel);
285 cairo_fill(cr);
286
287 }
288 else {
289
290 /* Draw the battery bar horizontally, using color 1 for the top half and
291 color 2 for the bottom half */
292 gdk_cairo_set_source_color(cr,
293 isCharging ? &lx_b->charging1 : &lx_b->discharging1);
294 cairo_rectangle(cr, lx_b->border,
295 lx_b->border, chargeLevel, lx_b->height / 2 - lx_b->border);
296 cairo_fill(cr);
297 gdk_cairo_set_source_color(cr,
298 isCharging ? &lx_b->charging2 : &lx_b->discharging2);
299 cairo_rectangle(cr, lx_b->border, (lx_b->height + 1)
300 / 2, chargeLevel, lx_b->height / 2 - lx_b->border);
301 cairo_fill(cr);
302
303 }
304 if( repaint )
305 gtk_widget_queue_draw( lx_b->drawingArea );
306
307 check_cairo_status(cr);
308 cairo_destroy(cr);
309 }
310
311 /* This callback is called every 3 seconds */
312 static int update_timout(lx_battery *lx_b) {
313 GDK_THREADS_ENTER();
314 lx_b->state_elapsed_time++;
315 lx_b->info_elapsed_time++;
316
317 /* check the batteries every 3 seconds */
318 if (lx_b->b != NULL)
319 battery_update( lx_b->b );
320
321 update_display( lx_b, TRUE );
322
323 GDK_THREADS_LEAVE();
324 return TRUE;
325 }
326
327 /* An update will be performed whenever the user clicks on the charge bar */
328 static gint buttonPressEvent(GtkWidget *widget, GdkEventButton *event,
329 Plugin* plugin) {
330
331 lx_battery *lx_b = (lx_battery*)plugin->priv;
332
333 update_display(lx_b, TRUE);
334
335 if( event->button == 3 ) /* right button */
336 {
337 GtkMenu* popup = lxpanel_get_panel_menu( plugin->panel, plugin, FALSE );
338 gtk_menu_popup( popup, NULL, NULL, NULL, NULL, event->button, event->time );
339 return TRUE;
340 }
341 return FALSE;
342 }
343
344
345 static gint configureEvent(GtkWidget *widget, GdkEventConfigure *event,
346 lx_battery *lx_b) {
347
348 ENTER;
349
350 if (lx_b->pixmap)
351 cairo_surface_destroy(lx_b->pixmap);
352
353 /* Update the plugin's dimensions */
354 lx_b->width = widget->allocation.width;
355 lx_b->height = widget->allocation.height;
356 if (lx_b->orientation == ORIENT_HORIZ) {
357 lx_b->length = lx_b->height;
358 lx_b->thickness = lx_b->width;
359 }
360 else {
361 lx_b->length = lx_b->width;
362 lx_b->thickness = lx_b->height;
363 }
364
365 lx_b->pixmap = cairo_image_surface_create (CAIRO_FORMAT_RGB24, widget->allocation.width,
366 widget->allocation.height);
367 check_cairo_surface_status(&lx_b->pixmap);
368
369 /* Perform an update so the bar will look right in its new orientation */
370 update_display(lx_b, FALSE);
371
372 RET(TRUE);
373
374 }
375
376
377 static gint exposeEvent(GtkWidget *widget, GdkEventExpose *event, lx_battery *lx_b) {
378
379 ENTER;
380 cairo_t *cr = gdk_cairo_create(widget->window);
381 gdk_cairo_region(cr, event->region);
382 cairo_clip(cr);
383
384 gdk_cairo_set_source_color(cr, &lx_b->drawingArea->style->black);
385 cairo_set_source_surface(cr, lx_b->pixmap, 0, 0);
386 cairo_paint(cr);
387
388 check_cairo_status(cr);
389 cairo_destroy(cr);
390
391 RET(FALSE);
392 }
393
394
395 static int
396 constructor(Plugin *p, char **fp)
397 {
398 ENTER;
399
400 lx_battery *lx_b;
401 p->priv = lx_b = g_new0(lx_battery, 1);
402
403 /* get available battery */
404 lx_b->b = battery_get ();
405
406 p->pwid = gtk_event_box_new();
407 GTK_WIDGET_SET_FLAGS( p->pwid, GTK_NO_WINDOW );
408 gtk_container_set_border_width( GTK_CONTAINER(p->pwid), 1 );
409
410 lx_b->drawingArea = gtk_drawing_area_new();
411 gtk_widget_add_events( lx_b->drawingArea, GDK_BUTTON_PRESS_MASK );
412
413 gtk_container_add( (GtkContainer*)p->pwid, lx_b->drawingArea );
414
415 if ((lx_b->orientation = p->panel->orientation) == ORIENT_HORIZ) {
416 lx_b->height = lx_b->length = 20;
417 lx_b->thickness = lx_b->width = 8;
418 }
419 else {
420 lx_b->height = lx_b->thickness = 8;
421 lx_b->length = lx_b->width = 20;
422 }
423 gtk_widget_set_size_request(lx_b->drawingArea, lx_b->width, lx_b->height);
424
425 gtk_widget_show(lx_b->drawingArea);
426
427 g_signal_connect (G_OBJECT (lx_b->drawingArea), "button_press_event",
428 G_CALLBACK(buttonPressEvent), (gpointer) p);
429 g_signal_connect (G_OBJECT (lx_b->drawingArea),"configure_event",
430 G_CALLBACK (configureEvent), (gpointer) lx_b);
431 g_signal_connect (G_OBJECT (lx_b->drawingArea), "expose_event",
432 G_CALLBACK (exposeEvent), (gpointer) lx_b);
433
434 sem_init(&(lx_b->alarmProcessLock), 0, 1);
435
436 lx_b->alarmCommand = lx_b->backgroundColor = lx_b->chargingColor1 = lx_b->chargingColor2
437 = lx_b->dischargingColor1 = lx_b->dischargingColor2 = NULL;
438
439 /* Set default values for integers */
440 lx_b->alarmTime = 5;
441 lx_b->requestedBorder = 1;
442
443 line s;
444 s.len = 256;
445
446 lx_b->show_extended_information = false;
447
448 if (fp) {
449
450 /* Apply options */
451 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
452 if (s.type == LINE_NONE) {
453 ERR( "batt: illegal token %s\n", s.str);
454 goto error;
455 }
456 if (s.type == LINE_VAR) {
457 if (!g_ascii_strcasecmp(s.t[0], "HideIfNoBattery"))
458 lx_b->hide_if_no_battery = atoi(s.t[1]);
459 else if (!g_ascii_strcasecmp(s.t[0], "AlarmCommand"))
460 lx_b->alarmCommand = g_strdup(s.t[1]);
461 else if (!g_ascii_strcasecmp(s.t[0], "BackgroundColor"))
462 lx_b->backgroundColor = g_strdup(s.t[1]);
463 else if (!g_ascii_strcasecmp(s.t[0], "ChargingColor1"))
464 lx_b->chargingColor1 = g_strdup(s.t[1]);
465 else if (!g_ascii_strcasecmp(s.t[0], "ChargingColor2"))
466 lx_b->chargingColor2 = g_strdup(s.t[1]);
467 else if (!g_ascii_strcasecmp(s.t[0], "DischargingColor1"))
468 lx_b->dischargingColor1 = g_strdup(s.t[1]);
469 else if (!g_ascii_strcasecmp(s.t[0], "DischargingColor2"))
470 lx_b->dischargingColor2 = g_strdup(s.t[1]);
471 else if (!g_ascii_strcasecmp(s.t[0], "AlarmTime"))
472 lx_b->alarmTime = atoi(s.t[1]);
473 else if (!g_ascii_strcasecmp(s.t[0], "BorderWidth"))
474 lx_b->requestedBorder = atoi(s.t[1]);
475 else if (!g_ascii_strcasecmp(s.t[0], "Size")) {
476 lx_b->thickness = MAX(1, atoi(s.t[1]));
477 if (lx_b->orientation == ORIENT_HORIZ)
478 lx_b->width = lx_b->thickness;
479 else
480 lx_b->height = lx_b->thickness;
481 gtk_widget_set_size_request(lx_b->drawingArea, lx_b->width,
482 lx_b->height);
483 }
484 else if (!g_ascii_strcasecmp(s.t[0], "ShowExtendedInformation"))
485 lx_b->show_extended_information = atoi(s.t[1]);
486 else {
487 ERR( "batt: unknown var %s\n", s.t[0]);
488 continue;
489 }
490 }
491 else {
492 ERR( "batt: illegal in this context %s\n", s.str);
493 goto error;
494 }
495 }
496
497 }
498
499 /* Make sure the border value is acceptable */
500 lx_b->border = MIN(MAX(0, lx_b->requestedBorder),
501 (MIN(lx_b->length, lx_b->thickness) - 1) / 2);
502
503 /* Apply more default options */
504 if (! lx_b->alarmCommand)
505 lx_b->alarmCommand = g_strdup("xmessage Battery low");
506 if (! lx_b->backgroundColor)
507 lx_b->backgroundColor = g_strdup("black");
508 if (! lx_b->chargingColor1)
509 lx_b->chargingColor1 = g_strdup("#28f200");
510 if (! lx_b->chargingColor2)
511 lx_b->chargingColor2 = g_strdup("#22cc00");
512 if (! lx_b->dischargingColor1)
513 lx_b->dischargingColor1 = g_strdup("#ffee00");
514 if (! lx_b->dischargingColor2)
515 lx_b->dischargingColor2 = g_strdup("#d9ca00");
516
517 gdk_color_parse(lx_b->backgroundColor, &lx_b->background);
518 gdk_color_parse(lx_b->chargingColor1, &lx_b->charging1);
519 gdk_color_parse(lx_b->chargingColor2, &lx_b->charging2);
520 gdk_color_parse(lx_b->dischargingColor1, &lx_b->discharging1);
521 gdk_color_parse(lx_b->dischargingColor2, &lx_b->discharging2);
522
523
524 /* Start the update loop */
525 lx_b->timer = g_timeout_add_seconds( 9, (GSourceFunc) update_timout, (gpointer) lx_b);
526
527 RET(TRUE);
528
529 error:
530 RET(FALSE);
531 }
532
533
534 static void
535 destructor(Plugin *p)
536 {
537 ENTER;
538
539 lx_battery *b = (lx_battery *) p->priv;
540
541 if (b->b != NULL)
542 battery_free(b->b);
543
544 if (b->pixmap)
545 cairo_surface_destroy(b->pixmap);
546
547 g_free(b->alarmCommand);
548 g_free(b->backgroundColor);
549 g_free(b->chargingColor1);
550 g_free(b->chargingColor2);
551 g_free(b->dischargingColor1);
552 g_free(b->dischargingColor2);
553
554 g_free(b->rateSamples);
555 sem_destroy(&(b->alarmProcessLock));
556 if (b->timer)
557 g_source_remove(b->timer);
558 g_free(b);
559
560 RET();
561
562 }
563
564
565 static void orientation(Plugin *p) {
566
567 ENTER;
568
569 lx_battery *b = (lx_battery *) p->priv;
570
571 if (b->orientation != p->panel->orientation) {
572 b->orientation = p->panel->orientation;
573 unsigned int swap = b->height;
574 b->height = b->width;
575 b->width = swap;
576 gtk_widget_set_size_request(b->drawingArea, b->width, b->height);
577 }
578
579 RET();
580 }
581
582
583 static void applyConfig(Plugin* p)
584 {
585 ENTER;
586
587 lx_battery *b = (lx_battery *) p->priv;
588
589 /* Update colors */
590 if (b->backgroundColor &&
591 gdk_color_parse(b->backgroundColor, &b->background));
592 if (b->chargingColor1 && gdk_color_parse(b->chargingColor1, &b->charging1));
593 if (b->chargingColor2 && gdk_color_parse(b->chargingColor2, &b->charging2));
594 if (b->dischargingColor1 &&
595 gdk_color_parse(b->dischargingColor1, &b->discharging1));
596 if (b->dischargingColor2 &&
597 gdk_color_parse(b->dischargingColor2, &b->discharging2));
598
599 /* Make sure the border value is acceptable */
600 b->border = MIN(MAX(0, b->requestedBorder),
601 (MIN(b->length, b->thickness) - 1) / 2);
602
603 /* Resize the widget */
604 if (b->orientation == ORIENT_HORIZ)
605 b->width = b->thickness;
606 else
607 b->height = b->thickness;
608 gtk_widget_set_size_request(b->drawingArea, b->width, b->height);
609
610 /* update tooltip */
611 set_tooltip_text(b);
612
613 RET();
614 }
615
616
617 static void config(Plugin *p, GtkWindow* parent) {
618 ENTER;
619
620 GtkWidget *dialog;
621 lx_battery *b = (lx_battery *) p->priv;
622 dialog = create_generic_config_dlg(_(p->class->name),
623 GTK_WIDGET(parent),
624 (GSourceFunc) applyConfig, (gpointer) p,
625 #if 0
626 _("Hide if there is no battery"), &b->hide_if_no_battery, CONF_TYPE_BOOL,
627 #endif
628 _("Alarm command"), &b->alarmCommand, CONF_TYPE_STR,
629 _("Alarm time (minutes left)"), &b->alarmTime, CONF_TYPE_INT,
630 _("Background color"), &b->backgroundColor, CONF_TYPE_STR,
631 _("Charging color 1"), &b->chargingColor1, CONF_TYPE_STR,
632 _("Charging color 2"), &b->chargingColor2, CONF_TYPE_STR,
633 _("Discharging color 1"), &b->dischargingColor1, CONF_TYPE_STR,
634 _("Discharging color 2"), &b->dischargingColor2, CONF_TYPE_STR,
635 _("Border width"), &b->requestedBorder, CONF_TYPE_INT,
636 _("Size"), &b->thickness, CONF_TYPE_INT,
637 _("Show Extended Information"), &b->show_extended_information, CONF_TYPE_BOOL,
638 NULL);
639 gtk_window_present(GTK_WINDOW(dialog));
640
641 RET();
642 }
643
644
645 static void save(Plugin* p, FILE* fp) {
646 lx_battery *lx_b = (lx_battery *) p->priv;
647
648 lxpanel_put_bool(fp, "HideIfNoBattery",lx_b->hide_if_no_battery);
649 lxpanel_put_str(fp, "AlarmCommand", lx_b->alarmCommand);
650 lxpanel_put_int(fp, "AlarmTime", lx_b->alarmTime);
651 lxpanel_put_str(fp, "BackgroundColor", lx_b->backgroundColor);
652 lxpanel_put_int(fp, "BorderWidth", lx_b->requestedBorder);
653 lxpanel_put_str(fp, "ChargingColor1", lx_b->chargingColor1);
654 lxpanel_put_str(fp, "ChargingColor2", lx_b->chargingColor2);
655 lxpanel_put_str(fp, "DischargingColor1", lx_b->dischargingColor1);
656 lxpanel_put_str(fp, "DischargingColor2", lx_b->dischargingColor2);
657 lxpanel_put_int(fp, "Size", lx_b->thickness);
658 lxpanel_put_bool(fp, "ShowExtendedInformation", lx_b->show_extended_information);
659 }
660
661
662 PluginClass batt_plugin_class = {
663
664 PLUGINCLASS_VERSIONING,
665
666 .type = "batt",
667 .name = N_("Battery Monitor"),
668 .version = "2.0",
669 .description = N_("Display battery status using ACPI"),
670
671 .constructor = constructor,
672 .destructor = destructor,
673 .config = config,
674 .save = save,
675 .panel_configuration_changed = orientation
676 };
677
678
679 /* vim: set sw=4 sts=4 : */