aboutsummaryrefslogtreecommitdiff
path: root/wmstatus.c
diff options
context:
space:
mode:
Diffstat (limited to 'wmstatus.c')
-rw-r--r--wmstatus.c198
1 files changed, 132 insertions, 66 deletions
diff --git a/wmstatus.c b/wmstatus.c
index a02a0d1..0885d97 100644
--- a/wmstatus.c
+++ b/wmstatus.c
@@ -1,7 +1,7 @@
/*
- * wmstatus.c: simple PulseAudio-enabled status setter for dwm and i3
+ * wmstatus.c: simple PulseAudio-enabled status setter for dwm and i3/sway
*
- * Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
+ * Copyright (c) 2015 - 2024, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
@@ -20,13 +20,16 @@
#define LIBERTY_WANT_ASYNC
#define LIBERTY_WANT_PROTO_MPD
-#define _GNU_SOURCE // openat
+// openat, dirfd
+#define _XOPEN_SOURCE 700
+#define _ATFILE_SOURCE
+#define _GNU_SOURCE
#include "config.h"
#undef PROGRAM_NAME
#define PROGRAM_NAME "wmstatus"
#include "liberty/liberty.c"
-#include "poller-pa.c"
+#include "liberty/liberty-pulse.c"
#include <dirent.h>
#include <spawn.h>
@@ -849,7 +852,7 @@ struct app_context
struct mpd_client mpd_client; ///< MPD client
char *mpd_song; ///< MPD current song
- char *mpd_status; ///< MPD status (overrides song)
+ bool mpd_stopped; ///< MPD stopped (overrides song)
// NUT:
@@ -989,7 +992,6 @@ app_context_free (struct app_context *self)
mpd_client_free (&self->mpd_client);
cstr_set (&self->mpd_song, NULL);
- cstr_set (&self->mpd_status, NULL);
nut_client_free (&self->nut_client);
str_map_free (&self->nut_ups_info);
@@ -1022,13 +1024,14 @@ read_value (int dir, const char *filename, struct error **e)
return NULL;
}
+ errno = 0;
struct str s = str_make ();
- bool success = read_line (fp, &s);
+ bool success = read_line (fp, &s) && !ferror (fp);
fclose (fp);
if (!success)
{
- error_set (e, "%s: %s", filename, "read failed");
+ error_set (e, "%s: %s", filename, errno ? strerror (errno) : "EOF");
return NULL;
}
return str_steal (&s);
@@ -1043,42 +1046,61 @@ read_number (int dir, const char *filename, struct error **e)
unsigned long number = 0;
if (!xstrtoul (&number, value, 10))
- error_set (e, "%s: %s", filename, "doesn't contain an unsigned number");
+ error_set (e, "%s: %s", filename, "doesn't contain a valid number");
free (value);
return number;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+static int
+read_battery_charge (int dir)
+{
+ struct error *error = NULL;
+ double capacity, now, full;
+ if ((capacity = read_number (dir, "capacity", &error), !error))
+ return capacity;
+
+ error_free (error);
+ if ((now = read_number (dir, "charge_now", &error), !error)
+ && (full = read_number (dir, "charge_full", &error), !error))
+ return now / full * 100 + 0.5;
+
+ error_free (error);
+ return -1;
+}
+
static char *
-read_battery_status (int dir, struct error **e)
+read_battery_status (int dir, char **type)
{
- char *result = NULL;
+ // We present errors to the user, don't fill up the session's log.
struct error *error = NULL;
+ struct str s = str_make ();
- char *status;
- double charge_now;
- double charge_full;
+ // Dell is being unreasonable and seems to set charge_now
+ // to charge_full_design when the battery is fully charged
+ int charge = read_battery_charge (dir);
+ if (charge >= 0 && charge <= 100)
+ str_append_printf (&s, "%u%%", charge);
- if ((status = read_value (dir, "status", &error), error)
- || (charge_now = read_number (dir, "charge_now", &error), error)
- || (charge_full = read_number (dir, "charge_full", &error), error))
- error_propagate (e, error);
+ char *status = NULL;
+ char *model_name = read_value (dir, "model_name", NULL);
+ if (model_name)
+ {
+ model_name[strcspn (model_name, " ")] = 0;
+ cstr_set (type, model_name);
+ }
+ else if ((status = read_value (dir, "status", &error), !error))
+ {
+ str_append_printf (&s, " (%s)", status);
+ free (status);
+ }
else
{
- struct str s = str_make ();
- str_append (&s, status);
-
- // Dell is being unreasonable and seems to set charge_now
- // to charge_full_design when the battery is fully charged
- unsigned percentage = charge_now / charge_full * 100 + 0.5;
- if (percentage < 100)
- str_append_printf (&s, " (%u%%)", percentage);
- result = str_steal (&s);
+ str_append_printf (&s, " (%s)", strerror (errno));
+ error_free (error);
}
-
- free (status);
- return result;
+ return str_steal (&s);
}
static char *
@@ -1092,16 +1114,24 @@ try_power_supply (int dir, struct error **e)
return NULL;
}
+ bool offline = !read_number (dir, "online", &error);
+ if (error)
+ {
+ error_free (error);
+ error = NULL;
+ }
+ else if (offline)
+ return NULL;
+
bool is_relevant =
!strcmp (type, "Battery") ||
+ !strcmp (type, "USB") ||
!strcmp (type, "UPS");
char *result = NULL;
if (is_relevant)
{
- char *status = read_battery_status (dir, &error);
- if (error)
- error_propagate (e, error);
+ char *status = read_battery_status (dir, &type);
if (status)
result = xstrdup_printf ("%s %s", type, status);
free (status);
@@ -1122,8 +1152,8 @@ make_battery_status (void)
}
struct dirent *entry;
- char *status = NULL;
- while (!status && (entry = readdir (power_supply)))
+ struct strv batteries = strv_make ();
+ while ((entry = readdir (power_supply)))
{
const char *device_name = entry->d_name;
if (device_name[0] == '.')
@@ -1137,8 +1167,11 @@ make_battery_status (void)
}
struct error *error = NULL;
- status = try_power_supply (dir, &error);
+ char *status = try_power_supply (dir, &error);
close (dir);
+
+ if (status)
+ strv_append_owned (&batteries, status);
if (error)
{
print_error ("%s: %s", device_name, error->message);
@@ -1146,7 +1179,10 @@ make_battery_status (void)
}
}
closedir (power_supply);
- return status;
+
+ char *result = batteries.len ? strv_join (&batteries, " ") : NULL;
+ strv_free (&batteries);
+ return result;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1203,7 +1239,7 @@ refresh_status (struct app_context *ctx)
{
if (ctx->prefix) ctx->backend->add (ctx->backend, ctx->prefix);
- if (ctx->mpd_status) ctx->backend->add (ctx->backend, ctx->mpd_status);
+ if (ctx->mpd_stopped) ctx->backend->add (ctx->backend, "MPD stopped");
else if (ctx->mpd_song) ctx->backend->add (ctx->backend, ctx->mpd_song);
if (ctx->noise_end_time)
@@ -1470,9 +1506,8 @@ mpd_on_info_response (const struct mpd_response *response,
struct str_map map;
mpd_vector_to_map (data, &map);
- cstr_set (&ctx->mpd_status, NULL);
-
struct str s = str_make ();
+ ctx->mpd_stopped = false;
const char *value;
if ((value = str_map_find (&map, "state")))
@@ -1480,7 +1515,7 @@ mpd_on_info_response (const struct mpd_response *response,
// Unicode approximates since in proportional fonts ASCII looks ugly
// and I don't want to depend on a particular font with player chars
if (!strcmp (value, "stop"))
- ctx->mpd_status = xstrdup ("MPD stopped");
+ ctx->mpd_stopped = true;
else if (!strcmp (value, "pause"))
str_append (&s, "▯▯ " /* "|| " */);
else
@@ -1577,6 +1612,10 @@ mpd_on_failure (void *user_data)
struct app_context *ctx = user_data;
print_error ("connection to MPD failed");
mpd_queue_reconnect (ctx);
+
+ cstr_set (&ctx->mpd_song, NULL);
+ ctx->mpd_stopped = false;
+ refresh_status (ctx);
}
static void
@@ -2030,8 +2069,19 @@ on_noise_adjust (struct app_context *ctx, int arg)
if (!ctx->noise_end_time && (arg < 0 || !noise_start (ctx)))
return;
- // The granularity of noise playback is whole minutes
- ctx->noise_end_time += arg * 60;
+ time_t now = time (NULL);
+ int diff = difftime (ctx->noise_end_time, now);
+
+ // The granularity of noise playback setting is whole hours.
+ enum { SECOND = 1, MINUTE = 60, HOUR = 3600 };
+ if (arg > 0)
+ // Add a minute to enable stepping up from 0:59 to 2:00.
+ diff = (diff + arg * HOUR + MINUTE) / HOUR * HOUR;
+ else if (arg++ < 0)
+ // Remove a second to enable stepping down from 2:00 to 1:00.
+ diff = (diff + arg * HOUR - SECOND) / HOUR * HOUR;
+
+ ctx->noise_end_time = now + diff;
on_noise_timer (ctx);
}
@@ -2186,9 +2236,9 @@ spawn (char *argv[])
mpd_client_idle (c, 0); \
}
-// XXX: pause without argument is deprecated, we can watch play state
-// if we want to have the toggle pause/play functionality
-MPD_SIMPLE (play, "pause", NULL)
+
+MPD_SIMPLE (play, "play", NULL)
+MPD_SIMPLE (toggle, "pause", NULL)
MPD_SIMPLE (stop, "stop", NULL)
MPD_SIMPLE (prev, "previous", NULL)
MPD_SIMPLE (next, "next", NULL)
@@ -2196,6 +2246,12 @@ MPD_SIMPLE (forward, "seekcur", "+10", NULL)
MPD_SIMPLE (backward, "seekcur", "-10", NULL)
static void
+on_mpd_play_toggle (struct app_context *ctx, int arg)
+{
+ (ctx->mpd_stopped ? on_mpd_play : on_mpd_toggle) (ctx, arg);
+}
+
+static void
on_volume_finish (pa_context *context, int success, void *userdata)
{
(void) context;
@@ -2280,8 +2336,11 @@ static void
on_input_switch (struct app_context *ctx, int arg)
{
(void) ctx;
- char *values[] = { "vga", "dvi", "dp", "hdmi" };
- char *argv[] = { "input-switch", values[arg], NULL };
+
+ char *values[] = { "vga", "dvi", "hdmi", "dp" },
+ *numbers[] = { "1", "2" };
+ char *argv[] = { "input-switch",
+ values[arg & 0xf], numbers[arg >> 4], NULL };
spawn (argv);
}
@@ -2389,27 +2448,33 @@ g_keys[] =
// can be used to figure out which modifier is AltGr
// MPD
- { Mod4Mask, XK_Up, on_mpd_play, 0 },
+ { Mod4Mask, XK_Up, on_mpd_play_toggle, 0 },
{ Mod4Mask, XK_Down, on_mpd_stop, 0 },
{ Mod4Mask, XK_Left, on_mpd_prev, 0 },
{ Mod4Mask, XK_Right, on_mpd_next, 0 },
{ Mod4Mask | ShiftMask, XK_Left, on_mpd_backward, 0 },
{ Mod4Mask | ShiftMask, XK_Right, on_mpd_forward, 0 },
- { 0, XF86XK_AudioPlay, on_mpd_play, 0 },
+ { 0, XF86XK_AudioPlay, on_mpd_play_toggle, 0 },
{ 0, XF86XK_AudioPrev, on_mpd_prev, 0 },
{ 0, XF86XK_AudioNext, on_mpd_next, 0 },
- // Display input sources
- { Mod4Mask, XK_F5, on_input_switch, 0 },
- { Mod4Mask, XK_F6, on_input_switch, 1 },
- { Mod4Mask, XK_F7, on_input_switch, 2 },
- { Mod4Mask, XK_F8, on_input_switch, 3 },
-
// Keyboard groups
- { Mod4Mask, XK_F9, on_lock_group, 0 },
- { Mod4Mask, XK_F10, on_lock_group, 1 },
- { Mod4Mask, XK_F11, on_lock_group, 2 },
- { Mod4Mask, XK_F12, on_lock_group, 3 },
+ { Mod4Mask, XK_F1, on_lock_group, 0 },
+ { Mod4Mask, XK_F2, on_lock_group, 1 },
+ { Mod4Mask, XK_F3, on_lock_group, 2 },
+ { Mod4Mask, XK_F4, on_lock_group, 3 },
+
+#define CSMask (ControlMask | ShiftMask)
+
+ // Display input sources
+ { Mod4Mask | ControlMask, XK_F1, on_input_switch, 0 },
+ { Mod4Mask | CSMask, XK_F1, on_input_switch, 16 | 0 },
+ { Mod4Mask | ControlMask, XK_F2, on_input_switch, 1 },
+ { Mod4Mask | CSMask, XK_F2, on_input_switch, 16 | 1 },
+ { Mod4Mask | ControlMask, XK_F3, on_input_switch, 2 },
+ { Mod4Mask | CSMask, XK_F3, on_input_switch, 16 | 2 },
+ { Mod4Mask | ControlMask, XK_F4, on_input_switch, 3 },
+ { Mod4Mask | CSMask, XK_F4, on_input_switch, 16 | 3 },
// Brightness
{ Mod4Mask, XK_Home, on_brightness, 10 },
@@ -2417,8 +2482,8 @@ g_keys[] =
{ 0, XF86XK_MonBrightnessUp, on_brightness, 10 },
{ 0, XF86XK_MonBrightnessDown, on_brightness, -10 },
- { Mod4Mask, XK_F4, on_standby, 0 },
- { Mod4Mask | ShiftMask, XK_F4, on_insomnia, 0 },
+ { Mod4Mask, XK_F5, on_standby, 0 },
+ { Mod4Mask | ShiftMask, XK_F5, on_insomnia, 0 },
{ Mod4Mask, XK_Pause, on_standby, 0 },
{ Mod4Mask | ShiftMask, XK_Pause, on_insomnia, 0 },
@@ -2438,8 +2503,8 @@ g_keys[] =
{ 0, XF86XK_AudioMicMute, on_volume_mic_mute, 0 },
// Noise playback
- { ControlMask, XF86XK_AudioRaiseVolume, on_noise_adjust, 60 },
- { ControlMask, XF86XK_AudioLowerVolume, on_noise_adjust, -60 },
+ { ControlMask, XF86XK_AudioRaiseVolume, on_noise_adjust, 1 },
+ { ControlMask, XF86XK_AudioLowerVolume, on_noise_adjust, -1 },
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -2637,7 +2702,7 @@ main (int argc, char *argv[])
{ 'd', "debug", NULL, 0, "run in debug mode" },
{ 'h', "help", NULL, 0, "display this help and exit" },
{ 'V', "version", NULL, 0, "output version information and exit" },
- { '3', "i3bar", NULL, 0, "print output for i3bar instead" },
+ { '3', "i3bar", NULL, 0, "print output for i3bar/sway-bar instead" },
{ 'w', "write-default-cfg", "FILENAME",
OPT_OPTIONAL_ARG | OPT_LONG_ONLY,
"write a default configuration file and exit" },
@@ -2711,7 +2776,8 @@ main (int argc, char *argv[])
if (ctx.backend->start)
ctx.backend->start (ctx.backend);
- poller_pa_run (ctx.api);
+ while (true)
+ poller_run (&ctx.poller);
if (ctx.backend->stop)
ctx.backend->stop (ctx.backend);