From 6ce01bb31cf2b6aeffc4aa3ebec5643da7587d35 Mon Sep 17 00:00:00 2001 From: Jason Moggridge Date: Sat, 23 Apr 2016 22:55:53 -0400 Subject: [PATCH] Give bar control over input generating command. When the bar is getting input from a loop with a pause, there can be a noticable delay to visually updating the bar on click events. By letting bar call an input command it can now update instantly on a click event and still have a delay so it's not an idle hog. Something like: while true; do date; sleep 10; done | bar -d Now becomes: bar -d -c "date" -t 10 Either option may be ommited and the bar will still perform as before. If a command (-c) is given but not a timeout (-t) then the timeout defaults to 5 seconds. Changed compiler standard to gnu99 for use with popen()/pclose() --- Makefile | 2 +- lemonbar.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 24800a5..605f1a6 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ ifneq "$(GIT_DESC)" "" endif CC ?= gcc -CFLAGS += -Wall -std=c99 -Os -DVERSION="\"$(VERSION)\"" +CFLAGS += -Wall -std=gnu99 -Os -DVERSION="\"$(VERSION)\"" LDFLAGS += -lxcb -lxcb-xinerama -lxcb-randr CFDEBUG = -g3 -pedantic -Wall -Wunused-parameter -Wlong-long \ -Wsign-conversion -Wconversion -Wimplicit-function-declaration diff --git a/lemonbar.c b/lemonbar.c index c31cb26..c507e35 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -81,6 +81,7 @@ enum { }; #define MAX_FONT_COUNT 5 +#define INPUT_SIZE 4024 static xcb_connection_t *c; static xcb_screen_t *scr; @@ -100,6 +101,29 @@ static rgba_t fgc, bgc, ugc; static rgba_t dfgc, dbgc, dugc; static area_stack_t area_stack; +int +generate_input(const char * command, char * input, size_t input_size) +{ + FILE * command_output; + + if ((command_output = popen(command, "r")) == NULL) { + fprintf(stderr, "Could not run provided command\n"); + return -1; + } + + if (fgets(input, input_size, command_output) == NULL) { + fprintf(stderr, "Command did not produce any output\n"); + return -1; + } + + if (pclose(command_output) == -1) { + fprintf(stderr, "Could not close pipe to command\n"); + return -1; + } + + return 0; +} + void update_gc (void) { @@ -1188,13 +1212,13 @@ init (char *wm_name) // Create the gc for drawing gc[GC_DRAW] = xcb_generate_id(c); - xcb_create_gc(c, gc[GC_DRAW], monhead->pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ fgc.v }); + xcb_create_gc(c, gc[GC_DRAW], monhead->pixmap, XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, (const uint32_t []){ fgc.v, 0 }); gc[GC_CLEAR] = xcb_generate_id(c); - xcb_create_gc(c, gc[GC_CLEAR], monhead->pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ bgc.v }); + xcb_create_gc(c, gc[GC_CLEAR], monhead->pixmap, XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, (const uint32_t []){ bgc.v, 0 }); gc[GC_ATTR] = xcb_generate_id(c); - xcb_create_gc(c, gc[GC_ATTR], monhead->pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ ugc.v }); + xcb_create_gc(c, gc[GC_ATTR], monhead->pixmap, XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES, (const uint32_t []){ ugc.v, 0 }); // Make the bar visible and clear the pixmap for (monitor_t *mon = monhead; mon; mon = mon->next) { @@ -1261,12 +1285,15 @@ main (int argc, char **argv) xcb_generic_event_t *ev; xcb_expose_event_t *expose_ev; xcb_button_press_event_t *press_ev; - char input[4096] = {0, }; + char input[INPUT_SIZE] = {0, }; bool permanent = false; int geom_v[4] = { -1, -1, 0, 0 }; int ch, areas; char *wm_name; + char *input_command = NULL; + int input_timeout = -1; + // Install the parachute! atexit(cleanup); signal(SIGINT, sighandle); @@ -1284,7 +1311,7 @@ main (int argc, char **argv) // Connect to the Xserver and initialize scr xconn(); - while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:U:n:")) != -1) { + while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:U:n:c:t:")) != -1) { switch (ch) { case 'h': printf ("lemonbar version %s\n", VERSION); @@ -1299,7 +1326,9 @@ main (int argc, char **argv) "\t-n Set the WM_NAME atom to the specified value for this bar\n" "\t-u Set the underline/overline height in pixels\n" "\t-B Set background color in #AARRGGBB\n" - "\t-F Set foreground color in #AARRGGBB\n", argv[0]); + "\t-F Set foreground color in #AARRGGBB\n" + "\t-c Command to execute instead of reading from stdin\n" + "\t-t Timeout (sec) to wait between updating with the command\n", argv[0]); exit (EXIT_SUCCESS); case 'g': (void)parse_geometry_string(optarg, geom_v); break; case 'p': permanent = true; break; @@ -1312,9 +1341,21 @@ main (int argc, char **argv) case 'F': dfgc = fgc = parse_color(optarg, NULL, (rgba_t)0xffffffffU); break; case 'U': dugc = ugc = parse_color(optarg, NULL, fgc); break; case 'a': areas = strtoul(optarg, NULL, 10); break; + case 'c': input_command = optarg; break; + case 't': input_timeout = strtol(optarg, NULL, 10) * 1000; break; } } + // Don't wait for input if there is a command, but no user defined timeout + if (input_command != NULL && input_timeout < 1) { + input_timeout = 5000; + } + + // Revert to normal usage if there is a timeout but no command + if (input_command == NULL && input_timeout >= 1) { + input_timeout = -1; + } + // Initialize the stack holding the clickable areas area_stack.at = 0; area_stack.max = areas; @@ -1348,12 +1389,12 @@ main (int argc, char **argv) if (xcb_connection_has_error(c)) break; - if (poll(pollin, 2, -1) > 0) { + if (poll(pollin, 2, input_timeout) > 0) { if (pollin[0].revents & POLLHUP) { // No more data... if (permanent) pollin[0].fd = -1; // ...null the fd and continue polling :D else break; // ...bail out } - if (pollin[0].revents & POLLIN) { // New input, process it + if (pollin[0].revents & POLLIN && input_command == NULL) { // New input, process it if (fgets(input, sizeof(input), stdin) == NULL) break; // EOF received @@ -1370,6 +1411,9 @@ main (int argc, char **argv) redraw = true; break; case XCB_BUTTON_PRESS: + if (expose_ev->count == 0) + redraw = true; + press_ev = (xcb_button_press_event_t *)ev; { area_t *area = area_get(press_ev->event, press_ev->detail, press_ev->event_x); @@ -1387,6 +1431,17 @@ main (int argc, char **argv) } } + // If there is a user supplied command, use it to generate input + if (input_command != NULL) { + if (generate_input(input_command, input, INPUT_SIZE) != 0) { + fprintf(stderr, "Failed to generate input from command\n"); + return EXIT_FAILURE; + } + + parse(input); + redraw = true; + } + if (redraw) { // Copy our temporary pixmap onto the window for (monitor_t *mon = monhead; mon; mon = mon->next) { xcb_copy_area(c, mon->pixmap, mon->window, gc[GC_DRAW], 0, 0, 0, 0, mon->width, bh);