diff --git a/replicants/Battery/build.sh b/replicants/Battery/build.sh new file mode 100755 index 0000000..5ef5646 --- /dev/null +++ b/replicants/Battery/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +../../scripts/buildreplicant.sh Battery "$@" diff --git a/replicants/Battery/src/icons/charging.xbm b/replicants/Battery/src/icons/charging.xbm new file mode 100644 index 0000000..6a4dabb --- /dev/null +++ b/replicants/Battery/src/icons/charging.xbm @@ -0,0 +1,15 @@ +#define charging_width 48 +#define charging_height 24 +static unsigned char charging_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x33, + 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x37, 0x0c, 0xe6, 0xff, 0x3f, 0xfe, 0x37, + 0x46, 0xec, 0xff, 0x07, 0xff, 0x67, 0xe6, 0xec, 0xff, 0x80, 0xff, 0x6f, + 0xe6, 0xec, 0x1f, 0xc0, 0xff, 0x6f, 0xe6, 0xec, 0x07, 0xe3, 0xff, 0x6f, + 0xe6, 0xec, 0xff, 0x63, 0xf0, 0x6f, 0xe6, 0xec, 0xff, 0x01, 0xfc, 0x6f, + 0xe6, 0xec, 0xff, 0x80, 0xff, 0x6f, 0x46, 0xec, 0x7f, 0xf0, 0xff, 0x67, + 0x0c, 0xe6, 0x3f, 0xfe, 0xff, 0x37, 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x37, + 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x33, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/error.xbm b/replicants/Battery/src/icons/error.xbm new file mode 100644 index 0000000..2e91fd4 --- /dev/null +++ b/replicants/Battery/src/icons/error.xbm @@ -0,0 +1,15 @@ +#define error_width 48 +#define error_height 24 +static unsigned char error_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x30, + 0x0c, 0x06, 0x40, 0x80, 0x00, 0x30, 0x0c, 0x06, 0xe0, 0xc0, 0x01, 0x30, + 0x46, 0x0c, 0xc0, 0xe1, 0x00, 0x60, 0xe6, 0x0c, 0x80, 0x73, 0x00, 0x60, + 0xe6, 0x0c, 0x00, 0x3f, 0x00, 0x60, 0xe6, 0x0c, 0x00, 0x1e, 0x00, 0x60, + 0xe6, 0x0c, 0x00, 0x1e, 0x00, 0x60, 0xe6, 0x0c, 0x00, 0x3f, 0x00, 0x60, + 0xe6, 0x0c, 0x80, 0x73, 0x00, 0x60, 0x46, 0x0c, 0xc0, 0xe1, 0x00, 0x60, + 0x0c, 0x06, 0xe0, 0xc0, 0x01, 0x30, 0x0c, 0x06, 0x40, 0x80, 0x00, 0x30, + 0x0c, 0x06, 0x00, 0x00, 0x00, 0x30, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/level0.xbm b/replicants/Battery/src/icons/level0.xbm new file mode 100644 index 0000000..7afadbb --- /dev/null +++ b/replicants/Battery/src/icons/level0.xbm @@ -0,0 +1,15 @@ +#define level0_width 48 +#define level0_height 24 +static unsigned char level0_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x32, + 0x0c, 0x06, 0x00, 0x02, 0x00, 0x37, 0x0c, 0x06, 0x00, 0x07, 0x00, 0x37, + 0x46, 0x0c, 0x00, 0x07, 0x00, 0x67, 0xe6, 0x0c, 0x00, 0x07, 0x80, 0x6f, + 0xe6, 0x0c, 0x00, 0x07, 0x80, 0x6f, 0xe6, 0x0c, 0x00, 0x07, 0x80, 0x6f, + 0xe6, 0x0c, 0x00, 0x02, 0x80, 0x6f, 0xe6, 0x0c, 0x00, 0x02, 0x80, 0x6f, + 0xe6, 0x0c, 0x00, 0x00, 0x80, 0x6f, 0x46, 0x0c, 0x00, 0x02, 0x00, 0x67, + 0x0c, 0x06, 0x00, 0x07, 0x00, 0x37, 0x0c, 0x06, 0x00, 0x02, 0x00, 0x37, + 0x0c, 0x06, 0x00, 0x00, 0x00, 0x32, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/level1.xbm b/replicants/Battery/src/icons/level1.xbm new file mode 100644 index 0000000..914ff86 --- /dev/null +++ b/replicants/Battery/src/icons/level1.xbm @@ -0,0 +1,15 @@ +#define level1_width 48 +#define level1_height 24 +static unsigned char level1_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0x00, 0xfe, 0x33, + 0x0c, 0x06, 0x00, 0x00, 0xff, 0x37, 0x0c, 0x06, 0x00, 0x00, 0xff, 0x37, + 0x46, 0x0c, 0x00, 0x00, 0xff, 0x67, 0xe6, 0x0c, 0x00, 0x80, 0xff, 0x6f, + 0xe6, 0x0c, 0x00, 0x80, 0xff, 0x6f, 0xe6, 0x0c, 0x00, 0x80, 0xff, 0x6f, + 0xe6, 0x0c, 0x00, 0x80, 0xff, 0x6f, 0xe6, 0x0c, 0x00, 0x80, 0xff, 0x6f, + 0xe6, 0x0c, 0x00, 0x80, 0xff, 0x6f, 0x46, 0x0c, 0x00, 0x00, 0xff, 0x67, + 0x0c, 0x06, 0x00, 0x00, 0xff, 0x37, 0x0c, 0x06, 0x00, 0x00, 0xff, 0x37, + 0x0c, 0x06, 0x00, 0x00, 0xfe, 0x33, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/level2.xbm b/replicants/Battery/src/icons/level2.xbm new file mode 100644 index 0000000..741d49f --- /dev/null +++ b/replicants/Battery/src/icons/level2.xbm @@ -0,0 +1,15 @@ +#define level2_width 48 +#define level2_height 24 +static unsigned char level2_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0x00, 0xf8, 0xff, 0x33, + 0x0c, 0x06, 0x00, 0xfc, 0xff, 0x37, 0x0c, 0x06, 0x00, 0xfc, 0xff, 0x37, + 0x46, 0x0c, 0x00, 0xfc, 0xff, 0x67, 0xe6, 0x0c, 0x00, 0xfe, 0xff, 0x6f, + 0xe6, 0x0c, 0x00, 0xfe, 0xff, 0x6f, 0xe6, 0x0c, 0x00, 0xfe, 0xff, 0x6f, + 0xe6, 0x0c, 0x00, 0xfe, 0xff, 0x6f, 0xe6, 0x0c, 0x00, 0xfe, 0xff, 0x6f, + 0xe6, 0x0c, 0x00, 0xfe, 0xff, 0x6f, 0x46, 0x0c, 0x00, 0xfc, 0xff, 0x67, + 0x0c, 0x06, 0x00, 0xfc, 0xff, 0x37, 0x0c, 0x06, 0x00, 0xfc, 0xff, 0x37, + 0x0c, 0x06, 0x00, 0xf8, 0xff, 0x33, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/level3.xbm b/replicants/Battery/src/icons/level3.xbm new file mode 100644 index 0000000..c5eac37 --- /dev/null +++ b/replicants/Battery/src/icons/level3.xbm @@ -0,0 +1,15 @@ +#define level3_width 48 +#define level3_height 24 +static unsigned char level3_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0x06, 0xfe, 0xff, 0xff, 0x33, + 0x0c, 0x06, 0xff, 0xff, 0xff, 0x37, 0x0c, 0x06, 0xff, 0xff, 0xff, 0x37, + 0x46, 0x0c, 0xff, 0xff, 0xff, 0x67, 0xe6, 0x8c, 0xff, 0xff, 0xff, 0x6f, + 0xe6, 0x8c, 0xff, 0xff, 0xff, 0x6f, 0xe6, 0x8c, 0xff, 0xff, 0xff, 0x6f, + 0xe6, 0x8c, 0xff, 0xff, 0xff, 0x6f, 0xe6, 0x8c, 0xff, 0xff, 0xff, 0x6f, + 0xe6, 0x8c, 0xff, 0xff, 0xff, 0x6f, 0x46, 0x0c, 0xff, 0xff, 0xff, 0x67, + 0x0c, 0x06, 0xff, 0xff, 0xff, 0x37, 0x0c, 0x06, 0xff, 0xff, 0xff, 0x37, + 0x0c, 0x06, 0xfe, 0xff, 0xff, 0x33, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/level4.xbm b/replicants/Battery/src/icons/level4.xbm new file mode 100644 index 0000000..0944b86 --- /dev/null +++ b/replicants/Battery/src/icons/level4.xbm @@ -0,0 +1,15 @@ +#define level4_width 48 +#define level4_height 24 +static unsigned char level4_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x33, + 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x37, 0x0c, 0xe6, 0xff, 0xff, 0xff, 0x37, + 0x46, 0xec, 0xff, 0xff, 0xff, 0x67, 0xe6, 0xec, 0xff, 0xff, 0xff, 0x6f, + 0xe6, 0xec, 0xff, 0xff, 0xff, 0x6f, 0xe6, 0xec, 0xff, 0xff, 0xff, 0x6f, + 0xe6, 0xec, 0xff, 0xff, 0xff, 0x6f, 0xe6, 0xec, 0xff, 0xff, 0xff, 0x6f, + 0xe6, 0xec, 0xff, 0xff, 0xff, 0x6f, 0x46, 0xec, 0xff, 0xff, 0xff, 0x67, + 0x0c, 0xe6, 0xff, 0xff, 0xff, 0x37, 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x37, + 0x0c, 0xf6, 0xff, 0xff, 0xff, 0x33, 0x18, 0x03, 0x00, 0x00, 0x00, 0x18, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x0f, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/icons/not.xbm b/replicants/Battery/src/icons/not.xbm new file mode 100644 index 0000000..fab9cd6 --- /dev/null +++ b/replicants/Battery/src/icons/not.xbm @@ -0,0 +1,15 @@ +#define not_width 48 +#define not_height 24 +static unsigned char not_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0x02, 0x50, 0x55, 0x55, 0x55, 0x55, 0x05, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x08, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10, + 0x02, 0x08, 0x00, 0x00, 0x00, 0x20, 0x44, 0x04, 0x00, 0x00, 0x00, 0x40, + 0xa2, 0x08, 0x00, 0x00, 0x00, 0x20, 0x44, 0x04, 0x00, 0x00, 0x00, 0x40, + 0xa2, 0x08, 0x00, 0x00, 0x00, 0x20, 0x44, 0x04, 0x00, 0x00, 0x00, 0x40, + 0xa2, 0x08, 0x00, 0x00, 0x00, 0x20, 0x44, 0x04, 0x00, 0x00, 0x00, 0x40, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x20, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x20, 0x10, 0x01, 0x00, 0x00, 0x00, 0x10, + 0xa0, 0xaa, 0xaa, 0xaa, 0xaa, 0x0a, 0x40, 0x55, 0x55, 0x55, 0x55, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Battery/src/main.c b/replicants/Battery/src/main.c new file mode 100644 index 0000000..50bdc9e --- /dev/null +++ b/replicants/Battery/src/main.c @@ -0,0 +1,292 @@ +#define _XOPEN_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "icons/level0.xbm" +#include "icons/level1.xbm" +#include "icons/level2.xbm" +#include "icons/level3.xbm" +#include "icons/level4.xbm" +#include "icons/charging.xbm" +#include "icons/error.xbm" +#include "icons/not.xbm" + +typedef enum { + BatteryStateCharging, + BatteryStateNotCharging, + BatteryStateFull, + BatteryStateDischarging, + BatteryStateError +} BatteryState; + +typedef struct { + int level; + BatteryState state; + int hours; + int minutes; + int seconds; +} BatteryInfo; + +typedef struct { + unsigned long pollInterval; + XtAppContext application; + Widget root; + Widget layout; + Widget icon; + Widget text; + XtIntervalId timer; + Pixmap levels[8]; +} BatteryReplicant; + +static BatteryInfo getBatteryInfo (void); +static Pixmap selectBatteryIcon (BatteryReplicant *, BatteryInfo); +static void loadAllPixmaps (BatteryReplicant *); +static void batteryPoll (XtPointer, XtIntervalId *); +static void resetBatteryPollTimeout (BatteryReplicant *); +static void handleBatteryInfoDialog (Widget, XtPointer, XtPointer); +static void handleDestroy (Widget, XtPointer, XtPointer); + +int Battery_XmdReplicantVersion (void) { + return XmdREPLICANT_VERSION; +} + +Widget Battery_XmdReplicantCreate ( + XtAppContext application, + Widget parent, + XmdReplicantState *state +) { + (void)(state); + + BatteryReplicant *this = XtNew(BatteryReplicant); + memset(this, 0, sizeof(*this)); + this->application = application; + + String pollIntervalString = XmdReplicantStateQuery(state, "Interval"); + if (pollIntervalString != NULL) { + this->pollInterval = 1000 * atoi(pollIntervalString); + } + if (this->pollInterval == 0) this->pollInterval = 2000; + XtFree(pollIntervalString); + + this->root = XtVaCreateWidget ( + "battery", xmFrameWidgetClass, parent, + XmNshadowType, XmSHADOW_ETCHED_IN, + NULL); + XtAddCallback(this->root, XmNdestroyCallback, handleDestroy, NULL); + this->layout = XtVaCreateWidget ( + "batteryLayout", xmRowColumnWidgetClass, this->root, + XmNorientation, XmHORIZONTAL, + NULL); + this->icon = XtVaCreateManagedWidget ( + "batteryIcon", xmPushButtonWidgetClass, this->layout, + XmNleftAttachment, XmATTACH_FORM, + XmNlabelType, XmPIXMAP, + NULL); + loadAllPixmaps(this); + XtAddCallback ( + this->icon, + XmNactivateCallback, handleBatteryInfoDialog, + NULL); + + XtVaCreateManagedWidget ( + "batterySeparator", xmSeparatorWidgetClass, this->layout, + XmNorientation, XmVERTICAL, + NULL); + this->text = XtVaCreateManagedWidget ( + "batteryText", xmLabelGadgetClass, this->layout, + XmNalignment, XmALIGNMENT_CENTER, + NULL); + + batteryPoll(this, NULL); + + XtManageChild(this->layout); + XtManageChild(this->root); + return this->layout; +} + +BatteryInfo getBatteryInfo (void) { + BatteryInfo result = { 0 }; + char charging[16] = { 0 }; + int battery; + + pid_t child; + FILE *stream = XmdVaPipedExecPath ( + "acpi", &child, "r", + "acpi", "-b", + NULL); + if (stream == NULL) { + result.state = BatteryStateError; + return result; + } + fscanf ( + stream, "Battery %d: %16s %d%%, %d:%d:%d", + &battery, charging, &result.level, + &result.hours, &result.minutes, &result.seconds); + + switch (charging[0]) { + case 'C': result.state = BatteryStateCharging; break; + case 'N': result.state = BatteryStateNotCharging; break; + case 'D': result.state = BatteryStateDischarging; break; + case 'F': result.state = BatteryStateFull; break; + default: result.state = BatteryStateError; break; + } + if (fclose(stream) != 0) { + result.state = BatteryStateError; + } + waitpid(child, NULL, 0); + return result; +} + +Pixmap selectBatteryIcon (BatteryReplicant *this, BatteryInfo info) { + if (info.state == BatteryStateCharging) { + return this->levels[5]; + } else if (info.state == BatteryStateNotCharging) { + return this->levels[7]; + } else if (info.state == BatteryStateError) { + return this->levels[6]; + } else if (info.level < 15) { + return this->levels[0]; + } else if (info.level < 35) { + return this->levels[1]; + } else if (info.level < 65) { + return this->levels[2]; + } else if (info.level < 85) { + return this->levels[3]; + } else { + return this->levels[4]; + } +} + +void loadAllPixmaps (BatteryReplicant *this) { + this->levels[0] = XmdLoadBitmapIcon(this->icon, level0); + this->levels[1] = XmdLoadBitmapIcon(this->icon, level1); + this->levels[2] = XmdLoadBitmapIcon(this->icon, level2); + this->levels[3] = XmdLoadBitmapIcon(this->icon, level3); + this->levels[4] = XmdLoadBitmapIcon(this->icon, level4); + this->levels[5] = XmdLoadBitmapIcon(this->icon, charging); + this->levels[6] = XmdLoadBitmapIcon(this->icon, error); + this->levels[7] = XmdLoadBitmapIcon(this->icon, not); +} + +void batteryPoll (XtPointer clientData, XtIntervalId *timer) { + (void)(clientData); + (void)(timer); + + BatteryReplicant *this = clientData; + BatteryInfo info = getBatteryInfo(); + + XtVaSetValues ( + this->icon, + XmNlabelType, XmPIXMAP, + XmNlabelPixmap, selectBatteryIcon(this, info), + NULL); + + char buffer[32]; + snprintf(buffer, XtNumber(buffer), "%d%%", info.level); + + /* if we don't unmanage and then re-manage the child, the text flies to + the top which is rather annoying. this little quirk bent me over and + fucked me for hours. */ + XtUnmanageChild(this->text); + XmString string = XmStringCreateLocalized(buffer); + XtVaSetValues ( + this->text, + XmNlabelString, string, + NULL); + XmStringFree(string); + XtManageChild(this->text); + + resetBatteryPollTimeout(this); +} + +void resetBatteryPollTimeout (BatteryReplicant *this) { + this->timer = XtAppAddTimeOut ( + this->application, this->pollInterval, batteryPoll, this); +} + +void handleBatteryInfoDialog (Widget button, XtPointer clientData, XtPointer callData) { + (void)(callData); + (void)(clientData); + + BatteryInfo info = getBatteryInfo(); + + static const String states[] = { + "Charging", + "Not charging", + "Full", + "Discharging", + "Error" + }; + + String messageBuffer = NULL; + switch (info.state) { + case BatteryStateCharging: + XtAsprintf ( + &messageBuffer, + "%d%%, %s\n" + "%d:%02d:%02d until charged.", + info.level, states[info.state], + info.hours, info.minutes, info.seconds); + break; + case BatteryStateDischarging: + XtAsprintf ( + &messageBuffer, + "%d%%, %s\n" + "%d:%02d:%02d until empty.", + info.level, states[info.state], + info.hours, info.minutes, info.seconds); + break; + + default: + XtAsprintf ( + &messageBuffer, + "%d%%, %s", + info.level, states[info.state]); + break; + } + + Arg args[5] = { 0 }; + int n = 0; + XmString message = XmStringCreateLocalized(messageBuffer); + XtSetArg(args[n], XmNmessageString, message); n ++; + XmString title = XmStringCreateLocalized("Battery Status"); + XtSetArg(args[n], XmNdialogTitle, title); n ++; + + Widget dialog = XmCreateInformationDialog(button, "batteryInfo", args, n); + + XmStringFree(message); + XmStringFree(title); + + XtManageChild(dialog); + XtPopup(XtParent(dialog), XtGrabNone); +} + +static void handleDestroy ( + Widget widget, + XtPointer clientData, + XtPointer callData +) { + (void)(callData); + (void)(widget); + + BatteryReplicant *this = clientData; + XtRemoveTimeOut(this->timer); + for (Cardinal index = 0; index < XtNumber(this->levels); index ++) { + XFreePixmap(XtDisplay(this->layout), this->levels[index]); + } + XtFree((char *)(this)); +}