diff options
Diffstat (limited to 'wmstatus.c')
-rw-r--r-- | wmstatus.c | 198 |
1 files changed, 132 insertions, 66 deletions
@@ -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); |