1 /* GKrellM
2 |  Copyright (C) 1999-2019 Bill Wilson
3 |
4 |  Author:  Bill Wilson    billw@gkrellm.net
5 |  Latest versions might be found at:  http://gkrellm.net
6 |
7 |
8 |  GKrellM is free software: you can redistribute it and/or modify it
9 |  under the terms of the GNU General Public License as published by
10 |  the Free Software Foundation, either version 3 of the License, or
11 |  (at your option) any later version.
12 |
13 |  GKrellM is distributed in the hope that it will be useful, but WITHOUT
14 |  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 |  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 |  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, see http://www.gnu.org/licenses/
20 |
21 |
22 |  Additional permission under GNU GPL version 3 section 7
23 |
24 |  If you modify this program, or any covered work, by linking or
25 |  combining it with the OpenSSL project's OpenSSL library (or a
26 |  modified version of that library), containing parts covered by
27 |  the terms of the OpenSSL or SSLeay licenses, you are granted
28 |  additional permission to convey the resulting work.
29 |  Corresponding Source for a non-source form of such a combination
30 |  shall include the source code for the parts of OpenSSL used as well
31 |  as that of the covered work.
32 */
33 
34 #include "gkrellm.h"
35 #include "gkrellm-private.h"
36 #include "gkrellm-sysdeps.h"
37 
38 #include <math.h>
39 
40 
41 #define	BAT_CONFIG_KEYWORD	"battery"
42 
43 typedef enum
44 	{
45 	BATTERYDISPLAY_PERCENT,
46 	BATTERYDISPLAY_TIME,
47 	BATTERYDISPLAY_RATE,
48 	BATTERYDISPLAY_EOM	/* end of modes */
49 	}
50 	BatteryDisplayMode;
51 
52 
53 typedef struct
54 	{
55 	gint				id;
56 	GkrellmPanel		*panel;
57 	GkrellmKrell		*krell;
58 	GkrellmDecal		*power_decal;
59 	GkrellmDecal		*time_decal;
60 
61 	GkrellmAlert		*alert;
62 
63 	gboolean			enabled;
64 
65 	BatteryDisplayMode	display_mode;
66 	gfloat				charge_rate;	/* % / min */
67 
68 	gboolean			present,
69 						on_line,
70 						charging;
71 	gint				percent;
72 	gint				time_left;		/* In minutes, -1 if minutes unavail */
73 	}
74 	Battery;
75 
76 static GList			*battery_list;
77 static GkrellmMonitor	*mon_battery;
78 static GtkWidget		*battery_vbox;
79 
80 static Battery			*composite_battery,
81 						*launch_battery;
82 
83 static gboolean			enable_composite,
84 						enable_each,
85 						enable_estimate;
86 
87 static gint				poll_interval = 5,
88 						full_cap_fallback = 5000;
89 
90 static GkrellmLauncher	launch;
91 
92 static GkrellmAlert		*bat_alert;		/* One alert dupped for each battery */
93 
94 static gint				style_id;
95 
96 static gboolean			alert_units_percent,
97 						alert_units_need_estimate_mode;
98 
99 static void		(*read_battery_data)();
100 static void		create_battery_panel(Battery *bat, gboolean first_create);
101 
102 static gint		n_batteries;
103 
104 
105 static Battery *
battery_nth(gint n,gboolean create)106 battery_nth(gint n, gboolean create)
107 	{
108 	Battery			*bat;
109 
110 	if (n > 10)
111 		return NULL;
112 	if (n < 0)
113 		{
114 		if (!composite_battery && create)
115 			{
116 			bat = g_new0(Battery, 1);
117 			battery_list = g_list_prepend(battery_list, bat);
118 			bat->id = GKRELLM_BATTERY_COMPOSITE_ID;		/* -1 */
119 			composite_battery = bat;
120 			gkrellm_alert_dup(&bat->alert, bat_alert);
121 			}
122 		return composite_battery;
123 		}
124 
125 	if (composite_battery)
126 		++n;
127 
128 	while (   (bat = (Battery *) g_list_nth_data(battery_list, n)) == NULL
129 		   && create
130 		  )
131 		{
132 		bat = g_new0(Battery, 1);
133 		battery_list = g_list_append(battery_list, bat);
134 		bat->id = n_batteries++;
135 		gkrellm_alert_dup(&bat->alert, bat_alert);
136 		}
137 	return bat;
138 	}
139 
140 
141   /* Themers need to be able to see the battery monitor.
142   */
143 static void
read_battery_demo(void)144 read_battery_demo(void)
145 	{
146 	gboolean	on_line, charging;
147 	gint		percent, time_left;
148 	static gint	bump = 60;
149 
150 	if (bump <= 5)
151 		bump = 60;
152 	bump -= 5;
153 	on_line = bump > 45;
154 	if (on_line)
155 		{
156 		charging = TRUE;
157 		time_left = 200 + (60 - bump) * 20;
158 		percent = time_left / 5;
159 		}
160 	else
161 		{
162 		charging = FALSE;
163 		time_left = bump;
164 		percent = 1 + bump;
165 		}
166 	gkrellm_battery_assign_data(0, TRUE, on_line, charging,
167 				percent, time_left);
168 	}
169 
170 
171 static gboolean
setup_battery_interface(void)172 setup_battery_interface(void)
173 	{
174 	if (!read_battery_data && !_GK.client_mode && gkrellm_sys_battery_init())
175 		read_battery_data = gkrellm_sys_battery_read_data;
176 	if (_GK.demo)
177 		read_battery_data = read_battery_demo;
178 	return read_battery_data ? TRUE : FALSE;
179 	}
180 
181 void
gkrellm_battery_client_divert(void (* read_func)())182 gkrellm_battery_client_divert(void (*read_func)())
183 	{
184 	read_battery_data = read_func;
185 	}
186 
187 void
gkrellm_battery_assign_data(gint n,gboolean present,gboolean on_line,gboolean charging,gint percent,gint time_left)188 gkrellm_battery_assign_data(gint n, gboolean present, gboolean on_line,
189 			gboolean charging, gint percent, gint time_left)
190 	{
191 	Battery	*bat;
192 
193 	bat = battery_nth(n, TRUE);
194 	if (!bat)
195 		return;
196 	bat->present = present;
197 	bat->on_line = on_line;
198 	bat->charging = charging;
199 	bat->percent = percent;
200 	bat->time_left = time_left;
201 	}
202 
203   /* Help out some laptops with Linux ACPI bugs */
204 gint
gkrellm_battery_full_cap_fallback(void)205 gkrellm_battery_full_cap_fallback(void)
206 	{
207 	return full_cap_fallback;
208 	}
209 
210 
211 /* -------------------------------------------------------------- */
212 
213 
214 /* estimate (guess-timate?) battery time remaining, based on the rate of
215    discharge (and conversely the time to charge based on the rate of charge).
216   - some BIOS' only provide battery levels, not any estimate of the time
217     remaining
218 
219   Battery charge/discharge characteristics (or, why dc/dt doesn't really work)
220   - the charge/discharge curves of most battery types tend to be very non-
221     linear (http://www.google.com/search?q=battery+charge+discharge+curve)
222   - on discharge, most battery types will initially fall somewhat rapidly
223     from 100 percent, then flatten out and stay somewhat linear until
224     suddenly "dropping out" when nearly depleted (approx. 10-20% capacity).
225     For practical purposes we can consider this point to be the end of the
226     discharge curve. This is simple enough to model via a fixed capacity
227     offset to cut out just at the knee of this curve, and allows us to
228     reasonably approximate the rest of the curve by a linear function
229     and simple dc/dt calculation.
230   - with regard to charging, however, it's not quite so easy. With a
231     constant voltage charger, the battery capacity rises exponentially
232     (charging current decreases as battery terminal voltage rises). The
233     final stages of charging are very gradual, with a relatively long
234     period at "almost but not quite 100%".
235 
236     Unfortunately a linear extrapolation at the beginning of an
237     exponential curve will be a poor approximation to the true expected
238     time to charge, tending to be significantly undervalued. Using an
239     exponential model to estimate time to approx. 90-95% (2.5 * exp. time
240     constant) seems to give a more reasonable fit. That said, the poor
241     relative resolution at higher charge values makes estimating the
242     exponential time constant difficult towards the end of the charge
243     cycle (the curve's very nearly flat). So, I've settled on a mixed
244     model - for c < ~70 I use an exponential model, and switch to linear
245     above that (or if the charge rate seems to have otherwise "flatlined").
246 
247     Empirically, this method seems to give reasonable results [1] -
248     certainly  much better than seeing "0:50:00 to full" for a good half an
249     hour (i.e. as happens with apmd, which uses a linear model for both
250     charging + discharging). Note that a constant-current charger should
251     be pretty well linear all the way along the charge curve, which means
252     the linear rate extrapolation should work well in this case. The user
253     can choose which model they wish to use via estimate_model.
254 
255     [1] I logged my Compaq Armada M300's capacity (via /proc/apm) over one
256     complete discharge/charge cycle (machine was idle the whole time). The
257     discharge curve was linear to approx. 14% when the BIOS alerted of
258     impending doom; upon plugging in the external power supply the capacity
259     rose exponentially to 100%, with a time constant of approx. 0.8 hr (i.e.
260     approx. 2+ hrs to full charge).
261 
262   Linear rate of change calculation:
263   - in an ideal, continuous world, estimated time to 0(100) would simply
264     be the remaining capacity divided by the charge rate
265        ttl = c / dc(t)/dt
266   - alas, the reported battery capacity is bound to integer values thus
267     c(t) is a discontinuous function. i.e. has fairly large steps. And of
268     course then dc/dt is undefined at the discontinuities.
269   - to avoid this issue the rate of charge is determined by the deltas from
270     the start of the last state change (charge/discharge cycle) (time T)
271        ttl(t) = c(t) / ((C - c(t)) / (T - t))    C = charge at time T
272     Furthermore, the rate changes are windowed and filtered to mitigate
273     c(t) transients (e.g. at the start of discharge) and smooth over
274     discontinuities (and fudge for battery characteristics, ref. above).
275 */
276 
277 #define BAT_SLEEP_DETECT 300		/* interval of >300s => must have slept */
278 #define BAT_DISCHARGE_TRANSIENT 10	/* ignore first 10% of discharge cycle */
279 #define BAT_EMPTY_CAPACITY 12		/* when is the battery "flat"? */
280 #define BAT_RATECHANGE_WINDOW 90	/* allow rate changes after 90s */
281 #define BAT_CHARGE_MODEL_LIMIT 60	/* linear charge model cutover point */
282 #define BAT_RATE_SMOOTHING 0.3		/* rate smoothing weight */
283 
284 /* #define BAT_ESTIMATE_DEBUG */
285 
286   /* user-provided nominal battery runtimes, hrs (used to determine initial
287   |  discharge, stable, charge rate (%/min))
288   */
289 static gfloat	estimate_runtime[2] = {0};
290 static gint		estimate_model[2] = {0};
291 static gboolean	reset_estimate;
292 
293 static void
estimate_battery_time_left(Battery * bat)294 estimate_battery_time_left(Battery *bat)
295 	{
296 	/* ?0 = at time 0 (state change); ?1 = at last "sample" (rate change) */
297 	static time_t	t0 = -1, t1;
298 	static gint		p0, p1;
299 	static gint		c0;
300 	static gfloat	rate = 0;
301 	static time_t	dt;
302 	static gint		dp;
303 	time_t			t = time(NULL);
304 
305 	/* 1 charging; 0 power on and stable; -1 discharging */
306 	gint			charging = bat->charging ? 1 : (bat->on_line ? 0 : -1);
307 
308 #ifdef BAT_ESTIMATE_DEBUG
309 		fprintf(stderr, "%ld bc?=%d ac?=%d (%+d) bp=%d\t", t,
310 			bat->charging, bat->on_line, charging,
311 			bat->percent);
312 #endif
313 
314 	if (   reset_estimate || t0 < 0 || c0 != charging
315 		|| (t - t1) > BAT_SLEEP_DETECT
316 	   )
317 		{
318 		/* init, state change, or sleep/hibernation
319 		*/
320 		reset_estimate = FALSE;
321 		c0 = charging;
322 
323 		t0 = t1 = t;
324 		if (charging < 0 && (bat->percent > 100 - BAT_DISCHARGE_TRANSIENT))
325 			p0 = p1 = 100 - BAT_DISCHARGE_TRANSIENT;
326 		else
327 			p0 = p1 = bat->percent;
328 		dp = dt = 0;
329 		rate = 0.0;
330 
331 		/* convert runtime (hrs) to signed rate (%/min)
332 		*/
333 		if (charging < 0)
334 			rate = -100 / (estimate_runtime[0] * 60);
335 		else if (charging > 0)
336 			rate =  100 / (estimate_runtime[1] * 60);
337 
338 #ifdef BAT_ESTIMATE_DEBUG
339 		fprintf(stderr, "[r = %.3f]\t", rate);
340 #endif
341 		}
342 	else
343 		{
344 		time_t	dt1 = t - t1;		/* delta since last rate change */
345 		gint	dp1 = bat->percent - p1;
346 
347 		/* time for a rate change?
348 		*/
349 		if (   dt1 > BAT_RATECHANGE_WINDOW
350 			&& ((charging > 0 && dp1 >= 0) || (charging < 0 && dp1 <= 0))
351 		   )
352 			{
353 			dt = t - t0;					/* since state change */
354 			dp = bat->percent - p0;
355 
356 			if (dp1 == 0)	/* flatlining (dp/dt = 0) */
357 				rate = (1 - BAT_RATE_SMOOTHING/4) * rate;
358 			else
359 				rate = BAT_RATE_SMOOTHING *
360 						((gdouble) dp / (gdouble) (dt/60)) +
361 						(1 - BAT_RATE_SMOOTHING) * rate;
362 
363 #ifdef BAT_ESTIMATE_DEBUG
364 			fprintf(stderr, "%d [dp = %+d dt = %.2f rate = %.3f]\t",
365 					(gint) dp1, dp, (gdouble) dt / 60, rate);
366 #endif
367 
368 			t1 = t;
369 			p1 = bat->percent;
370 			}
371 		}
372 
373 	if (charging && rate != 0.0)	/* (dis)charging */
374 		{
375 		gfloat	eta;
376 		gint 	p = charging > 0 ? 100 - bat->percent :
377 						bat->percent - BAT_EMPTY_CAPACITY;
378 
379 		if (   charging > 0 && estimate_model[1]
380 			&& bat->percent < BAT_CHARGE_MODEL_LIMIT && dp > 0
381 		   )
382 			/* charging, use exponential: eta =~ 2.5 * time-constant (~=92%) */
383 			eta = -2.5 * dt/60 / (log(1 - (gdouble)dp/(gdouble)(p+dp)));
384 		else
385 			eta = fabs((gdouble)p / rate);	/* use linear */
386 
387 #ifdef BAT_ESTIMATE_DEBUG
388 		fprintf(stderr, "eta = %.2f\t", eta);
389 #endif
390 
391 		/* round off to nearest 5 mins */
392 		bat->time_left = (gint)((eta > 0 ? eta + 2.5: 0) / 5) * 5;
393 		bat->charge_rate = rate;
394 		}
395 	else
396 		{
397 		bat->time_left = INT_MAX;	/* inf */
398 		bat->charge_rate = 0.0;
399 		}
400 
401 #ifdef BAT_ESTIMATE_DEBUG
402 		fprintf(stderr, "\n");
403 #endif
404 	}
405 
406 static void
draw_time_left_decal(Battery * bat,gboolean force)407 draw_time_left_decal(Battery *bat, gboolean force)
408 	{
409 	GkrellmDecal	*d;
410 	gchar			buf[16];
411 	gint			x, w, t;
412 	int				battery_display_mode = bat->display_mode;
413 	static BatteryDisplayMode	last_mode = BATTERYDISPLAY_EOM;
414 
415 	if (!bat->panel)
416 		return;
417 	if (bat->time_left == -1)
418 		battery_display_mode = BATTERYDISPLAY_PERCENT;
419 	if (last_mode != battery_display_mode)
420 		force = TRUE;
421 	last_mode = bat->display_mode;
422 
423 	switch (battery_display_mode)
424 		{
425 		case BATTERYDISPLAY_TIME:
426 			t = bat->time_left;
427 			if (t == INT_MAX || t == INT_MIN)
428 				snprintf(buf, sizeof(buf), "--");
429 			else
430 				snprintf(buf, sizeof(buf), "%2d:%02d", t / 60, t % 60);
431 			break;
432 
433 		case BATTERYDISPLAY_RATE:
434 			/* t is used by draw_decal_text() to see if a refresh is reqd */
435 			t = (gint) (bat->charge_rate * 100.0);
436 			snprintf(buf, sizeof(buf), "%0.1f%%/m",
437 						bat->charge_rate);
438 			break;
439 
440 		case BATTERYDISPLAY_PERCENT:
441 		default:
442 			t = bat->percent;
443 			if (t == -1)	/* APM battery flags should cause hide... but */
444 				snprintf(buf, sizeof(buf), "no bat");
445 			else
446 				snprintf(buf, sizeof(buf), "%d%%", t);
447 			break;
448 		}
449 
450 	d = bat->time_decal;
451 	w = gkrellm_gdk_string_width(d->text_style.font, buf);
452 	x = (d->w - w) / 2;
453 	if (x < 0)
454 		x = 0;
455 	d->x_off = x;
456 	gkrellm_draw_decal_text(bat->panel, d, buf, force ? -1 : t);
457 	}
458 
459 static void
update_battery_panel(Battery * bat)460 update_battery_panel(Battery *bat)
461 	{
462 	GkrellmPanel	*p  = bat->panel;
463 
464 	if (!p)
465 		return;
466 	if (!bat->present)
467 		{	/* Battery can be removed while running */
468 		gkrellm_panel_hide(p);
469 		return;
470 		}
471 	gkrellm_panel_show(p);
472 
473 	if (bat->time_left > 0 && bat->charging)
474 		bat->charge_rate = (gfloat) (100 - bat->percent) / (gfloat) bat->time_left;
475 	else
476 		bat->charge_rate = 0.0;
477 
478 	if (enable_estimate)
479 		estimate_battery_time_left(bat);
480 
481 	if (bat->on_line)
482 		{
483 		gkrellm_reset_alert(bat->alert);
484 		gkrellm_freeze_alert(bat->alert);
485 		gkrellm_draw_decal_pixmap(p, bat->power_decal, D_MISC_AC);
486 		}
487 	else
488 		{
489 		if (   (bat == composite_battery && enable_composite)
490 			|| (bat != composite_battery && enable_each)
491 			)
492 			{
493 			gkrellm_thaw_alert(bat->alert);
494 			gkrellm_check_alert(bat->alert, alert_units_percent
495 						? bat->percent : bat->time_left);
496 			}
497 		gkrellm_draw_decal_pixmap(p, bat->power_decal, D_MISC_BATTERY);
498 		}
499 	draw_time_left_decal(bat, FALSE);
500 	gkrellm_update_krell(p, bat->krell, bat->percent);
501 	gkrellm_draw_panel_layers(p);
502 	}
503 
504 static void
update_battery(void)505 update_battery(void)
506 	{
507 	GList		*list;
508 	Battery		*bat;
509 	static gint	seconds = 0;
510 
511 	if (!enable_each && !enable_composite)
512 		return;
513 	if (GK.second_tick)
514 		{
515 		if (seconds == 0)
516 			{
517 			for (list = battery_list; list; list = list->next)
518 				((Battery *) list->data)->present = FALSE;
519 
520 			(*read_battery_data)();
521 			for (list = battery_list; list; list = list->next)
522 				{
523 				bat = (Battery *) list->data;
524 				if (!bat->panel)
525 					create_battery_panel(bat, TRUE);
526 				if (bat->enabled)
527 					update_battery_panel(bat);
528 				}
529 			}
530 		seconds = (seconds + 1) % poll_interval;
531 		}
532 	}
533 
534 static gboolean
cb_expose_event(GtkWidget * widget,GdkEventExpose * ev,GkrellmPanel * p)535 cb_expose_event(GtkWidget *widget, GdkEventExpose *ev, GkrellmPanel *p)
536 	{
537 	gdk_draw_drawable(gtk_widget_get_window(widget),
538                           gtk_widget_get_style(widget)->fg_gc[gtk_widget_get_state(widget)], p->pixmap,
539 			ev->area.x, ev->area.y, ev->area.x, ev->area.y,
540 			ev->area.width, ev->area.height);
541 	return FALSE;
542 	}
543 
544 static gboolean
cb_panel_enter(GtkWidget * w,GdkEventButton * ev,Battery * bat)545 cb_panel_enter(GtkWidget *w, GdkEventButton *ev, Battery *bat)
546 	{
547 	gkrellm_decal_on_top_layer(bat->time_decal, TRUE);
548 	gkrellm_draw_panel_layers(bat->panel);
549 	return FALSE;
550 	}
551 
552 static gboolean
cb_panel_leave(GtkWidget * w,GdkEventButton * ev,Battery * bat)553 cb_panel_leave(GtkWidget *w, GdkEventButton *ev, Battery *bat)
554 	{
555 	gkrellm_decal_on_top_layer(bat->time_decal, FALSE);
556 	gkrellm_draw_panel_layers(bat->panel);
557 	return FALSE;
558 	}
559 
560 
561 static gboolean
cb_panel_press(GtkWidget * widget,GdkEventButton * ev,Battery * bat)562 cb_panel_press(GtkWidget *widget, GdkEventButton *ev, Battery *bat)
563 	{
564 	GkrellmDecal			*d;
565 	static gboolean	time_unavailable_warned;
566 
567 	d = launch.decal;
568 	if (ev->button == 3)
569 		gkrellm_open_config_window(mon_battery);
570 	else if (   ev->button == 2
571 			 || (ev->button == 1 && !d)
572 			 || (ev->button == 1 && d && ev->x < d->x)
573 			)
574 		{
575 		if (bat->time_left == -1 && bat->present)
576 			{
577 			if (!time_unavailable_warned)
578 				gkrellm_message_dialog(_("GKrellM Battery"),
579 					_("Battery times are unavailable.  You\n"
580 					  "could select the Estimated Time option."));
581 			time_unavailable_warned = TRUE;
582 			bat->display_mode = BATTERYDISPLAY_PERCENT;
583 			}
584 		else
585 			{
586 			bat->display_mode++;
587 			if (bat->display_mode == BATTERYDISPLAY_EOM)
588 				bat->display_mode = 0;
589 
590 			draw_time_left_decal(bat, TRUE);
591 			gkrellm_draw_panel_layers(bat->panel);
592 			gkrellm_config_modified();
593 			}
594 		}
595 	return FALSE;
596 	}
597 
598 static void
create_battery_panel(Battery * bat,gboolean first_create)599 create_battery_panel(Battery *bat, gboolean first_create)
600 	{
601 	GkrellmPanel		*p;
602 	GkrellmStyle		*style;
603 	GkrellmMargin		*m;
604 	gint				x, w;
605 
606 	if (!bat->panel)
607 		bat->panel = gkrellm_panel_new0();
608 	p = bat->panel;
609 	style = gkrellm_meter_style(style_id);
610 	m = gkrellm_get_style_margins(style);
611 	bat->power_decal = gkrellm_create_decal_pixmap(p,
612 			gkrellm_decal_misc_pixmap(), gkrellm_decal_misc_mask(),
613 			N_MISC_DECALS, style, m->left, -1);
614 
615 	x = bat->power_decal->x + bat->power_decal->w + 2;
616 	w = gkrellm_chart_width() - x - m->right;
617 	bat->time_decal = gkrellm_create_decal_text(p, "8/%",
618 						gkrellm_meter_textstyle(style_id),
619 						style, x, -1, w);
620 
621 	bat->krell = gkrellm_create_krell(p,
622 						gkrellm_krell_meter_piximage(style_id), style);
623 	gkrellm_monotonic_krell_values(bat->krell, FALSE);
624 	gkrellm_set_krell_full_scale(bat->krell, 100, 1);
625 
626 	gkrellm_panel_configure(p, NULL, style);
627 	gkrellm_panel_create(battery_vbox, mon_battery, p);
628 
629 	/* Center the decals with respect to each other.
630 	*/
631 	if (bat->power_decal->h > bat->time_decal->h)
632 		bat->time_decal->y += (bat->power_decal->h - bat->time_decal->h) / 2;
633 	else
634 		bat->power_decal->y += (bat->time_decal->h - bat->power_decal->h) / 2;
635 
636 	if (first_create)
637 		{
638 		g_signal_connect(G_OBJECT(p->drawing_area), "expose_event",
639 				G_CALLBACK(cb_expose_event), p);
640 		g_signal_connect(G_OBJECT(p->drawing_area), "button_press_event",
641 				G_CALLBACK(cb_panel_press), bat);
642 		g_signal_connect(G_OBJECT(p->drawing_area), "enter_notify_event",
643                 G_CALLBACK(cb_panel_enter), bat);
644 		g_signal_connect(G_OBJECT(p->drawing_area), "leave_notify_event",
645                 G_CALLBACK(cb_panel_leave), bat);
646 		 }
647 
648 	gkrellm_setup_decal_launcher(p, &launch, bat->time_decal);
649 	if (   (bat == composite_battery && enable_composite)
650 		|| (bat->id == 0 && composite_battery && !enable_composite)
651 		|| (bat->id == 0 && !composite_battery)
652 		)
653 		launch_battery = bat;
654 
655 	if (bat == composite_battery)
656 		bat->enabled = enable_composite;
657 	else
658 		bat->enabled = enable_each;
659 
660 	if (bat->enabled)
661 		update_battery_panel(bat);
662 	else
663 		gkrellm_panel_hide(p);
664 	}
665 
666 static void
spacer_visibility(void)667 spacer_visibility(void)
668 	{
669 	GList		*list;
670 	Battery		*bat;
671 	gboolean	enabled = FALSE;
672 
673 	for (list = battery_list; list; list = list->next)
674 		{
675 		bat = (Battery *) list->data;
676 		enabled |= bat->enabled;
677 		}
678 	if (enabled)
679 		gkrellm_spacers_show(mon_battery);
680 	else
681 		gkrellm_spacers_hide(mon_battery);
682 	}
683 
684 static void
create_battery(GtkWidget * vbox,gint first_create)685 create_battery(GtkWidget *vbox, gint first_create)
686 	{
687 	GList	*list;
688 	Battery	*bat;
689 
690 	battery_vbox = vbox;
691 	if (_GK.demo)
692 		enable_each = TRUE;
693 	for (list = battery_list; list; list = list->next)
694 		{
695 		bat = (Battery *) list->data;
696 		create_battery_panel(bat, first_create);
697 		}
698 	spacer_visibility();
699 	}
700 
701 
702   /* Expand alert command substitution variables:
703   |  $H - hostname           $n - battery id
704   |  $t - time left          $p - percent
705   |  $o - online (boolean)   $c - charging (boolean)
706   */
707 static void
cb_command_process(GkrellmAlert * alert,gchar * src,gchar * buf,gint size,Battery * bat)708 cb_command_process(GkrellmAlert *alert, gchar *src, gchar *buf, gint size,
709 			Battery *bat)
710 	{
711 	gchar		c, *s;
712 	gint		len, value;
713 
714 	if (!buf || size < 1)
715 		return;
716 	--size;
717 	*buf = '\0';
718 	if (!src)
719 		return;
720 	for (s = src; *s != '\0' && size > 0; ++s)
721 		{
722 		len = 1;
723 		if (*s == '$' && *(s + 1) != '\0')
724 			{
725 			value = -1;
726 			if ((c = *(s + 1)) == 'H')
727 				len = snprintf(buf, size, "%s", gkrellm_sys_get_host_name());
728 			else if (c == 'n' && bat != composite_battery)
729 				value = bat->id;
730 			else if (c == 't')
731 				value = bat->time_left;
732 			else if (c == 'p')
733 				value = bat->percent;
734 			else if (c == 'o')
735 				value = bat->on_line;
736 			else if (c == 'c')
737 				value = bat->charging;
738 			else
739 				len = 0;
740 
741 			if (value >= 0)
742 				len = snprintf(buf, size, "%d", value);
743 			++s;
744 			}
745 		else
746 			*buf = *s;
747 		size -= len;
748 		buf += len;
749 		}
750 	*buf = '\0';
751 	}
752 
753 static void
cb_battery_alert_trigger(GkrellmAlert * alert,Battery * bat)754 cb_battery_alert_trigger(GkrellmAlert *alert, Battery *bat)
755 	{
756 	GkrellmAlertdecal	*ad;
757 	GkrellmDecal		*d;
758 
759 	alert->panel = bat->panel;
760 	ad = &alert->ad;
761 	d = bat->time_decal;
762 	ad->x = d->x + 1;
763 	ad->y = d->y - 2;
764 	ad->w = d->w - 1;
765 	ad->h = d->h + 4;
766 	gkrellm_render_default_alert_decal(alert);
767 	}
768 
769 static void
dup_battery_alert(void)770 dup_battery_alert(void)
771 	{
772 	GList	*list;
773 	Battery	*bat;
774 
775 	for (list = battery_list; list; list = list->next)
776 		{
777 		bat = (Battery *) list->data;
778 		gkrellm_alert_dup(&bat->alert, bat_alert);
779 		gkrellm_alert_trigger_connect(bat->alert,
780 					cb_battery_alert_trigger, bat);
781 		gkrellm_alert_command_process_connect(bat->alert,
782 					cb_command_process, bat);
783 		}
784 	}
785 
786 
787   /* If the OS reports battery times, alerts will always have minutes units.
788   |  If the OS does not report battery times the initial alert create will
789   |  have minutes units if the estimate time option is enabled and it will
790   |  have battery percent level units if estimate time option is off.  Alert
791   |  creates from load config will have units in effect at last save config.
792   */
793 static void
create_alert(void)794 create_alert(void)
795 	{
796 	Battery	*bat;
797 
798 	if (!battery_list)
799 		return;
800 	bat = (Battery *) battery_list->data;
801 
802 	if (!bat_alert)
803 		{
804 		alert_units_need_estimate_mode = FALSE;
805 
806 		if (   alert_units_percent
807 			|| (bat->time_left == -1 && !enable_estimate)
808 		   )
809 			{
810 			if (bat->time_left == -1)
811 				alert_units_percent = TRUE;
812 			bat_alert = gkrellm_alert_create(NULL, _("Battery"),
813 					_("Battery Percent Limits"),
814 					FALSE, TRUE, TRUE, 99, 0, 1, 10, 0);
815 			}
816 		else
817 			{
818 			bat_alert = gkrellm_alert_create(NULL, _("Battery"),
819 					_("Battery Minutes Remaining Limits"),
820 					FALSE, TRUE, TRUE, 500, 0, 1, 10, 0);
821 			if (bat->time_left == -1)
822 				alert_units_need_estimate_mode = TRUE;
823 			}
824 		}
825 	gkrellm_alert_config_connect(bat_alert, dup_battery_alert, NULL);
826 
827 	/* This alert is a master to be dupped and is itself never checked */
828 	}
829 
830 
831 static void
save_battery_config(FILE * f)832 save_battery_config(FILE *f)
833 	{
834 	GList	*list;
835 	Battery	*bat;
836 
837 	fprintf(f, "%s enable %d\n", BAT_CONFIG_KEYWORD, enable_each);
838 	fprintf(f, "%s enable_composite %d\n", BAT_CONFIG_KEYWORD,
839 					enable_composite);
840 	fprintf(f, "%s estimate_time %d\n", BAT_CONFIG_KEYWORD, enable_estimate);
841 
842 	/* 2.1.15: scale saved float values to avoid decimal points in the config.
843 	*/
844 	fprintf(f, "%s estimate_time_discharge %.0f\n", BAT_CONFIG_KEYWORD,
845 				estimate_runtime[0] * GKRELLM_FLOAT_FACTOR);
846 	fprintf(f, "%s estimate_time_charge %.0f\n", BAT_CONFIG_KEYWORD,
847 				estimate_runtime[1] * GKRELLM_FLOAT_FACTOR);
848 	fprintf(f, "%s estimate_time_charge_model %d\n", BAT_CONFIG_KEYWORD,
849 				estimate_model[1]);
850 	fprintf(f, "%s full_cap_fallback %d\n", BAT_CONFIG_KEYWORD,
851 						full_cap_fallback);
852 	if (!_GK.client_mode)
853 		fprintf(f, "%s poll_interval %d\n", BAT_CONFIG_KEYWORD, poll_interval);
854 	if (launch.command)
855 		fprintf(f, "%s launch1 %s\n", BAT_CONFIG_KEYWORD, launch.command);
856 	if (launch.tooltip_comment)
857 		fprintf(f, "%s tooltip_comment %s\n",
858 					BAT_CONFIG_KEYWORD, launch.tooltip_comment);
859 	fprintf(f, "%s alert_units_percent %d\n", BAT_CONFIG_KEYWORD,
860 				alert_units_percent);
861 	for (list = battery_list; list; list = list->next)
862 		{
863 		bat = (Battery *) list->data;
864 		if (bat == composite_battery)	/* Don't 2.1.16 backwards break */
865 			fprintf(f, "%s display_mode_composite %d %d\n", BAT_CONFIG_KEYWORD,
866 						bat->display_mode, bat->id);
867 		else
868 			fprintf(f, "%s display_mode %d %d\n", BAT_CONFIG_KEYWORD,
869 						bat->display_mode, bat->id);
870 		}
871 	gkrellm_save_alertconfig(f, bat_alert, BAT_CONFIG_KEYWORD, NULL);
872 	}
873 
874 static void
load_battery_config(gchar * arg)875 load_battery_config(gchar *arg)
876 	{
877 	Battery			*bat;
878 	gint			display_mode, n = 0;
879 	gchar			config[32], item[CFG_BUFSIZE],
880 					name[CFG_BUFSIZE], item1[CFG_BUFSIZE];
881 
882 	if (sscanf(arg, "%31s %[^\n]", config, item) == 2)
883 		{
884 		if (!strcmp(config, "enable"))
885 			sscanf(item, "%d", &enable_each);
886 		if (!strcmp(config, "enable_composite"))
887 			sscanf(item, "%d", &enable_composite);
888 		else if (!strcmp(config, "estimate_time"))
889 			sscanf(item, "%d", &enable_estimate);
890 		else if (!strcmp(config, "estimate_time_discharge"))
891 			{
892 			sscanf(item, "%f", &estimate_runtime[0]);
893 			estimate_runtime[0] /= _GK.float_factor;
894 			}
895 		else if (!strcmp(config, "estimate_time_charge"))
896 			{
897 			sscanf(item, "%f", &estimate_runtime[1]);
898 			estimate_runtime[1] /= _GK.float_factor;
899 			}
900 		else if (!strcmp(config, "estimate_time_charge_model"))
901 			sscanf(item, "%d", &estimate_model[1]);
902 		else if (!strcmp(config, "full_cap_fallback"))
903 			sscanf(item, "%d", &full_cap_fallback);
904 		else if (!strcmp(config, "poll_interval"))
905 			sscanf(item, "%d", &poll_interval);
906 		else if (!strcmp(config, "launch1"))
907 			launch.command = g_strdup(item);
908 		else if (!strcmp(config, "tooltip_comment"))
909 			launch.tooltip_comment = g_strdup(item);
910 		else if (!strncmp(config, "display_mode", 12))
911 			{
912 			sscanf(item, "%d %d", &display_mode, &n);
913 			if ((bat = battery_nth(n, FALSE)) != NULL)
914 				bat->display_mode = display_mode;
915 			}
916 		else if (!strcmp(config, "alert_units_percent"))
917 			sscanf(item, "%d", &alert_units_percent);
918 		else if (!strcmp(config, GKRELLM_ALERTCONFIG_KEYWORD))
919 			{
920 			if (!strncmp(item, "BAT", 3))	/* Config compat musical chairs */
921 				sscanf(item, "%32s %[^\n]", name, item1);
922 			else
923 				strcpy(item1, item);
924 			create_alert();
925 			gkrellm_load_alertconfig(&bat_alert, item1);
926 			dup_battery_alert();
927 			}
928 		}
929 	}
930 
931 
932 static GtkWidget	*launch_entry,
933 					*tooltip_entry;
934 
935 static GtkWidget	*estimate_runtime_spin_button[2],
936 					*estimate_model_button[2];
937 
938 static void
update_battery_panels(void)939 update_battery_panels(void)
940 	{
941 	GList		*list;
942 	Battery		*bat;
943 
944 	for (list = battery_list; list; list = list->next)
945 		{
946 		bat = (Battery *) list->data;
947 		if (bat->enabled)
948 			update_battery_panel(bat);
949 		}
950 	}
951 
952 static void
cb_set_alert(GtkWidget * button,Battery * bat)953 cb_set_alert(GtkWidget *button, Battery *bat)
954 	{
955 	create_alert();
956 	gkrellm_alert_config_window(&bat_alert);
957 	}
958 
959 static void
alert_units_percent_cb(GtkToggleButton * button,gpointer data)960 alert_units_percent_cb(GtkToggleButton *button, gpointer data)
961 	{
962 	GList	*list;
963 	Battery	*bat;
964 
965 	alert_units_percent = button->active;
966 
967 	if (bat_alert)
968 		{
969 		for (list = battery_list; list; list = list->next)
970 			{
971 			bat = (Battery *) list->data;
972 			gkrellm_reset_alert(bat->alert);
973 			gkrellm_alert_destroy(&bat->alert);
974 			}
975 		gkrellm_alert_destroy(&bat_alert);
976 		gkrellm_config_message_dialog(_("GKrellM Battery"),
977 				_("The Battery alert units are changed\n"
978 				  "and the alert must be reconfigured."));
979 		}
980 	}
981 
982 static void
cb_enable_estimate(GtkToggleButton * button,GtkWidget * box)983 cb_enable_estimate(GtkToggleButton *button, GtkWidget *box)
984 	{
985 	GList		*list;
986 	Battery		*bat;
987 	gboolean	enable;
988 
989 	enable = button->active;
990 	gtk_widget_set_sensitive(box, enable);
991 
992 	if (enable_estimate != enable)
993 		{
994 		/* If alert units need estimated time mode and estimation switches off,
995 		|  destroy the alert because the alert units can now only be percent.
996 		*/
997 		for (list = battery_list; list; list = list->next)
998 			{
999 			bat = (Battery *) list->data;
1000 			if (bat->alert && (!enable && alert_units_need_estimate_mode))
1001 				gkrellm_alert_destroy(&bat->alert);
1002 			}
1003 		if (   bat_alert
1004 		    && (!enable && alert_units_need_estimate_mode)
1005 		    && !alert_units_percent
1006 		   )
1007 			{
1008 			gkrellm_alert_destroy(&bat_alert);
1009 			gkrellm_config_message_dialog(_("GKrellM Battery"),
1010 					_("The Battery alert units are changed\n"
1011 					  "and the alert must be reconfigured."));
1012 			}
1013 		}
1014 	enable_estimate = enable;
1015 	update_battery_panels();
1016 	}
1017 
1018 static void
cb_runtime(GtkWidget * entry,gpointer data)1019 cb_runtime(GtkWidget *entry, gpointer data)
1020 	{
1021 	gint	i = GPOINTER_TO_INT(data) - 1;
1022 
1023 	estimate_runtime[i] = gtk_spin_button_get_value(
1024 			GTK_SPIN_BUTTON(estimate_runtime_spin_button[i]));
1025 	reset_estimate = TRUE;
1026 	update_battery_panels();
1027 	}
1028 
1029 
1030 static void
cb_enable(GtkToggleButton * button,gpointer data)1031 cb_enable(GtkToggleButton *button, gpointer data)
1032 	{
1033 	GList	*list;
1034 	Battery	*bat;
1035 	gint	which  = GPOINTER_TO_INT(data);
1036 
1037 	if (which == 0)
1038 		enable_composite = enable_each = button->active;
1039 	else if (which == 1)
1040 		enable_each = button->active;
1041 	else if (which == 2)
1042 		enable_composite = button->active;
1043 
1044 	for (list = battery_list; list; list = list->next)
1045 		{
1046 		bat = (Battery *) list->data;
1047 		if (bat == composite_battery)
1048 			bat->enabled = enable_composite;
1049 		else
1050 			bat->enabled = enable_each;
1051 
1052 		if (bat->enabled)
1053 			{
1054 			gkrellm_panel_show(bat->panel);
1055 			update_battery_panel(bat);
1056 			}
1057 		else
1058 			{
1059 			gkrellm_reset_alert(bat->alert);
1060 			gkrellm_panel_hide(bat->panel);
1061 			}
1062 		}
1063 	if (composite_battery)
1064 		{
1065 		gkrellm_remove_launcher(&launch);
1066 
1067 		if (composite_battery->enabled)
1068 			{
1069 			gkrellm_setup_decal_launcher(composite_battery->panel,
1070 						&launch, composite_battery->time_decal);
1071 			launch_battery = composite_battery;
1072 			}
1073 		else
1074 			{
1075 			bat = battery_nth(0, FALSE);
1076 			if (bat && bat->enabled)
1077 				{
1078 				gkrellm_setup_decal_launcher(bat->panel,
1079 						&launch, bat->time_decal);
1080 				launch_battery = bat;
1081 				}
1082 			}
1083 
1084 		}
1085 	spacer_visibility();
1086 	}
1087 
1088 static void
cb_estimate_model(GtkWidget * entry,gpointer data)1089 cb_estimate_model(GtkWidget *entry, gpointer data)
1090 	{
1091 	gint	i = GPOINTER_TO_INT(data);
1092 
1093 	estimate_model[i] =
1094 			GTK_TOGGLE_BUTTON(estimate_model_button[i])->active;
1095 	reset_estimate = TRUE;
1096 	update_battery_panels();
1097 	}
1098 
1099 static void
cb_poll_interval(GtkWidget * entry,GtkSpinButton * spin)1100 cb_poll_interval(GtkWidget *entry, GtkSpinButton *spin)
1101 	{
1102 	poll_interval = gtk_spin_button_get_value_as_int(spin);
1103 	}
1104 
1105 static void
cb_launch_entry(GtkWidget * widget,gpointer data)1106 cb_launch_entry(GtkWidget *widget, gpointer data)
1107 	{
1108 	if (!launch_battery)
1109 		return;
1110 	gkrellm_apply_launcher(&launch_entry, &tooltip_entry,
1111 				launch_battery->panel, &launch, gkrellm_launch_button_cb);
1112 	}
1113 
1114 
1115 static gchar	*battery_info_text[] =
1116 {
1117 N_("<h>Setup\n"),
1118 N_("<b>Display Estimated Time\n"),
1119 N_("If battery times are not reported by the BIOS or if the reported times\n"
1120 "are inaccurate, select this option to display a battery time remaining or\n"
1121 "time to charge which is calculated based on the current battery percentage\n"
1122 "level, user supplied total battery times, and a linear extrapolation model.\n"),
1123 "\n",
1124 N_("<b>Total Battery Times\n"),
1125 N_("Enter the typical total run time and total charge time in hours for your\n"
1126 "battery.\n"),
1127 "\n",
1128 N_("<b>Exponential Charge Model\n"),		/* xgettext:no-c-format */
1129 N_("For some charging systems battery capacity rises exponentially, which\n"
1130 "means the simple linear model will grossly underestimate the time to 100%.\n"
1131 "Select the exponential model for more accuracy in this case.\n"),
1132 "\n",
1133 "<b>",
1134 N_("Alerts"),
1135 "\n",
1136 N_("Substitution variables may be used in alert commands.\n"),
1137 N_("\t$p    battery percent level.\n"),
1138 N_("\t$t    battery time left.\n"),
1139 N_("\t$n    battery number.\n"),
1140 N_("\t$o    online state (boolean).\n"),
1141 N_("\t$c    charging state (boolean).\n"),
1142 "\n",
1143 N_("<h>Mouse Button Actions:\n"),
1144 N_("<b>\tLeft "),
1145 N_(" click on the charging state decal to toggle the display mode\n"
1146 "\t\tbetween a minutes, percentage, or charging rate display.\n"),
1147 N_("<b>\tMiddle "),
1148 N_(" clicking anywhere on the Battery panel also toggles the display mode.\n")
1149 };
1150 
1151 static void
create_battery_tab(GtkWidget * tab_vbox)1152 create_battery_tab(GtkWidget *tab_vbox)
1153 	{
1154 	GtkWidget	*tabs, *table, *vbox, *vbox1, *vbox2,
1155 				*hbox, *hbox2, *text;
1156 	Battery		*bat;
1157 	gint		i;
1158 
1159 	tabs = gtk_notebook_new();
1160 	gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
1161 	gtk_box_pack_start(GTK_BOX(tab_vbox), tabs, TRUE, TRUE, 0);
1162 
1163 /* -- Setup tab */
1164 	vbox = gkrellm_gtk_notebook_page(tabs, _("Options"));
1165 	vbox = gkrellm_gtk_framed_vbox(vbox, NULL, 2, TRUE, 10, 6);
1166 
1167 	if (composite_battery && n_batteries > 0)
1168 		{
1169 		vbox1 = gkrellm_gtk_category_vbox(vbox, _("Enable"), 2, 2, TRUE);
1170 
1171 		gkrellm_gtk_check_button_connected(vbox1, NULL,
1172 				enable_composite, FALSE, FALSE, 0,
1173 				cb_enable, GINT_TO_POINTER(2),
1174 				_("Composite Battery"));
1175 		gkrellm_gtk_check_button_connected(vbox1, NULL,
1176 				enable_each, FALSE, FALSE, 0,
1177 				cb_enable, GINT_TO_POINTER(1),
1178 				_("Real Batteries"));
1179 		}
1180 	else
1181 		gkrellm_gtk_check_button_connected(vbox, NULL,
1182 				enable_each, FALSE, FALSE, 10,
1183 				cb_enable, GINT_TO_POINTER(0),
1184 				_("Enable Battery"));
1185 
1186 	vbox2 = gtk_vbox_new(FALSE, 0);
1187 	gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
1188 
1189 	vbox1 = gtk_vbox_new(FALSE, 0);
1190 	gkrellm_gtk_check_button_connected(vbox2, NULL,
1191 			enable_estimate, FALSE, FALSE, 2,
1192 			cb_enable_estimate, vbox1,
1193 			_("Display estimated time remaining and time to charge"));
1194 	gtk_widget_set_sensitive(vbox1, enable_estimate ? TRUE : FALSE);
1195 	gtk_box_pack_start(GTK_BOX(vbox2), vbox1, FALSE, FALSE, 0);
1196 
1197 	vbox1 = gkrellm_gtk_category_vbox(vbox1, NULL, 0, 0, TRUE);
1198 	gkrellm_gtk_spin_button(vbox1, &estimate_runtime_spin_button[0],
1199 			estimate_runtime[0], 0.1, 24, 0.1, 1.0, 1, 55,
1200 			cb_runtime, GINT_TO_POINTER(1), FALSE,
1201 			_("Total battery run time in hours"));
1202 	gkrellm_gtk_spin_button(vbox1, &estimate_runtime_spin_button[1],
1203 			estimate_runtime[1], 0.1, 24, 0.1, 1.0, 1, 55,
1204 			cb_runtime, GINT_TO_POINTER(2), FALSE,
1205 			_("Total battery charge time in hours"));
1206 
1207 	gkrellm_gtk_check_button_connected(vbox1, &estimate_model_button[1],
1208             estimate_model[1], FALSE, FALSE, 0,
1209 			cb_estimate_model, GINT_TO_POINTER(1),
1210 			_("Exponential charge model"));
1211 
1212 	if (!_GK.client_mode)
1213 		{
1214 		hbox2 = gtk_hbox_new(FALSE, 0);
1215 		gkrellm_gtk_spin_button(hbox2, NULL,
1216 				(gfloat) poll_interval, 1, 3600, 1, 10, 0, 55,
1217 				cb_poll_interval, NULL, FALSE,
1218 				_("Seconds between updates"));
1219 		gtk_box_pack_end(GTK_BOX(vbox), hbox2, FALSE, FALSE, 6);
1220 		}
1221 
1222 	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Setup"));
1223 
1224 	vbox1 = gkrellm_gtk_category_vbox(vbox,
1225 				_("Launch Commands"),
1226 				4, 0, TRUE);
1227 	table = gkrellm_gtk_launcher_table_new(vbox1, 1);
1228 	gkrellm_gtk_config_launcher(table, 0, &launch_entry, &tooltip_entry,
1229 					_("Battery"), &launch);
1230 	g_signal_connect(G_OBJECT(launch_entry), "changed",
1231 			G_CALLBACK(cb_launch_entry), NULL);
1232 	g_signal_connect(G_OBJECT(tooltip_entry), "changed",
1233 			G_CALLBACK(cb_launch_entry), NULL);
1234 
1235 	hbox = gtk_hbox_new(FALSE, 0);
1236 	gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 8);
1237 	gkrellm_gtk_alert_button(hbox, NULL, FALSE, FALSE, 4, TRUE,
1238 				cb_set_alert, NULL);
1239 	if (battery_list)
1240 		{
1241 		bat = (Battery *) battery_list->data;
1242 		if (bat && bat->time_left >= 0)		/* No choice if no battery times */
1243 			gkrellm_gtk_check_button_connected(hbox, NULL,
1244 					alert_units_percent, FALSE, FALSE, 16,
1245 					alert_units_percent_cb, NULL,
1246 					_("Alerts check for percent capacity remaining."));
1247 		}
1248 
1249 
1250 /* --Info tab */
1251 	vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Info"));
1252 	text = gkrellm_gtk_scrolled_text_view(vbox, NULL,
1253 				GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1254 	for (i = 0; i < sizeof(battery_info_text)/sizeof(gchar *); ++i)
1255 		gkrellm_gtk_text_view_append(text, _(battery_info_text[i]));
1256 	}
1257 
1258 static GkrellmMonitor	monitor_battery =
1259 	{
1260 	N_("Battery"),			/* Name, for config tab.	*/
1261 	MON_BATTERY,			/* Id, 0 if a plugin		*/
1262 	create_battery,			/* The create function		*/
1263 	update_battery,			/* The update function		*/
1264 	create_battery_tab,		/* The config tab create function	*/
1265 	NULL,				/* Apply the config function		*/
1266 
1267 	save_battery_config,	/* Save user conifg			*/
1268 	load_battery_config,	/* Load user config			*/
1269 	BAT_CONFIG_KEYWORD,	/* config keyword			*/
1270 
1271 	NULL,				/* Undef 2	*/
1272 	NULL,				/* Undef 1	*/
1273 	NULL,				/* Undef 0	*/
1274 
1275 	0,					/* insert_before_id - place plugin before this mon */
1276 
1277 	NULL,				/* Handle if a plugin, filled in by GKrellM		*/
1278 	NULL				/* path if a plugin, filled in by GKrellM		*/
1279 	};
1280 
1281 GkrellmMonitor *
gkrellm_init_battery_monitor(void)1282 gkrellm_init_battery_monitor(void)
1283 	{
1284 	estimate_runtime[0] =  1.5;	/* 1.5 hour battery */
1285 	estimate_runtime[1] =	3.0;	/* 3 hour recharge */
1286 	if (_GK.client_mode)
1287 		poll_interval = 1;
1288 
1289 	monitor_battery.name=_(monitor_battery.name);
1290 
1291 	style_id = gkrellm_add_meter_style(&monitor_battery, BATTERY_STYLE_NAME);
1292 	mon_battery = &monitor_battery;
1293 	if (setup_battery_interface())
1294 		{
1295 		(*read_battery_data)();
1296 		return &monitor_battery;
1297 		}
1298 	return NULL;
1299 	}
1300