1300 lines
35 KiB
C
1300 lines
35 KiB
C
/* GKrellM
|
|
| Copyright (C) 1999-2014 Bill Wilson
|
|
|
|
|
| Author: Bill Wilson billw@gkrellm.net
|
|
| Latest versions might be found at: http://gkrellm.net
|
|
|
|
|
|
|
|
| GKrellM 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 3 of the License, or
|
|
| (at your option) any later version.
|
|
|
|
|
| GKrellM 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, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
| Additional permission under GNU GPL version 3 section 7
|
|
|
|
|
| If you modify this program, or any covered work, by linking or
|
|
| combining it with the OpenSSL project's OpenSSL library (or a
|
|
| modified version of that library), containing parts covered by
|
|
| the terms of the OpenSSL or SSLeay licenses, you are granted
|
|
| additional permission to convey the resulting work.
|
|
| Corresponding Source for a non-source form of such a combination
|
|
| shall include the source code for the parts of OpenSSL used as well
|
|
| as that of the covered work.
|
|
*/
|
|
|
|
#include "gkrellm.h"
|
|
#include "gkrellm-private.h"
|
|
#include "gkrellm-sysdeps.h"
|
|
|
|
#include <math.h>
|
|
|
|
|
|
#define BAT_CONFIG_KEYWORD "battery"
|
|
|
|
typedef enum
|
|
{
|
|
BATTERYDISPLAY_PERCENT,
|
|
BATTERYDISPLAY_TIME,
|
|
BATTERYDISPLAY_RATE,
|
|
BATTERYDISPLAY_EOM /* end of modes */
|
|
}
|
|
BatteryDisplayMode;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
gint id;
|
|
GkrellmPanel *panel;
|
|
GkrellmKrell *krell;
|
|
GkrellmDecal *power_decal;
|
|
GkrellmDecal *time_decal;
|
|
|
|
GkrellmAlert *alert;
|
|
|
|
gboolean enabled;
|
|
|
|
BatteryDisplayMode display_mode;
|
|
gfloat charge_rate; /* % / min */
|
|
|
|
gboolean present,
|
|
on_line,
|
|
charging;
|
|
gint percent;
|
|
gint time_left; /* In minutes, -1 if minutes unavail */
|
|
}
|
|
Battery;
|
|
|
|
static GList *battery_list;
|
|
static GkrellmMonitor *mon_battery;
|
|
static GtkWidget *battery_vbox;
|
|
|
|
static Battery *composite_battery,
|
|
*launch_battery;
|
|
|
|
static gboolean enable_composite,
|
|
enable_each,
|
|
enable_estimate;
|
|
|
|
static gint poll_interval = 5,
|
|
full_cap_fallback = 5000;
|
|
|
|
static GkrellmLauncher launch;
|
|
|
|
static GkrellmAlert *bat_alert; /* One alert dupped for each battery */
|
|
|
|
static gint style_id;
|
|
|
|
static gboolean alert_units_percent,
|
|
alert_units_need_estimate_mode;
|
|
|
|
static void (*read_battery_data)();
|
|
static void create_battery_panel(Battery *bat, gboolean first_create);
|
|
|
|
static gint n_batteries;
|
|
|
|
|
|
static Battery *
|
|
battery_nth(gint n, gboolean create)
|
|
{
|
|
Battery *bat;
|
|
|
|
if (n > 10)
|
|
return NULL;
|
|
if (n < 0)
|
|
{
|
|
if (!composite_battery && create)
|
|
{
|
|
bat = g_new0(Battery, 1);
|
|
battery_list = g_list_prepend(battery_list, bat);
|
|
bat->id = GKRELLM_BATTERY_COMPOSITE_ID; /* -1 */
|
|
composite_battery = bat;
|
|
gkrellm_alert_dup(&bat->alert, bat_alert);
|
|
}
|
|
return composite_battery;
|
|
}
|
|
|
|
if (composite_battery)
|
|
++n;
|
|
|
|
while ( (bat = (Battery *) g_list_nth_data(battery_list, n)) == NULL
|
|
&& create
|
|
)
|
|
{
|
|
bat = g_new0(Battery, 1);
|
|
battery_list = g_list_append(battery_list, bat);
|
|
bat->id = n_batteries++;
|
|
gkrellm_alert_dup(&bat->alert, bat_alert);
|
|
}
|
|
return bat;
|
|
}
|
|
|
|
|
|
/* Themers need to be able to see the battery monitor.
|
|
*/
|
|
static void
|
|
read_battery_demo(void)
|
|
{
|
|
gboolean on_line, charging;
|
|
gint percent, time_left;
|
|
static gint bump = 60;
|
|
|
|
if (bump <= 5)
|
|
bump = 60;
|
|
bump -= 5;
|
|
on_line = bump > 45;
|
|
if (on_line)
|
|
{
|
|
charging = TRUE;
|
|
time_left = 200 + (60 - bump) * 20;
|
|
percent = time_left / 5;
|
|
}
|
|
else
|
|
{
|
|
charging = FALSE;
|
|
time_left = bump;
|
|
percent = 1 + bump;
|
|
}
|
|
gkrellm_battery_assign_data(0, TRUE, on_line, charging,
|
|
percent, time_left);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
setup_battery_interface(void)
|
|
{
|
|
if (!read_battery_data && !_GK.client_mode && gkrellm_sys_battery_init())
|
|
read_battery_data = gkrellm_sys_battery_read_data;
|
|
if (_GK.demo)
|
|
read_battery_data = read_battery_demo;
|
|
return read_battery_data ? TRUE : FALSE;
|
|
}
|
|
|
|
void
|
|
gkrellm_battery_client_divert(void (*read_func)())
|
|
{
|
|
read_battery_data = read_func;
|
|
}
|
|
|
|
void
|
|
gkrellm_battery_assign_data(gint n, gboolean present, gboolean on_line,
|
|
gboolean charging, gint percent, gint time_left)
|
|
{
|
|
Battery *bat;
|
|
|
|
bat = battery_nth(n, TRUE);
|
|
if (!bat)
|
|
return;
|
|
bat->present = present;
|
|
bat->on_line = on_line;
|
|
bat->charging = charging;
|
|
bat->percent = percent;
|
|
bat->time_left = time_left;
|
|
}
|
|
|
|
/* Help out some laptops with Linux ACPI bugs */
|
|
gint
|
|
gkrellm_battery_full_cap_fallback(void)
|
|
{
|
|
return full_cap_fallback;
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------- */
|
|
|
|
|
|
/* estimate (guess-timate?) battery time remaining, based on the rate of
|
|
discharge (and conversely the time to charge based on the rate of charge).
|
|
- some BIOS' only provide battery levels, not any estimate of the time
|
|
remaining
|
|
|
|
Battery charge/discharge characteristics (or, why dc/dt doesn't really work)
|
|
- the charge/discharge curves of most battery types tend to be very non-
|
|
linear (http://www.google.com/search?q=battery+charge+discharge+curve)
|
|
- on discharge, most battery types will initially fall somewhat rapidly
|
|
from 100 percent, then flatten out and stay somewhat linear until
|
|
suddenly "dropping out" when nearly depleted (approx. 10-20% capacity).
|
|
For practical purposes we can consider this point to be the end of the
|
|
discharge curve. This is simple enough to model via a fixed capacity
|
|
offset to cut out just at the knee of this curve, and allows us to
|
|
reasonably approximate the rest of the curve by a linear function
|
|
and simple dc/dt calculation.
|
|
- with regard to charging, however, it's not quite so easy. With a
|
|
constant voltage charger, the battery capacity rises exponentially
|
|
(charging current decreases as battery terminal voltage rises). The
|
|
final stages of charging are very gradual, with a relatively long
|
|
period at "almost but not quite 100%".
|
|
|
|
Unfortunately a linear extrapolation at the beginning of an
|
|
exponential curve will be a poor approximation to the true expected
|
|
time to charge, tending to be significantly undervalued. Using an
|
|
exponential model to estimate time to approx. 90-95% (2.5 * exp. time
|
|
constant) seems to give a more reasonable fit. That said, the poor
|
|
relative resolution at higher charge values makes estimating the
|
|
exponential time constant difficult towards the end of the charge
|
|
cycle (the curve's very nearly flat). So, I've settled on a mixed
|
|
model - for c < ~70 I use an exponential model, and switch to linear
|
|
above that (or if the charge rate seems to have otherwise "flatlined").
|
|
|
|
Empirically, this method seems to give reasonable results [1] -
|
|
certainly much better than seeing "0:50:00 to full" for a good half an
|
|
hour (i.e. as happens with apmd, which uses a linear model for both
|
|
charging + discharging). Note that a constant-current charger should
|
|
be pretty well linear all the way along the charge curve, which means
|
|
the linear rate extrapolation should work well in this case. The user
|
|
can choose which model they wish to use via estimate_model.
|
|
|
|
[1] I logged my Compaq Armada M300's capacity (via /proc/apm) over one
|
|
complete discharge/charge cycle (machine was idle the whole time). The
|
|
discharge curve was linear to approx. 14% when the BIOS alerted of
|
|
impending doom; upon plugging in the external power supply the capacity
|
|
rose exponentially to 100%, with a time constant of approx. 0.8 hr (i.e.
|
|
approx. 2+ hrs to full charge).
|
|
|
|
Linear rate of change calculation:
|
|
- in an ideal, continuous world, estimated time to 0(100) would simply
|
|
be the remaining capacity divided by the charge rate
|
|
ttl = c / dc(t)/dt
|
|
- alas, the reported battery capacity is bound to integer values thus
|
|
c(t) is a discontinuous function. i.e. has fairly large steps. And of
|
|
course then dc/dt is undefined at the discontinuities.
|
|
- to avoid this issue the rate of charge is determined by the deltas from
|
|
the start of the last state change (charge/discharge cycle) (time T)
|
|
ttl(t) = c(t) / ((C - c(t)) / (T - t)) C = charge at time T
|
|
Furthermore, the rate changes are windowed and filtered to mitigate
|
|
c(t) transients (e.g. at the start of discharge) and smooth over
|
|
discontinuities (and fudge for battery characteristics, ref. above).
|
|
*/
|
|
|
|
#define BAT_SLEEP_DETECT 300 /* interval of >300s => must have slept */
|
|
#define BAT_DISCHARGE_TRANSIENT 10 /* ignore first 10% of discharge cycle */
|
|
#define BAT_EMPTY_CAPACITY 12 /* when is the battery "flat"? */
|
|
#define BAT_RATECHANGE_WINDOW 90 /* allow rate changes after 90s */
|
|
#define BAT_CHARGE_MODEL_LIMIT 60 /* linear charge model cutover point */
|
|
#define BAT_RATE_SMOOTHING 0.3 /* rate smoothing weight */
|
|
|
|
/* #define BAT_ESTIMATE_DEBUG */
|
|
|
|
/* user-provided nominal battery runtimes, hrs (used to determine initial
|
|
| discharge, stable, charge rate (%/min))
|
|
*/
|
|
static gfloat estimate_runtime[2] = {0};
|
|
static gint estimate_model[2] = {0};
|
|
static gboolean reset_estimate;
|
|
|
|
static void
|
|
estimate_battery_time_left(Battery *bat)
|
|
{
|
|
/* ?0 = at time 0 (state change); ?1 = at last "sample" (rate change) */
|
|
static time_t t0 = -1, t1;
|
|
static gint p0, p1;
|
|
static gint c0;
|
|
static gfloat rate = 0;
|
|
static time_t dt;
|
|
static gint dp;
|
|
time_t t = time(NULL);
|
|
|
|
/* 1 charging; 0 power on and stable; -1 discharging */
|
|
gint charging = bat->charging ? 1 : (bat->on_line ? 0 : -1);
|
|
|
|
#ifdef BAT_ESTIMATE_DEBUG
|
|
fprintf(stderr, "%ld bc?=%d ac?=%d (%+d) bp=%d\t", t,
|
|
bat->charging, bat->on_line, charging,
|
|
bat->percent);
|
|
#endif
|
|
|
|
if ( reset_estimate || t0 < 0 || c0 != charging
|
|
|| (t - t1) > BAT_SLEEP_DETECT
|
|
)
|
|
{
|
|
/* init, state change, or sleep/hibernation
|
|
*/
|
|
reset_estimate = FALSE;
|
|
c0 = charging;
|
|
|
|
t0 = t1 = t;
|
|
if (charging < 0 && (bat->percent > 100 - BAT_DISCHARGE_TRANSIENT))
|
|
p0 = p1 = 100 - BAT_DISCHARGE_TRANSIENT;
|
|
else
|
|
p0 = p1 = bat->percent;
|
|
dp = dt = 0;
|
|
rate = 0.0;
|
|
|
|
/* convert runtime (hrs) to signed rate (%/min)
|
|
*/
|
|
if (charging < 0)
|
|
rate = -100 / (estimate_runtime[0] * 60);
|
|
else if (charging > 0)
|
|
rate = 100 / (estimate_runtime[1] * 60);
|
|
|
|
#ifdef BAT_ESTIMATE_DEBUG
|
|
fprintf(stderr, "[r = %.3f]\t", rate);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
time_t dt1 = t - t1; /* delta since last rate change */
|
|
gint dp1 = bat->percent - p1;
|
|
|
|
/* time for a rate change?
|
|
*/
|
|
if ( dt1 > BAT_RATECHANGE_WINDOW
|
|
&& ((charging > 0 && dp1 >= 0) || (charging < 0 && dp1 <= 0))
|
|
)
|
|
{
|
|
dt = t - t0; /* since state change */
|
|
dp = bat->percent - p0;
|
|
|
|
if (dp1 == 0) /* flatlining (dp/dt = 0) */
|
|
rate = (1 - BAT_RATE_SMOOTHING/4) * rate;
|
|
else
|
|
rate = BAT_RATE_SMOOTHING *
|
|
((gdouble) dp / (gdouble) (dt/60)) +
|
|
(1 - BAT_RATE_SMOOTHING) * rate;
|
|
|
|
#ifdef BAT_ESTIMATE_DEBUG
|
|
fprintf(stderr, "%d [dp = %+d dt = %.2f rate = %.3f]\t",
|
|
(gint) dp1, dp, (gdouble) dt / 60, rate);
|
|
#endif
|
|
|
|
t1 = t;
|
|
p1 = bat->percent;
|
|
}
|
|
}
|
|
|
|
if (charging && rate != 0.0) /* (dis)charging */
|
|
{
|
|
gfloat eta;
|
|
gint p = charging > 0 ? 100 - bat->percent :
|
|
bat->percent - BAT_EMPTY_CAPACITY;
|
|
|
|
if ( charging > 0 && estimate_model[1]
|
|
&& bat->percent < BAT_CHARGE_MODEL_LIMIT && dp > 0
|
|
)
|
|
/* charging, use exponential: eta =~ 2.5 * time-constant (~=92%) */
|
|
eta = -2.5 * dt/60 / (log(1 - (gdouble)dp/(gdouble)(p+dp)));
|
|
else
|
|
eta = abs((gdouble)p / rate); /* use linear */
|
|
|
|
#ifdef BAT_ESTIMATE_DEBUG
|
|
fprintf(stderr, "eta = %.2f\t", eta);
|
|
#endif
|
|
|
|
/* round off to nearest 5 mins */
|
|
bat->time_left = (gint)((eta > 0 ? eta + 2.5: 0) / 5) * 5;
|
|
bat->charge_rate = rate;
|
|
}
|
|
else
|
|
{
|
|
bat->time_left = INT_MAX; /* inf */
|
|
bat->charge_rate = 0.0;
|
|
}
|
|
|
|
#ifdef BAT_ESTIMATE_DEBUG
|
|
fprintf(stderr, "\n");
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
draw_time_left_decal(Battery *bat, gboolean force)
|
|
{
|
|
GkrellmDecal *d;
|
|
gchar buf[16];
|
|
gint x, w, t;
|
|
int battery_display_mode = bat->display_mode;
|
|
static BatteryDisplayMode last_mode = BATTERYDISPLAY_EOM;
|
|
|
|
if (!bat->panel)
|
|
return;
|
|
if (bat->time_left == -1)
|
|
battery_display_mode = BATTERYDISPLAY_PERCENT;
|
|
if (last_mode != battery_display_mode)
|
|
force = TRUE;
|
|
last_mode = bat->display_mode;
|
|
|
|
switch (battery_display_mode)
|
|
{
|
|
case BATTERYDISPLAY_TIME:
|
|
t = bat->time_left;
|
|
if (t == INT_MAX || t == INT_MIN)
|
|
snprintf(buf, sizeof(buf), "--");
|
|
else
|
|
snprintf(buf, sizeof(buf), "%2d:%02d", t / 60, t % 60);
|
|
break;
|
|
|
|
case BATTERYDISPLAY_RATE:
|
|
/* t is used by draw_decal_text() to see if a refresh is reqd */
|
|
t = (gint) (bat->charge_rate * 100.0);
|
|
snprintf(buf, sizeof(buf), "%0.1f%%/m",
|
|
bat->charge_rate);
|
|
break;
|
|
|
|
case BATTERYDISPLAY_PERCENT:
|
|
default:
|
|
t = bat->percent;
|
|
if (t == -1) /* APM battery flags should cause hide... but */
|
|
snprintf(buf, sizeof(buf), "no bat");
|
|
else
|
|
snprintf(buf, sizeof(buf), "%d%%", t);
|
|
break;
|
|
}
|
|
|
|
d = bat->time_decal;
|
|
w = gkrellm_gdk_string_width(d->text_style.font, buf);
|
|
x = (d->w - w) / 2;
|
|
if (x < 0)
|
|
x = 0;
|
|
d->x_off = x;
|
|
gkrellm_draw_decal_text(bat->panel, d, buf, force ? -1 : t);
|
|
}
|
|
|
|
static void
|
|
update_battery_panel(Battery *bat)
|
|
{
|
|
GkrellmPanel *p = bat->panel;
|
|
|
|
if (!p)
|
|
return;
|
|
if (!bat->present)
|
|
{ /* Battery can be removed while running */
|
|
gkrellm_panel_hide(p);
|
|
return;
|
|
}
|
|
gkrellm_panel_show(p);
|
|
|
|
if (bat->time_left > 0 && bat->charging)
|
|
bat->charge_rate = (gfloat) (100 - bat->percent) / (gfloat) bat->time_left;
|
|
else
|
|
bat->charge_rate = 0.0;
|
|
|
|
if (enable_estimate)
|
|
estimate_battery_time_left(bat);
|
|
|
|
if (bat->on_line)
|
|
{
|
|
gkrellm_reset_alert(bat->alert);
|
|
gkrellm_freeze_alert(bat->alert);
|
|
gkrellm_draw_decal_pixmap(p, bat->power_decal, D_MISC_AC);
|
|
}
|
|
else
|
|
{
|
|
if ( (bat == composite_battery && enable_composite)
|
|
|| (bat != composite_battery && enable_each)
|
|
)
|
|
{
|
|
gkrellm_thaw_alert(bat->alert);
|
|
gkrellm_check_alert(bat->alert, alert_units_percent
|
|
? bat->percent : bat->time_left);
|
|
}
|
|
gkrellm_draw_decal_pixmap(p, bat->power_decal, D_MISC_BATTERY);
|
|
}
|
|
draw_time_left_decal(bat, FALSE);
|
|
gkrellm_update_krell(p, bat->krell, bat->percent);
|
|
gkrellm_draw_panel_layers(p);
|
|
}
|
|
|
|
static void
|
|
update_battery(void)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
static gint seconds = 0;
|
|
|
|
if (!enable_each && !enable_composite)
|
|
return;
|
|
if (GK.second_tick)
|
|
{
|
|
if (seconds == 0)
|
|
{
|
|
for (list = battery_list; list; list = list->next)
|
|
((Battery *) list->data)->present = FALSE;
|
|
|
|
(*read_battery_data)();
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
if (!bat->panel)
|
|
create_battery_panel(bat, TRUE);
|
|
if (bat->enabled)
|
|
update_battery_panel(bat);
|
|
}
|
|
}
|
|
seconds = (seconds + 1) % poll_interval;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
cb_expose_event(GtkWidget *widget, GdkEventExpose *ev, GkrellmPanel *p)
|
|
{
|
|
gdk_draw_drawable(gtk_widget_get_window(widget),
|
|
gtk_widget_get_style(widget)->fg_gc[gtk_widget_get_state(widget)], p->pixmap,
|
|
ev->area.x, ev->area.y, ev->area.x, ev->area.y,
|
|
ev->area.width, ev->area.height);
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
cb_panel_enter(GtkWidget *w, GdkEventButton *ev, Battery *bat)
|
|
{
|
|
gkrellm_decal_on_top_layer(bat->time_decal, TRUE);
|
|
gkrellm_draw_panel_layers(bat->panel);
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
cb_panel_leave(GtkWidget *w, GdkEventButton *ev, Battery *bat)
|
|
{
|
|
gkrellm_decal_on_top_layer(bat->time_decal, FALSE);
|
|
gkrellm_draw_panel_layers(bat->panel);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
cb_panel_press(GtkWidget *widget, GdkEventButton *ev, Battery *bat)
|
|
{
|
|
GkrellmDecal *d;
|
|
static gboolean time_unavailable_warned;
|
|
|
|
d = launch.decal;
|
|
if (ev->button == 3)
|
|
gkrellm_open_config_window(mon_battery);
|
|
else if ( ev->button == 2
|
|
|| (ev->button == 1 && !d)
|
|
|| (ev->button == 1 && d && ev->x < d->x)
|
|
)
|
|
{
|
|
if (bat->time_left == -1 && bat->present)
|
|
{
|
|
if (!time_unavailable_warned)
|
|
gkrellm_message_dialog(_("GKrellM Battery"),
|
|
_("Battery times are unavailable. You\n"
|
|
"could select the Estimated Time option."));
|
|
time_unavailable_warned = TRUE;
|
|
bat->display_mode = BATTERYDISPLAY_PERCENT;
|
|
}
|
|
else
|
|
{
|
|
bat->display_mode++;
|
|
if (bat->display_mode == BATTERYDISPLAY_EOM)
|
|
bat->display_mode = 0;
|
|
|
|
draw_time_left_decal(bat, TRUE);
|
|
gkrellm_draw_panel_layers(bat->panel);
|
|
gkrellm_config_modified();
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
create_battery_panel(Battery *bat, gboolean first_create)
|
|
{
|
|
GkrellmPanel *p;
|
|
GkrellmStyle *style;
|
|
GkrellmMargin *m;
|
|
gint x, w;
|
|
|
|
if (!bat->panel)
|
|
bat->panel = gkrellm_panel_new0();
|
|
p = bat->panel;
|
|
style = gkrellm_meter_style(style_id);
|
|
m = gkrellm_get_style_margins(style);
|
|
bat->power_decal = gkrellm_create_decal_pixmap(p,
|
|
gkrellm_decal_misc_pixmap(), gkrellm_decal_misc_mask(),
|
|
N_MISC_DECALS, style, m->left, -1);
|
|
|
|
x = bat->power_decal->x + bat->power_decal->w + 2;
|
|
w = gkrellm_chart_width() - x - m->right;
|
|
bat->time_decal = gkrellm_create_decal_text(p, "8/%",
|
|
gkrellm_meter_textstyle(style_id),
|
|
style, x, -1, w);
|
|
|
|
bat->krell = gkrellm_create_krell(p,
|
|
gkrellm_krell_meter_piximage(style_id), style);
|
|
gkrellm_monotonic_krell_values(bat->krell, FALSE);
|
|
gkrellm_set_krell_full_scale(bat->krell, 100, 1);
|
|
|
|
gkrellm_panel_configure(p, NULL, style);
|
|
gkrellm_panel_create(battery_vbox, mon_battery, p);
|
|
|
|
/* Center the decals with respect to each other.
|
|
*/
|
|
if (bat->power_decal->h > bat->time_decal->h)
|
|
bat->time_decal->y += (bat->power_decal->h - bat->time_decal->h) / 2;
|
|
else
|
|
bat->power_decal->y += (bat->time_decal->h - bat->power_decal->h) / 2;
|
|
|
|
if (first_create)
|
|
{
|
|
g_signal_connect(G_OBJECT(p->drawing_area), "expose_event",
|
|
G_CALLBACK(cb_expose_event), p);
|
|
g_signal_connect(G_OBJECT(p->drawing_area), "button_press_event",
|
|
G_CALLBACK(cb_panel_press), bat);
|
|
g_signal_connect(G_OBJECT(p->drawing_area), "enter_notify_event",
|
|
G_CALLBACK(cb_panel_enter), bat);
|
|
g_signal_connect(G_OBJECT(p->drawing_area), "leave_notify_event",
|
|
G_CALLBACK(cb_panel_leave), bat);
|
|
}
|
|
|
|
gkrellm_setup_decal_launcher(p, &launch, bat->time_decal);
|
|
if ( (bat == composite_battery && enable_composite)
|
|
|| (bat->id == 0 && composite_battery && !enable_composite)
|
|
|| (bat->id == 0 && !composite_battery)
|
|
)
|
|
launch_battery = bat;
|
|
|
|
if (bat == composite_battery)
|
|
bat->enabled = enable_composite;
|
|
else
|
|
bat->enabled = enable_each;
|
|
|
|
if (bat->enabled)
|
|
update_battery_panel(bat);
|
|
else
|
|
gkrellm_panel_hide(p);
|
|
}
|
|
|
|
static void
|
|
spacer_visibility(void)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
gboolean enabled = FALSE;
|
|
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
enabled |= bat->enabled;
|
|
}
|
|
if (enabled)
|
|
gkrellm_spacers_show(mon_battery);
|
|
else
|
|
gkrellm_spacers_hide(mon_battery);
|
|
}
|
|
|
|
static void
|
|
create_battery(GtkWidget *vbox, gint first_create)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
|
|
battery_vbox = vbox;
|
|
if (_GK.demo)
|
|
enable_each = TRUE;
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
create_battery_panel(bat, first_create);
|
|
}
|
|
spacer_visibility();
|
|
}
|
|
|
|
|
|
/* Expand alert command substitution variables:
|
|
| $H - hostname $n - battery id
|
|
| $t - time left $p - percent
|
|
| $o - online (boolean) $c - charging (boolean)
|
|
*/
|
|
static void
|
|
cb_command_process(GkrellmAlert *alert, gchar *src, gchar *buf, gint size,
|
|
Battery *bat)
|
|
{
|
|
gchar c, *s;
|
|
gint len, value;
|
|
|
|
if (!buf || size < 1)
|
|
return;
|
|
--size;
|
|
*buf = '\0';
|
|
if (!src)
|
|
return;
|
|
for (s = src; *s != '\0' && size > 0; ++s)
|
|
{
|
|
len = 1;
|
|
if (*s == '$' && *(s + 1) != '\0')
|
|
{
|
|
value = -1;
|
|
if ((c = *(s + 1)) == 'H')
|
|
len = snprintf(buf, size, "%s", gkrellm_sys_get_host_name());
|
|
else if (c == 'n' && bat != composite_battery)
|
|
value = bat->id;
|
|
else if (c == 't')
|
|
value = bat->time_left;
|
|
else if (c == 'p')
|
|
value = bat->percent;
|
|
else if (c == 'o')
|
|
value = bat->on_line;
|
|
else if (c == 'c')
|
|
value = bat->charging;
|
|
else
|
|
len = 0;
|
|
|
|
if (value >= 0)
|
|
len = snprintf(buf, size, "%d", value);
|
|
++s;
|
|
}
|
|
else
|
|
*buf = *s;
|
|
size -= len;
|
|
buf += len;
|
|
}
|
|
*buf = '\0';
|
|
}
|
|
|
|
static void
|
|
cb_battery_alert_trigger(GkrellmAlert *alert, Battery *bat)
|
|
{
|
|
GkrellmAlertdecal *ad;
|
|
GkrellmDecal *d;
|
|
|
|
alert->panel = bat->panel;
|
|
ad = &alert->ad;
|
|
d = bat->time_decal;
|
|
ad->x = d->x + 1;
|
|
ad->y = d->y - 2;
|
|
ad->w = d->w - 1;
|
|
ad->h = d->h + 4;
|
|
gkrellm_render_default_alert_decal(alert);
|
|
}
|
|
|
|
static void
|
|
dup_battery_alert(void)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
gkrellm_alert_dup(&bat->alert, bat_alert);
|
|
gkrellm_alert_trigger_connect(bat->alert,
|
|
cb_battery_alert_trigger, bat);
|
|
gkrellm_alert_command_process_connect(bat->alert,
|
|
cb_command_process, bat);
|
|
}
|
|
}
|
|
|
|
|
|
/* If the OS reports battery times, alerts will always have minutes units.
|
|
| If the OS does not report battery times the initial alert create will
|
|
| have minutes units if the estimate time option is enabled and it will
|
|
| have battery percent level units if estimate time option is off. Alert
|
|
| creates from load config will have units in effect at last save config.
|
|
*/
|
|
static void
|
|
create_alert(void)
|
|
{
|
|
Battery *bat;
|
|
|
|
if (!battery_list)
|
|
return;
|
|
bat = (Battery *) battery_list->data;
|
|
|
|
if (!bat_alert)
|
|
{
|
|
alert_units_need_estimate_mode = FALSE;
|
|
|
|
if ( alert_units_percent
|
|
|| (bat->time_left == -1 && !enable_estimate)
|
|
)
|
|
{
|
|
if (bat->time_left == -1)
|
|
alert_units_percent = TRUE;
|
|
bat_alert = gkrellm_alert_create(NULL, _("Battery"),
|
|
_("Battery Percent Limits"),
|
|
FALSE, TRUE, TRUE, 99, 0, 1, 10, 0);
|
|
}
|
|
else
|
|
{
|
|
bat_alert = gkrellm_alert_create(NULL, _("Battery"),
|
|
_("Battery Minutes Remaining Limits"),
|
|
FALSE, TRUE, TRUE, 500, 0, 1, 10, 0);
|
|
if (bat->time_left == -1)
|
|
alert_units_need_estimate_mode = TRUE;
|
|
}
|
|
}
|
|
gkrellm_alert_config_connect(bat_alert, dup_battery_alert, NULL);
|
|
|
|
/* This alert is a master to be dupped and is itself never checked */
|
|
}
|
|
|
|
|
|
static void
|
|
save_battery_config(FILE *f)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
|
|
fprintf(f, "%s enable %d\n", BAT_CONFIG_KEYWORD, enable_each);
|
|
fprintf(f, "%s enable_composite %d\n", BAT_CONFIG_KEYWORD,
|
|
enable_composite);
|
|
fprintf(f, "%s estimate_time %d\n", BAT_CONFIG_KEYWORD, enable_estimate);
|
|
|
|
/* 2.1.15: scale saved float values to avoid decimal points in the config.
|
|
*/
|
|
fprintf(f, "%s estimate_time_discharge %.0f\n", BAT_CONFIG_KEYWORD,
|
|
estimate_runtime[0] * GKRELLM_FLOAT_FACTOR);
|
|
fprintf(f, "%s estimate_time_charge %.0f\n", BAT_CONFIG_KEYWORD,
|
|
estimate_runtime[1] * GKRELLM_FLOAT_FACTOR);
|
|
fprintf(f, "%s estimate_time_charge_model %d\n", BAT_CONFIG_KEYWORD,
|
|
estimate_model[1]);
|
|
fprintf(f, "%s full_cap_fallback %d\n", BAT_CONFIG_KEYWORD,
|
|
full_cap_fallback);
|
|
if (!_GK.client_mode)
|
|
fprintf(f, "%s poll_interval %d\n", BAT_CONFIG_KEYWORD, poll_interval);
|
|
if (launch.command)
|
|
fprintf(f, "%s launch1 %s\n", BAT_CONFIG_KEYWORD, launch.command);
|
|
if (launch.tooltip_comment)
|
|
fprintf(f, "%s tooltip_comment %s\n",
|
|
BAT_CONFIG_KEYWORD, launch.tooltip_comment);
|
|
fprintf(f, "%s alert_units_percent %d\n", BAT_CONFIG_KEYWORD,
|
|
alert_units_percent);
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
if (bat == composite_battery) /* Don't 2.1.16 backwards break */
|
|
fprintf(f, "%s display_mode_composite %d %d\n", BAT_CONFIG_KEYWORD,
|
|
bat->display_mode, bat->id);
|
|
else
|
|
fprintf(f, "%s display_mode %d %d\n", BAT_CONFIG_KEYWORD,
|
|
bat->display_mode, bat->id);
|
|
}
|
|
gkrellm_save_alertconfig(f, bat_alert, BAT_CONFIG_KEYWORD, NULL);
|
|
}
|
|
|
|
static void
|
|
load_battery_config(gchar *arg)
|
|
{
|
|
Battery *bat;
|
|
gint display_mode, n = 0;
|
|
gchar config[32], item[CFG_BUFSIZE],
|
|
name[CFG_BUFSIZE], item1[CFG_BUFSIZE];
|
|
|
|
if (sscanf(arg, "%31s %[^\n]", config, item) == 2)
|
|
{
|
|
if (!strcmp(config, "enable"))
|
|
sscanf(item, "%d", &enable_each);
|
|
if (!strcmp(config, "enable_composite"))
|
|
sscanf(item, "%d", &enable_composite);
|
|
else if (!strcmp(config, "estimate_time"))
|
|
sscanf(item, "%d", &enable_estimate);
|
|
else if (!strcmp(config, "estimate_time_discharge"))
|
|
{
|
|
sscanf(item, "%f", &estimate_runtime[0]);
|
|
estimate_runtime[0] /= _GK.float_factor;
|
|
}
|
|
else if (!strcmp(config, "estimate_time_charge"))
|
|
{
|
|
sscanf(item, "%f", &estimate_runtime[1]);
|
|
estimate_runtime[1] /= _GK.float_factor;
|
|
}
|
|
else if (!strcmp(config, "estimate_time_charge_model"))
|
|
sscanf(item, "%d", &estimate_model[1]);
|
|
else if (!strcmp(config, "full_cap_fallback"))
|
|
sscanf(item, "%d", &full_cap_fallback);
|
|
else if (!strcmp(config, "poll_interval"))
|
|
sscanf(item, "%d", &poll_interval);
|
|
else if (!strcmp(config, "launch1"))
|
|
launch.command = g_strdup(item);
|
|
else if (!strcmp(config, "tooltip_comment"))
|
|
launch.tooltip_comment = g_strdup(item);
|
|
else if (!strncmp(config, "display_mode", 12))
|
|
{
|
|
sscanf(item, "%d %d", &display_mode, &n);
|
|
if ((bat = battery_nth(n, FALSE)) != NULL)
|
|
bat->display_mode = display_mode;
|
|
}
|
|
else if (!strcmp(config, "alert_units_percent"))
|
|
sscanf(item, "%d", &alert_units_percent);
|
|
else if (!strcmp(config, GKRELLM_ALERTCONFIG_KEYWORD))
|
|
{
|
|
if (!strncmp(item, "BAT", 3)) /* Config compat musical chairs */
|
|
sscanf(item, "%32s %[^\n]", name, item1);
|
|
else
|
|
strcpy(item1, item);
|
|
create_alert();
|
|
gkrellm_load_alertconfig(&bat_alert, item1);
|
|
dup_battery_alert();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static GtkWidget *launch_entry,
|
|
*tooltip_entry;
|
|
|
|
static GtkWidget *estimate_runtime_spin_button[2],
|
|
*estimate_model_button[2];
|
|
|
|
static void
|
|
update_battery_panels(void)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
if (bat->enabled)
|
|
update_battery_panel(bat);
|
|
}
|
|
}
|
|
|
|
static void
|
|
cb_set_alert(GtkWidget *button, Battery *bat)
|
|
{
|
|
create_alert();
|
|
gkrellm_alert_config_window(&bat_alert);
|
|
}
|
|
|
|
static void
|
|
alert_units_percent_cb(GtkToggleButton *button, gpointer data)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
|
|
alert_units_percent = button->active;
|
|
|
|
if (bat_alert)
|
|
{
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
gkrellm_reset_alert(bat->alert);
|
|
gkrellm_alert_destroy(&bat->alert);
|
|
}
|
|
gkrellm_alert_destroy(&bat_alert);
|
|
gkrellm_config_message_dialog(_("GKrellM Battery"),
|
|
_("The Battery alert units are changed\n"
|
|
"and the alert must be reconfigured."));
|
|
}
|
|
}
|
|
|
|
static void
|
|
cb_enable_estimate(GtkToggleButton *button, GtkWidget *box)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
gboolean enable;
|
|
|
|
enable = button->active;
|
|
gtk_widget_set_sensitive(box, enable);
|
|
|
|
if (enable_estimate != enable)
|
|
{
|
|
/* If alert units need estimated time mode and estimation switches off,
|
|
| destroy the alert because the alert units can now only be percent.
|
|
*/
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
if (bat->alert && (!enable && alert_units_need_estimate_mode))
|
|
gkrellm_alert_destroy(&bat->alert);
|
|
}
|
|
if ( bat_alert
|
|
&& (!enable && alert_units_need_estimate_mode)
|
|
&& !alert_units_percent
|
|
)
|
|
{
|
|
gkrellm_alert_destroy(&bat_alert);
|
|
gkrellm_config_message_dialog(_("GKrellM Battery"),
|
|
_("The Battery alert units are changed\n"
|
|
"and the alert must be reconfigured."));
|
|
}
|
|
}
|
|
enable_estimate = enable;
|
|
update_battery_panels();
|
|
}
|
|
|
|
static void
|
|
cb_runtime(GtkWidget *entry, gpointer data)
|
|
{
|
|
gint i = GPOINTER_TO_INT(data) - 1;
|
|
|
|
estimate_runtime[i] = gtk_spin_button_get_value(
|
|
GTK_SPIN_BUTTON(estimate_runtime_spin_button[i]));
|
|
reset_estimate = TRUE;
|
|
update_battery_panels();
|
|
}
|
|
|
|
|
|
static void
|
|
cb_enable(GtkToggleButton *button, gpointer data)
|
|
{
|
|
GList *list;
|
|
Battery *bat;
|
|
gint which = GPOINTER_TO_INT(data);
|
|
|
|
if (which == 0)
|
|
enable_composite = enable_each = button->active;
|
|
else if (which == 1)
|
|
enable_each = button->active;
|
|
else if (which == 2)
|
|
enable_composite = button->active;
|
|
|
|
for (list = battery_list; list; list = list->next)
|
|
{
|
|
bat = (Battery *) list->data;
|
|
if (bat == composite_battery)
|
|
bat->enabled = enable_composite;
|
|
else
|
|
bat->enabled = enable_each;
|
|
|
|
if (bat->enabled)
|
|
{
|
|
gkrellm_panel_show(bat->panel);
|
|
update_battery_panel(bat);
|
|
}
|
|
else
|
|
{
|
|
gkrellm_reset_alert(bat->alert);
|
|
gkrellm_panel_hide(bat->panel);
|
|
}
|
|
}
|
|
if (composite_battery)
|
|
{
|
|
gkrellm_remove_launcher(&launch);
|
|
|
|
if (composite_battery->enabled)
|
|
{
|
|
gkrellm_setup_decal_launcher(composite_battery->panel,
|
|
&launch, composite_battery->time_decal);
|
|
launch_battery = composite_battery;
|
|
}
|
|
else
|
|
{
|
|
bat = battery_nth(0, FALSE);
|
|
if (bat && bat->enabled)
|
|
{
|
|
gkrellm_setup_decal_launcher(bat->panel,
|
|
&launch, bat->time_decal);
|
|
launch_battery = bat;
|
|
}
|
|
}
|
|
|
|
}
|
|
spacer_visibility();
|
|
}
|
|
|
|
static void
|
|
cb_estimate_model(GtkWidget *entry, gpointer data)
|
|
{
|
|
gint i = GPOINTER_TO_INT(data);
|
|
|
|
estimate_model[i] =
|
|
GTK_TOGGLE_BUTTON(estimate_model_button[i])->active;
|
|
reset_estimate = TRUE;
|
|
update_battery_panels();
|
|
}
|
|
|
|
static void
|
|
cb_poll_interval(GtkWidget *entry, GtkSpinButton *spin)
|
|
{
|
|
poll_interval = gtk_spin_button_get_value_as_int(spin);
|
|
}
|
|
|
|
static void
|
|
cb_launch_entry(GtkWidget *widget, gpointer data)
|
|
{
|
|
if (!launch_battery)
|
|
return;
|
|
gkrellm_apply_launcher(&launch_entry, &tooltip_entry,
|
|
launch_battery->panel, &launch, gkrellm_launch_button_cb);
|
|
}
|
|
|
|
|
|
static gchar *battery_info_text[] =
|
|
{
|
|
N_("<h>Setup\n"),
|
|
N_("<b>Display Estimated Time\n"),
|
|
N_("If battery times are not reported by the BIOS or if the reported times\n"
|
|
"are inaccurate, select this option to display a battery time remaining or\n"
|
|
"time to charge which is calculated based on the current battery percentage\n"
|
|
"level, user supplied total battery times, and a linear extrapolation model.\n"),
|
|
"\n",
|
|
N_("<b>Total Battery Times\n"),
|
|
N_("Enter the typical total run time and total charge time in hours for your\n"
|
|
"battery.\n"),
|
|
"\n",
|
|
N_("<b>Exponential Charge Model\n"), /* xgettext:no-c-format */
|
|
N_("For some charging systems battery capacity rises exponentially, which\n"
|
|
"means the simple linear model will grossly underestimate the time to 100%.\n"
|
|
"Select the exponential model for more accuracy in this case.\n"),
|
|
"\n",
|
|
"<b>",
|
|
N_("Alerts"),
|
|
"\n",
|
|
N_("Substitution variables may be used in alert commands.\n"),
|
|
N_("\t$p battery percent level.\n"),
|
|
N_("\t$t battery time left.\n"),
|
|
N_("\t$n battery number.\n"),
|
|
N_("\t$o online state (boolean).\n"),
|
|
N_("\t$c charging state (boolean).\n"),
|
|
"\n",
|
|
N_("<h>Mouse Button Actions:\n"),
|
|
N_("<b>\tLeft "),
|
|
N_(" click on the charging state decal to toggle the display mode\n"
|
|
"\t\tbetween a minutes, percentage, or charging rate display.\n"),
|
|
N_("<b>\tMiddle "),
|
|
N_(" clicking anywhere on the Battery panel also toggles the display mode.\n")
|
|
};
|
|
|
|
static void
|
|
create_battery_tab(GtkWidget *tab_vbox)
|
|
{
|
|
GtkWidget *tabs, *table, *vbox, *vbox1, *vbox2,
|
|
*hbox, *hbox2, *text;
|
|
Battery *bat;
|
|
gint i;
|
|
|
|
tabs = gtk_notebook_new();
|
|
gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
|
|
gtk_box_pack_start(GTK_BOX(tab_vbox), tabs, TRUE, TRUE, 0);
|
|
|
|
/* -- Setup tab */
|
|
vbox = gkrellm_gtk_notebook_page(tabs, _("Options"));
|
|
vbox = gkrellm_gtk_framed_vbox(vbox, NULL, 2, TRUE, 10, 6);
|
|
|
|
if (composite_battery && n_batteries > 0)
|
|
{
|
|
vbox1 = gkrellm_gtk_category_vbox(vbox, _("Enable"), 2, 2, TRUE);
|
|
|
|
gkrellm_gtk_check_button_connected(vbox1, NULL,
|
|
enable_composite, FALSE, FALSE, 0,
|
|
cb_enable, GINT_TO_POINTER(2),
|
|
_("Composite Battery"));
|
|
gkrellm_gtk_check_button_connected(vbox1, NULL,
|
|
enable_each, FALSE, FALSE, 0,
|
|
cb_enable, GINT_TO_POINTER(1),
|
|
_("Real Batteries"));
|
|
}
|
|
else
|
|
gkrellm_gtk_check_button_connected(vbox, NULL,
|
|
enable_each, FALSE, FALSE, 10,
|
|
cb_enable, GINT_TO_POINTER(0),
|
|
_("Enable Battery"));
|
|
|
|
vbox2 = gtk_vbox_new(FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
|
|
|
|
vbox1 = gtk_vbox_new(FALSE, 0);
|
|
gkrellm_gtk_check_button_connected(vbox2, NULL,
|
|
enable_estimate, FALSE, FALSE, 2,
|
|
cb_enable_estimate, vbox1,
|
|
_("Display estimated time remaining and time to charge"));
|
|
gtk_widget_set_sensitive(vbox1, enable_estimate ? TRUE : FALSE);
|
|
gtk_box_pack_start(GTK_BOX(vbox2), vbox1, FALSE, FALSE, 0);
|
|
|
|
vbox1 = gkrellm_gtk_category_vbox(vbox1, NULL, 0, 0, TRUE);
|
|
gkrellm_gtk_spin_button(vbox1, &estimate_runtime_spin_button[0],
|
|
estimate_runtime[0], 0.1, 24, 0.1, 1.0, 1, 55,
|
|
cb_runtime, GINT_TO_POINTER(1), FALSE,
|
|
_("Total battery run time in hours"));
|
|
gkrellm_gtk_spin_button(vbox1, &estimate_runtime_spin_button[1],
|
|
estimate_runtime[1], 0.1, 24, 0.1, 1.0, 1, 55,
|
|
cb_runtime, GINT_TO_POINTER(2), FALSE,
|
|
_("Total battery charge time in hours"));
|
|
|
|
gkrellm_gtk_check_button_connected(vbox1, &estimate_model_button[1],
|
|
estimate_model[1], FALSE, FALSE, 0,
|
|
cb_estimate_model, GINT_TO_POINTER(1),
|
|
_("Exponential charge model"));
|
|
|
|
if (!_GK.client_mode)
|
|
{
|
|
hbox2 = gtk_hbox_new(FALSE, 0);
|
|
gkrellm_gtk_spin_button(hbox2, NULL,
|
|
(gfloat) poll_interval, 1, 3600, 1, 10, 0, 55,
|
|
cb_poll_interval, NULL, FALSE,
|
|
_("Seconds between updates"));
|
|
gtk_box_pack_end(GTK_BOX(vbox), hbox2, FALSE, FALSE, 6);
|
|
}
|
|
|
|
vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Setup"));
|
|
|
|
vbox1 = gkrellm_gtk_category_vbox(vbox,
|
|
_("Launch Commands"),
|
|
4, 0, TRUE);
|
|
table = gkrellm_gtk_launcher_table_new(vbox1, 1);
|
|
gkrellm_gtk_config_launcher(table, 0, &launch_entry, &tooltip_entry,
|
|
_("Battery"), &launch);
|
|
g_signal_connect(G_OBJECT(launch_entry), "changed",
|
|
G_CALLBACK(cb_launch_entry), NULL);
|
|
g_signal_connect(G_OBJECT(tooltip_entry), "changed",
|
|
G_CALLBACK(cb_launch_entry), NULL);
|
|
|
|
hbox = gtk_hbox_new(FALSE, 0);
|
|
gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 8);
|
|
gkrellm_gtk_alert_button(hbox, NULL, FALSE, FALSE, 4, TRUE,
|
|
cb_set_alert, NULL);
|
|
if (battery_list)
|
|
{
|
|
bat = (Battery *) battery_list->data;
|
|
if (bat && bat->time_left >= 0) /* No choice if no battery times */
|
|
gkrellm_gtk_check_button_connected(hbox, NULL,
|
|
alert_units_percent, FALSE, FALSE, 16,
|
|
alert_units_percent_cb, NULL,
|
|
_("Alerts check for percent capacity remaining."));
|
|
}
|
|
|
|
|
|
/* --Info tab */
|
|
vbox = gkrellm_gtk_framed_notebook_page(tabs, _("Info"));
|
|
text = gkrellm_gtk_scrolled_text_view(vbox, NULL,
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
for (i = 0; i < sizeof(battery_info_text)/sizeof(gchar *); ++i)
|
|
gkrellm_gtk_text_view_append(text, _(battery_info_text[i]));
|
|
}
|
|
|
|
static GkrellmMonitor monitor_battery =
|
|
{
|
|
N_("Battery"), /* Name, for config tab. */
|
|
MON_BATTERY, /* Id, 0 if a plugin */
|
|
create_battery, /* The create function */
|
|
update_battery, /* The update function */
|
|
create_battery_tab, /* The config tab create function */
|
|
NULL, /* Apply the config function */
|
|
|
|
save_battery_config, /* Save user conifg */
|
|
load_battery_config, /* Load user config */
|
|
BAT_CONFIG_KEYWORD, /* config keyword */
|
|
|
|
NULL, /* Undef 2 */
|
|
NULL, /* Undef 1 */
|
|
NULL, /* Undef 0 */
|
|
|
|
0, /* insert_before_id - place plugin before this mon */
|
|
|
|
NULL, /* Handle if a plugin, filled in by GKrellM */
|
|
NULL /* path if a plugin, filled in by GKrellM */
|
|
};
|
|
|
|
GkrellmMonitor *
|
|
gkrellm_init_battery_monitor(void)
|
|
{
|
|
estimate_runtime[0] = 1.5; /* 1.5 hour battery */
|
|
estimate_runtime[1] = 3.0; /* 3 hour recharge */
|
|
if (_GK.client_mode)
|
|
poll_interval = 1;
|
|
|
|
monitor_battery.name=_(monitor_battery.name);
|
|
|
|
style_id = gkrellm_add_meter_style(&monitor_battery, BATTERY_STYLE_NAME);
|
|
mon_battery = &monitor_battery;
|
|
if (setup_battery_interface())
|
|
{
|
|
(*read_battery_data)();
|
|
return &monitor_battery;
|
|
}
|
|
return NULL;
|
|
}
|