dwmstatus: add i3bar support, rename to wmstatus

This commit is contained in:
Přemysl Eric Janouch 2016-12-30 11:30:52 +01:00
parent 5e5ccb8748
commit 617bc12ea2
Signed by: p
GPG Key ID: B715679E3A361BE6
3 changed files with 209 additions and 40 deletions

View File

@ -35,9 +35,9 @@ configure_file (${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h
include_directories (${PROJECT_BINARY_DIR}) include_directories (${PROJECT_BINARY_DIR})
# Build # Build
add_executable (dwmstatus dwmstatus.c) add_executable (wmstatus wmstatus.c)
target_link_libraries (dwmstatus ${project_libraries}) target_link_libraries (wmstatus ${project_libraries})
add_threads (dwmstatus) add_threads (wmstatus)
add_executable (brightness brightness.c) add_executable (brightness brightness.c)
target_link_libraries (brightness ${project_libraries}) target_link_libraries (brightness ${project_libraries})
@ -72,7 +72,7 @@ if (WITH_GDM)
install (TARGETS gdm-switch-user DESTINATION ${CMAKE_INSTALL_BINDIR}) install (TARGETS gdm-switch-user DESTINATION ${CMAKE_INSTALL_BINDIR})
endif (WITH_GDM) endif (WITH_GDM)
install (TARGETS dwmstatus brightness fancontrol-ng siprandom install (TARGETS wmstatus brightness fancontrol-ng siprandom
DESTINATION ${CMAKE_INSTALL_BINDIR}) DESTINATION ${CMAKE_INSTALL_BINDIR})
install (PROGRAMS shellify DESTINATION ${CMAKE_INSTALL_BINDIR}) install (PROGRAMS shellify DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})

View File

@ -5,7 +5,7 @@ desktop-tools
'desktop-tools' is a collection of tools to run my desktop that might be useful 'desktop-tools' is a collection of tools to run my desktop that might be useful
to other people as well: to other people as well:
- 'dwmstatus' does literally everything my dwm doesn't but I'd like it to. It - 'wmstatus' does literally everything my dwm doesn't but I'd like it to. It
includes PulseAudio volume management and hand-written NUT and MPD clients, includes PulseAudio volume management and hand-written NUT and MPD clients,
all in the name of liberation from GPL-licensed software of course. all in the name of liberation from GPL-licensed software of course.
- 'brightness' allows me to change the brightness of w/e display device I have. - 'brightness' allows me to change the brightness of w/e display device I have.

View File

@ -1,5 +1,5 @@
/* /*
* dwmstatus.c: simple PulseAudio-enabled dwmstatus * wmstatus.c: simple PulseAudio-enabled status setter for dwm and i3
* *
* Copyright (c) 2015 - 2016, Přemysl Janouch <p.janouch@gmail.com> * Copyright (c) 2015 - 2016, Přemysl Janouch <p.janouch@gmail.com>
* *
@ -25,7 +25,7 @@
#include "config.h" #include "config.h"
#undef PROGRAM_NAME #undef PROGRAM_NAME
#define PROGRAM_NAME "dwmstatus" #define PROGRAM_NAME "wmstatus"
#include "liberty/liberty.c" #include "liberty/liberty.c"
#include <dirent.h> #include <dirent.h>
@ -57,12 +57,19 @@ log_message_custom (void *user_data, const char *quote, const char *fmt,
fputs ("\n", stream); fputs ("\n", stream);
} }
static void // TODO: we almost certainly want this in liberty instead of the "char" version
set_dwm_status (Display *dpy, const char *str) static char *
join_str_vector_ex (const struct str_vector *v, const char *delimiter)
{ {
print_debug ("setting status to: %s", str); if (!v->len)
XStoreName (dpy, DefaultRootWindow (dpy), str); return xstrdup ("");
XSync (dpy, False);
struct str result;
str_init (&result);
str_append (&result, v->vector[0]);
for (size_t i = 1; i < v->len; i++)
str_append_printf (&result, "%s%s", delimiter, v->vector[i]);
return str_steal (&result);
} }
// --- PulseAudio mainloop abstraction ----------------------------------------- // --- PulseAudio mainloop abstraction -----------------------------------------
@ -960,6 +967,158 @@ nut_client_connect
self->state = NUT_CONNECTING; self->state = NUT_CONNECTING;
} }
// --- Backends ----------------------------------------------------------------
// TODO: rename the whole application to just wmstatus
struct backend
{
/// Initialization
void (*start) (struct backend *self);
/// Deinitialization
void (*stop) (struct backend *self);
/// Destroy the backend object
void (*destroy) (struct backend *self);
/// Add another entry to the status
void (*add) (struct backend *self, const char *entry);
/// Flush the status to the window manager
void (*flush) (struct backend *self);
};
// --- DWM backend -------------------------------------------------------------
struct backend_dwm
{
struct backend super; ///< Parent class
Display *dpy; ///< X11 Display
struct str_vector items; ///< Items on the current row
};
static void
backend_dwm_destroy (struct backend *b)
{
struct backend_dwm *self = CONTAINER_OF (b, struct backend_dwm, super);
str_vector_free (&self->items);
free (self);
}
static void
backend_dwm_add (struct backend *b, const char *entry)
{
struct backend_dwm *self = CONTAINER_OF (b, struct backend_dwm, super);
str_vector_add (&self->items, entry);
}
static void
backend_dwm_flush (struct backend *b)
{
struct backend_dwm *self = CONTAINER_OF (b, struct backend_dwm, super);
char *str = join_str_vector_ex (&self->items, " ");
str_vector_reset (&self->items);
print_debug ("setting status to: %s", str);
XStoreName (self->dpy, DefaultRootWindow (self->dpy), str);
XSync (self->dpy, False);
free (str);
}
static struct backend *
backend_dwm_new (Display *dpy)
{
struct backend_dwm *self = xcalloc (1, sizeof *self);
self->super.destroy = backend_dwm_destroy;
self->super.add = backend_dwm_add;
self->super.flush = backend_dwm_flush;
self->dpy = dpy;
str_vector_init (&self->items);
return &self->super;
}
// --- i3bar backend -----------------------------------------------------------
struct backend_i3
{
struct backend super; ///< Parent class
struct str_vector items; ///< Items on the current row
};
static void
backend_i3_destroy (struct backend *b)
{
struct backend_dwm *self = CONTAINER_OF (b, struct backend_dwm, super);
str_vector_free (&self->items);
free (self);
}
static void
backend_i3_start (struct backend *b)
{
(void) b;
// Start with an empty array so that we can later start with a comma
// as i3bar's JSON library is quite pedantic
fputs ("{\"version\":1}\n[[]", stdout);
}
static void
backend_i3_stop (struct backend *b)
{
(void) b;
fputc (']', stdout);
}
static void
backend_i3_add (struct backend *b, const char *entry)
{
struct backend_i3 *self = CONTAINER_OF (b, struct backend_i3, super);
str_vector_add (&self->items, entry);
}
static void
backend_i3_flush (struct backend *b)
{
struct backend_i3 *self = CONTAINER_OF (b, struct backend_i3, super);
fputs (",[", stdout);
for (size_t i = 0; i < self->items.len; i++)
{
if (i) fputc (',', stdout);
const char *str = self->items.vector[i];
size_t len = strlen (str);
if (!soft_assert (utf8_validate (str, len)))
continue;
fputs ("{\"full_text\":\"", stdout);
for (const char *p = str; *p; p++)
if (*p == '"')
fputs ("\\\"", stdout);
else if (*p == '\\')
fputs ("\\\\", stdout);
else
fputc (*p, stdout);
fputs ("\",\"separator\":false}", stdout);
}
fputs ("]\n", stdout);
// We need to flush the pipe explicitly to get i3bar to update
fflush (stdout);
str_vector_reset (&self->items);
}
static struct backend *
backend_i3_new (void)
{
struct backend_i3 *self = xcalloc (1, sizeof *self);
self->super.start = backend_i3_start;
self->super.stop = backend_i3_stop;
self->super.add = backend_i3_add;
self->super.flush = backend_i3_flush;
str_vector_init (&self->items);
return &self->super;
}
// --- Configuration ----------------------------------------------------------- // --- Configuration -----------------------------------------------------------
static struct simple_config_item g_config_table[] = static struct simple_config_item g_config_table[] =
@ -984,6 +1143,7 @@ static struct simple_config_item g_config_table[] =
struct app_context struct app_context
{ {
struct str_map config; ///< Program configuration struct str_map config; ///< Program configuration
struct backend *backend; ///< WM backend
Display *dpy; ///< X display handle Display *dpy; ///< X display handle
int xkb_base_event_code; ///< Xkb base event code int xkb_base_event_code; ///< Xkb base event code
@ -1070,6 +1230,7 @@ static void
app_context_free (struct app_context *self) app_context_free (struct app_context *self)
{ {
str_map_free (&self->config); str_map_free (&self->config);
if (self->backend) self->backend->destroy (self->backend);
poller_fd_reset (&self->x_event); poller_fd_reset (&self->x_event);
free (self->layout); free (self->layout);
@ -1233,7 +1394,7 @@ make_battery_status (void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static char * static char *
make_time_status (char *fmt) make_time_status (const char *fmt)
{ {
char buf[129] = ""; char buf[129] = "";
time_t now = time (NULL); time_t now = time (NULL);
@ -1257,55 +1418,49 @@ make_volume_status (struct app_context *ctx)
struct str s; struct str s;
str_init (&s); str_init (&s);
str_append_printf (&s, "%u%%", VOLUME_PERCENT (ctx->sink_volume.values[0])); if (ctx->sink_muted)
if (!pa_cvolume_channels_equal_to (&ctx->sink_volume, ctx->sink_volume.values[0])) str_append (&s, "Muted ");
str_append_printf (&s,
"%u%%", VOLUME_PERCENT (ctx->sink_volume.values[0]));
if (!pa_cvolume_channels_equal_to
(&ctx->sink_volume, ctx->sink_volume.values[0]))
{
for (size_t i = 1; i < ctx->sink_volume.channels; i++) for (size_t i = 1; i < ctx->sink_volume.channels; i++)
str_append_printf (&s, " / %u%%", str_append_printf (&s, " / %u%%",
VOLUME_PERCENT (ctx->sink_volume.values[i])); VOLUME_PERCENT (ctx->sink_volume.values[i]));
}
return str_steal (&s); return str_steal (&s);
} }
static void static void
refresh_status (struct app_context *ctx) refresh_status (struct app_context *ctx)
{ {
struct str status; if (ctx->prefix) ctx->backend->add (ctx->backend, ctx->prefix);
str_init (&status);
if (ctx->prefix) if (ctx->mpd_status) ctx->backend->add (ctx->backend, ctx->mpd_status);
str_append_printf (&status, "%s ", ctx->prefix); else if (ctx->mpd_song) ctx->backend->add (ctx->backend, ctx->mpd_song);
if (ctx->mpd_status) if (ctx->failed) ctx->backend->add (ctx->backend, "PA failure");
str_append_printf (&status, "%s ", ctx->mpd_status);
else if (ctx->mpd_song)
str_append_printf (&status, "%s ", ctx->mpd_song);
if (ctx->failed)
str_append_printf (&status, "%s ", "PA failure");
else else
{ {
char *volumes = make_volume_status (ctx); char *volumes = make_volume_status (ctx);
str_append_printf (&status, "%s%s ", ctx->backend->add (ctx->backend, volumes);
ctx->sink_muted ? "Muted " : "", volumes);
free (volumes); free (volumes);
} }
char *battery = make_battery_status (); char *battery = make_battery_status ();
if (battery) if (battery) ctx->backend->add (ctx->backend, battery);
str_append_printf (&status, "%s ", battery);
free (battery); free (battery);
if (ctx->nut_status) if (ctx->nut_status) ctx->backend->add (ctx->backend, ctx->nut_status);
str_append_printf (&status, "%s ", ctx->nut_status); if (ctx->layout) ctx->backend->add (ctx->backend, ctx->layout);
if (ctx->layout)
str_append_printf (&status, "%s ", ctx->layout);
char *times = make_time_status ("Week %V, %a %d %b %Y %H:%M %Z"); char *times = make_time_status ("Week %V, %a %d %b %Y %H:%M %Z");
str_append (&status, times); ctx->backend->add (ctx->backend, times);
free (times); free (times);
set_dwm_status (ctx->dpy, status.str); ctx->backend->flush (ctx->backend);
str_free (&status);
} }
static void static void
@ -2211,6 +2366,7 @@ main (int argc, char *argv[])
{ 'd', "debug", NULL, 0, "run in debug mode" }, { 'd', "debug", NULL, 0, "run in debug mode" },
{ 'h', "help", NULL, 0, "display this help and exit" }, { 'h', "help", NULL, 0, "display this help and exit" },
{ 'V', "version", NULL, 0, "output version information and exit" }, { 'V', "version", NULL, 0, "output version information and exit" },
{ '3', "i3bar", NULL, 0, "print output for i3bar instead" },
{ 'w', "write-default-cfg", "FILENAME", { 'w', "write-default-cfg", "FILENAME",
OPT_OPTIONAL_ARG | OPT_LONG_ONLY, OPT_OPTIONAL_ARG | OPT_LONG_ONLY,
"write a default configuration file and exit" }, "write a default configuration file and exit" },
@ -2219,6 +2375,7 @@ main (int argc, char *argv[])
struct opt_handler oh; struct opt_handler oh;
opt_handler_init (&oh, argc, argv, opts, NULL, "Set root window name."); opt_handler_init (&oh, argc, argv, opts, NULL, "Set root window name.");
bool i3bar = false;
int c; int c;
while ((c = opt_handler_get (&oh)) != -1) while ((c = opt_handler_get (&oh)) != -1)
@ -2233,6 +2390,9 @@ main (int argc, char *argv[])
case 'V': case 'V':
printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
case '3':
i3bar = true;
break;
case 'w': case 'w':
call_simple_config_write_default (optarg, g_config_table); call_simple_config_write_default (optarg, g_config_table);
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
@ -2276,8 +2436,17 @@ main (int argc, char *argv[])
grab_keys (&ctx); grab_keys (&ctx);
poller_pa_run (ctx.api); if (i3bar)
app_context_free (&ctx); ctx.backend = backend_i3_new ();
else
ctx.backend = backend_dwm_new (ctx.dpy);
if (ctx.backend->start)
ctx.backend->start (ctx.backend);
poller_pa_run (ctx.api);
if (ctx.backend->stop)
ctx.backend->stop (ctx.backend);
app_context_free (&ctx);
return 0; return 0;
} }