Enhancement: Fixes #265, Battery Power On Taskband

This commit is contained in:
Rory Fewell
2024-11-09 20:41:44 +00:00
parent 47f81d297b
commit 156d592b92
11 changed files with 501 additions and 10 deletions

View File

@@ -28,6 +28,7 @@ wintc_resolve_library(garcon-gtk3-1 GARCON_GTK3)
wintc_resolve_library(gdk-pixbuf-2.0 GDK_PIXBUF)
wintc_resolve_library(glib-2.0 GLIB)
wintc_resolve_library(gtk+-3.0 GTK3)
wintc_resolve_library(upower-glib UPOWER_GLIB)
wintc_resolve_library(wintc-comgtk WINTC_COMGTK)
wintc_resolve_library(wintc-exec WINTC_EXEC)
wintc_resolve_library(wintc-shcommon WINTC_SHCOMMON)
@@ -64,6 +65,8 @@ add_executable(
src/systray/clock.h
src/systray/notifarea.c
src/systray/notifarea.h
src/systray/power.c
src/systray/power.h
src/systray/toolbar.c
src/systray/toolbar.h
src/systray/volume.c
@@ -92,6 +95,7 @@ target_include_directories(
PRIVATE ${GDK_PIXBUF_INCLUDE_DIRS}
PRIVATE ${GLIB_INCLUDE_DIRS}
PRIVATE ${GTK3_INCLUDE_DIRS}
PRIVATE ${UPOWER_GLIB_INCLUDE_DIRS}
PRIVATE ${WINTC_COMGTK_INCLUDE_DIRS}
PRIVATE ${WINTC_EXEC_INCLUDE_DIRS}
PRIVATE ${WINTC_SHCOMMON_INCLUDE_DIRS}
@@ -109,6 +113,7 @@ target_link_directories(
PRIVATE ${GDK_PIXBUF_LIBRARY_DIRS}
PRIVATE ${GLIB_LIBRARY_DIRS}
PRIVATE ${GTK3_LIBRARY_DIRS}
PRIVATE ${UPOWER_GLIB_LIBRARY_DIRS}
PRIVATE ${WINTC_COMGTK_LIBRARY_DIRS}
PRIVATE ${WINTC_EXEC_LIBRARY_DIRS}
PRIVATE ${WINTC_SHCOMMON_LIBRARY_DIRS}
@@ -126,6 +131,7 @@ target_link_libraries(
PRIVATE ${GDK_PIXBUF_LIBRARIES}
PRIVATE ${GLIB_LIBRARIES}
PRIVATE ${GTK3_LIBRARIES}
PRIVATE ${UPOWER_GLIB_LIBRARIES}
PRIVATE ${WINTC_COMGTK_LIBRARIES}
PRIVATE ${WINTC_EXEC_LIBRARIES}
PRIVATE ${WINTC_SHCOMMON_LIBRARIES}

View File

@@ -5,6 +5,7 @@ bt,rt:garcon-gtk3
bt,rt:gdk-pixbuf2
bt,rt:glib2
bt,rt:gtk3
bt,rt:upower-glib
bt,rt:wintc-comgtk
bt,rt:wintc-exec
bt,rt:wintc-shcommon

View File

@@ -5,6 +5,7 @@
#include "behaviour.h"
#include "clock.h"
#include "notifarea.h"
#include "power.h"
#include "volume.h"
//
@@ -34,6 +35,10 @@ static void wintc_notification_area_dispose(
GObject* object
);
static void wintc_notification_area_append_component(
WinTCNotificationArea* notif_area,
GType component_type
);
static GtkWidget* wintc_notification_area_append_icon(
WinTCNotificationArea* notif_area
);
@@ -111,18 +116,15 @@ static void wintc_notification_area_init(
self->clock_runner =
wintc_clock_runner_new(GTK_LABEL(self->label_clock));
// Create volume icon and behaviour for system tray
// Create notification area icons
//
WinTCNotificationVolume* notif_volume;
GtkWidget* widget_volume;
widget_volume = wintc_notification_area_append_icon(self);
notif_volume = wintc_notification_volume_new(widget_volume);
wintc_notification_area_map_widget(
wintc_notification_area_append_component(
self,
widget_volume,
WINTC_NOTIFICATION_BEHAVIOUR(notif_volume)
WINTC_TYPE_NOTIFICATION_POWER
);
wintc_notification_area_append_component(
self,
WINTC_TYPE_NOTIFICATION_VOLUME
);
}
@@ -163,6 +165,29 @@ GtkWidget* notification_area_new(void)
//
// PRIVATE FUNCTIONS
//
static void wintc_notification_area_append_component(
WinTCNotificationArea* notif_area,
GType component_type
)
{
GtkWidget* widget = wintc_notification_area_append_icon(notif_area);
WinTCNotificationBehaviour* notif =
WINTC_NOTIFICATION_BEHAVIOUR(
g_object_new(
component_type,
"widget-notif", widget,
NULL
)
);
wintc_notification_area_map_widget(
notif_area,
widget,
notif
);
}
static GtkWidget* wintc_notification_area_append_icon(
WinTCNotificationArea* notif_area
)

View File

@@ -0,0 +1,420 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <libupower-glib/upower.h>
#include <wintc/comgtk.h>
#include "behaviour.h"
#include "power.h"
//
// GTK OOP CLASS/INSTANCE DEFINITIONS
//
struct _WinTCNotificationPowerClass
{
WinTCNotificationBehaviourClass __parent__;
};
struct _WinTCNotificationPower
{
WinTCNotificationBehaviour __parent__;
// Power stuff
//
UpClient* up_client;
UpDevice* up_last_battery;
gchar* up_last_battery_path;
};
//
// FORWARD DECLARATIONS
//
static void wintc_notification_power_constructed(
GObject* object
);
static void wintc_notification_power_finalize(
GObject* object
);
static UpDeviceLevel battery_pct_to_enum(
gdouble percentage
);
static void check_and_register_battery(
WinTCNotificationPower* power,
UpDevice* device
);
static void update_battery_battery_level(
WinTCNotificationPower* power,
UpDevice* device
);
static void update_client_on_battery(
WinTCNotificationPower* power,
UpClient* client
);
static void on_up_client_device_added(
UpClient* up_client,
UpDevice* up_device,
gpointer user_data
);
static void on_up_client_device_removed(
UpClient* up_client,
const gchar* object_path,
gpointer user_data
);
static void on_up_client_battery_notify(
UpClient* up_client,
GParamSpec* pspec,
gpointer user_data
);
static void on_up_device_battery_notify(
UpDevice* up_device,
GParamSpec* pspec,
gpointer user_data
);
//
// GTK TYPE DEFINITIONS & CTORS
//
G_DEFINE_TYPE(
WinTCNotificationPower,
wintc_notification_power,
WINTC_TYPE_NOTIFICATION_BEHAVIOUR
)
static void wintc_notification_power_class_init(
WinTCNotificationPowerClass* klass
)
{
GObjectClass* object_class = G_OBJECT_CLASS(klass);
object_class->constructed = wintc_notification_power_constructed;
object_class->finalize = wintc_notification_power_finalize;
}
static void wintc_notification_power_init(
WINTC_UNUSED(WinTCNotificationPower* self)
) {}
//
// CLASS VIRTUAL METHODS
//
static void wintc_notification_power_constructed(
GObject* object
)
{
WinTCNotificationPower* power = WINTC_NOTIFICATION_POWER(object);
// Connect to upower, enumerate existing devices and attach signals for
// picking up new ones
//
GPtrArray* all_devices;
UpDevice* device;
power->up_client = up_client_new();
WINTC_LOG_DEBUG("Connected to upower");
all_devices = up_client_get_devices2(power->up_client);
for (guint i = 0; i < all_devices->len; i++)
{
device = all_devices->pdata[i];
check_and_register_battery(power, device);
}
g_signal_connect(
power->up_client,
"device-added",
G_CALLBACK(on_up_client_device_added),
power
);
g_signal_connect(
power->up_client,
"device-removed",
G_CALLBACK(on_up_client_device_removed),
power
);
g_signal_connect(
power->up_client,
"notify::on-battery",
G_CALLBACK(on_up_client_battery_notify),
power
);
update_client_on_battery(power, power->up_client);
(G_OBJECT_CLASS(
wintc_notification_power_parent_class
))->constructed(object);
}
static void wintc_notification_power_finalize(
GObject* object
)
{
WinTCNotificationPower* power = WINTC_NOTIFICATION_POWER(object);
g_free(power->up_last_battery_path);
(G_OBJECT_CLASS(
wintc_notification_power_parent_class
))->finalize(object);
}
//
// PUBLIC FUNCTIONS
//
WinTCNotificationPower* wintc_notification_power_new(
GtkWidget* widget_notif
)
{
return WINTC_NOTIFICATION_POWER(
g_object_new(
WINTC_TYPE_NOTIFICATION_POWER,
"widget-notif", widget_notif,
NULL
)
);
}
//
// PRIVATE FUNCTIONS
//
static UpDeviceLevel battery_pct_to_enum(
gdouble percentage
)
{
// FIXME: Not checked against Windows XP
//
if (percentage <= 15.0f)
{
return UP_DEVICE_LEVEL_CRITICAL;
}
else if (percentage <= 25.0f)
{
return UP_DEVICE_LEVEL_LOW;
}
else if (percentage <= 75.0f)
{
return UP_DEVICE_LEVEL_NORMAL;
}
else if (percentage <= 90.0f)
{
return UP_DEVICE_LEVEL_HIGH;
}
else
{
return UP_DEVICE_LEVEL_FULL;
}
}
static void check_and_register_battery(
WinTCNotificationPower* power,
UpDevice* device
)
{
guint device_kind;
g_object_get(
device,
"kind", &device_kind,
NULL
);
if (device_kind != UP_DEVICE_KIND_BATTERY)
{
return;
}
update_battery_battery_level(power, device);
g_signal_connect(
device,
"notify::percentage",
G_CALLBACK(on_up_device_battery_notify),
power
);
g_signal_connect(
device,
"notify::power-supply",
G_CALLBACK(on_up_device_battery_notify),
power
);
g_signal_connect(
device,
"notify::state",
G_CALLBACK(on_up_device_battery_notify),
power
);
}
static void update_battery_battery_level(
WinTCNotificationPower* power,
UpDevice* device
)
{
gdouble percentage;
gboolean power_supply;
guint state;
g_object_get(
device,
"percentage", &percentage,
"power-supply", &power_supply,
"state", &state,
NULL
);
if (!power_supply)
{
return;
}
// Update icon based on levels
//
// We do not use 'battery-level' from the upower API, because it's not
// reliable (reports UP_DEVICE_LEVEL_NONE) -- so just use percentage
//
const gchar* icon_name;
UpDeviceLevel battery_level = battery_pct_to_enum(percentage);
gboolean is_charging = state == UP_DEVICE_STATE_CHARGING;
switch (battery_level)
{
case UP_DEVICE_LEVEL_CRITICAL:
icon_name =
is_charging ?
"battery-caution-charging" :
"battery-caution";
break;
case UP_DEVICE_LEVEL_LOW:
icon_name =
is_charging ?
"battery-low-charging" :
"battery-low";
break;
case UP_DEVICE_LEVEL_NORMAL:
icon_name =
is_charging ?
"battery-good-charging" :
"battery-good";
break;
case UP_DEVICE_LEVEL_HIGH:
case UP_DEVICE_LEVEL_FULL:
icon_name =
is_charging ?
"battery-full-charging" :
"battery-full";
break;
default:
icon_name = "battery-missing";
break;
}
g_object_set(
power,
"icon-name", icon_name,
NULL
);
// Keep track of last battery, for when switching to-from AC power
//
const gchar* this_battery_path = up_device_get_object_path(device);
if (g_strcmp0(power->up_last_battery_path, this_battery_path) != 0)
{
g_free(power->up_last_battery_path);
power->up_last_battery = device;
power->up_last_battery_path = g_strdup(this_battery_path);
}
}
static void update_client_on_battery(
WinTCNotificationPower* power,
UpClient* client
)
{
if (up_client_get_on_battery(client))
{
if (power->up_last_battery)
{
update_battery_battery_level(power, power->up_last_battery);
}
else
{
g_object_set(
power,
"icon-name", "battery-missing",
NULL
);
}
}
else
{
if (!power->up_last_battery)
{
g_object_set(
power,
"icon-name", "ac-adapter",
NULL
);
}
}
}
//
// CALLBACKS
//
static void on_up_client_device_added(
WINTC_UNUSED(UpClient* up_client),
UpDevice* up_device,
gpointer user_data
)
{
WinTCNotificationPower* power = WINTC_NOTIFICATION_POWER(user_data);
check_and_register_battery(power, up_device);
}
static void on_up_client_device_removed(
WINTC_UNUSED(UpClient* up_client),
const gchar* object_path,
gpointer user_data
)
{
WinTCNotificationPower* power = WINTC_NOTIFICATION_POWER(user_data);
if (g_strcmp0(power->up_last_battery_path, object_path))
{
power->up_last_battery = NULL;
g_free(power->up_last_battery_path);
}
}
static void on_up_client_battery_notify(
UpClient* up_client,
WINTC_UNUSED(GParamSpec* pspec),
gpointer user_data
)
{
WinTCNotificationPower* power = WINTC_NOTIFICATION_POWER(user_data);
update_client_on_battery(power, up_client);
}
static void on_up_device_battery_notify(
UpDevice* up_device,
WINTC_UNUSED(GParamSpec* pspec),
gpointer user_data
)
{
WinTCNotificationPower* power = WINTC_NOTIFICATION_POWER(user_data);
update_battery_battery_level(power, up_device);
}

View File

@@ -0,0 +1,29 @@
#ifndef __POWER_H__
#define __POWER_H__
#include <glib.h>
#include <gtk/gtk.h>
//
// GTK OOP BOILERPLATE
//
typedef struct _WinTCNotificationPowerClass WinTCNotificationPowerClass;
typedef struct _WinTCNotificationPower WinTCNotificationPower;
#define WINTC_TYPE_NOTIFICATION_POWER (wintc_notification_power_get_type())
#define WINTC_NOTIFICATION_POWER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WINTC_TYPE_NOTIFICATION_POWER, WinTCNotificationPower))
#define WINTC_NOTIFICATION_POWER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WINTC_TYPE_NOTIFICATION_BEHAVIOUR, WinTCNotificationPowerClass))
#define IS_WINTC_NOTIFICATION_POWER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WINTC_TYPE_NOTIFICATION_POWER))
#define IS_WINTC_NOTIFICATION_POWER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WINTC_TYPE_NOTIFICATION_POWER))
#define WINTC_NOTIFICATION_POWER_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS((obj), WINTC_TYPE_NOTIFICATION_POWER, WinTCNotificationPower))
GType wintc_notification_power_get_type(void) G_GNUC_CONST;
//
// PUBLIC FUNCTIONS
//
WinTCNotificationPower* wintc_notification_power_new(
GtkWidget* widget_notif
);
#endif

View File

@@ -23,6 +23,8 @@ sass-->bt,rt-->sassc
sqlite3-->bt-->sqlite-dev
sqlite3-->rt-->sqlite
sysinfo-->bt,rt-->NULL
upower-glib-->bt-->upower-dev
upower-glib-->rt-->upower
webkitgtk-->bt-->webkit2gtk-4.1-dev
webkitgtk-->rt-->webkit2gtk-4.1
wintc-comctl-->bt,rt-->libwintc-comctl

View File

@@ -15,6 +15,7 @@ python3-venv-->bt,rt-->python3
sass-->bt,rt-->ruby-sass
sqlite3-->bt,rt-->sqlite
sysinfo-->bt,rt-->NULL
upower-glib-->bt,rt-->upower
webkitgtk-->bt,rt-->webkit2gtk-4.1
wintc-comctl-->bt,rt-->wintc-comctl
wintc-comgtk-->bt,rt-->wintc-comgtk

View File

@@ -14,6 +14,7 @@ python3-venv-->bt,rt-->py39-virtualenv
sass-->bt,rt-->rubygem-sass
sqlite3-->bt,rt-->sqlite3
sysinfo-->bt,rt-->libsysinfo
upower-glib-->bt,rt-->upower
webkitgtk-->bt,rt-->webkit2-gtk3
wintc-comctl-->bt,rt-->wintc-comctl
wintc-comgtk-->bt,rt-->wintc-comgtk

View File

@@ -24,6 +24,8 @@ sass-->bt,rt-->ruby-sass
sqlite3-->bt-->libsqlite3-dev
sqlite3-->rt-->libsqlite3-0
sysinfo-->bt,rt-->NULL
upower-glib-->bt-->libupower-glib-dev
upower-glib-->rt-->libupower-glib3
webkitgtk-->bt-->libwebkit2gtk-4.1-dev
webkitgtk-->rt-->libwebkit2gtk-4.1-0
wintc-comctl-->bt,rt-->libwintc-comctl

View File

@@ -23,6 +23,8 @@ sass-->bt,rt-->rubygem-sass
sqlite3-->bt-->sqlite-devel
sqlite3-->rt-->sqlite
sysinfo-->bt,rt-->NULL
upower-glib-->bt-->upower-devel
upower-glib-->rt-->upower-libs
webkitgtk-->bt-->webkit2gtk4.1-devel
webkitgtk-->rt-->webkit2gtk4.1
wintc-comctl-->bt,rt-->wintc-comctl

View File

@@ -23,6 +23,8 @@ sass-->bt,rt-->sassc
sqlite3-->bt-->sqlite-devel
sqlite3-->rt-->sqlite
sysinfo-->bt,rt-->NULL
upower-glib-->bt-->upower-devel
upower-glib-->rt-->upower
webkitgtk-->bt-->libwebkit2gtk41-devel
webkitgtk-->rt-->libwebkit2gtk41
wintc-comctl-->bt,rt-->wintc-comctl