LemonBoy / bar

A featherweight, lemon-scented, bar based on xcb
MIT License
1.61k stars 194 forks source link

Allow changing the 'default' background color at runtime #238

Open Ingvix opened 2 years ago

Ingvix commented 2 years ago

I was surprised to notice that I couldn't change the background color of the whole bar at runtime, only the text background. I wished to change my bar color depending on which monitor has the focus but that doesn't seem to be possible. Would it be reasonable to implement this? I tried it myself but couldn't get it to work so far as this is my first time dabbling with xcb.

Ingvix commented 2 years ago

And even if it's not implemented, I wouldn't mind any pointers making it for myself. I've tried doing it like this:

diff --git a/lemonbar.c b/lemonbar.c
index 4da59ee..6c4d3de 100644
--- a/lemonbar.c
+++ b/lemonbar.c
@@ -115,6 +115,14 @@ update_gc (void)
     xcb_change_gc(c, gc[GC_ATTR], XCB_GC_FOREGROUND, (const uint32_t []){ ugc.v });
 }

+void
+update_dbgc (void)
+{
+    for (monitor_t *mon = monhead; mon; mon = mon->next)
+        xcb_change_window_attributes(c, mon->window, XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL,
+                (const uint32_t[]){dbgc.v, dbgc.v});
+}
+
 void
 fill_gradient (xcb_drawable_t d, int x, int y, int width, int height, rgba_t start, rgba_t stop)
 {
@@ -588,6 +596,7 @@ parse (char *text)
                         draw_lines(cur_mon, left_ep, right_ep - left_ep);
                         pos_x = 0; align = ALIGN_R;
                     } break;
+                    case 'b': dbgc = bgc = parse_color(p, &p, dbgc); update_dbgc(); update_gc(); break;

                     // Define input area.
                     case 'A': {

This results in the bar changing color on the next input line and not the current and somehow even this doesn't happen unless there's some pause between the inputs, as in echo "%{b#F00}foo"; sleep 1; echo "bar" | lemonbar -p changes the color on "bar" but echo "%{b#F00}foo"; echo "bar" | lemonbar -p doesn't change it at all. I have no clue why is this happening.

Also it would probably be wiser to use some additional variable to replace dbgc here to preserve the original background color but first I'd like to get this to work properly.

Ingvix commented 2 years ago

Another option would be to modify the B command's effect to continue from alignment to another painting the any gaps with the assigned background color, which would probably be more intuitive way of doing it, actually increasing the customization possibilities without increasing usage complexity.

Ingvix commented 2 years ago

I noticed the background is actually drawn on it everytime with fill_rect() before parsing the input line and therefore setting the window background doesn't do a thing visually. So that's one mystery solved.

Also it's stupid trying to drawing the color to every monitor's bar.

Ingvix commented 2 years ago

I ended up with this sort of solution that works for my use case. It works if used before any text to be drawn on the bar, otherwise it draws the rectangle on top of what's already drawn. "r" as an argument to "b" reverses the foreground and background color, so it's not really intuitive but since standalone "r" was already taken I just made it like this.

So clearly this is not something you'd want on the official release but if for the time being someone else without the proper knowhow wants to have this feature, they can just patch their build with this. I might not have enough motivation and/or knowhow to work on the good kind of implementation as described in https://github.com/LemonBoy/bar/issues/238#issuecomment-961228103 but I hope someone else will.

diff --git a/lemonbar.c b/lemonbar.c
index 4da59ee..975dbc4 100644
--- a/lemonbar.c
+++ b/lemonbar.c
@@ -603,6 +603,19 @@ parse (char *text)
                     case 'B': bgc = parse_color(p, &p, dbgc); update_gc(); break;
                     case 'F': fgc = parse_color(p, &p, dfgc); update_gc(); break;
                     case 'U': ugc = parse_color(p, &p, dugc); update_gc(); break;
+                    case 'b' : {
+                        if (*p == 'r') {
+                            rgba_t tmp = bgc;
+                            bgc = fgc;
+                            fgc = tmp;
+                            p += 1;
+                        } else {
+                            bgc = parse_color(p, &p, dbgc);
+                        }
+                        update_gc();
+                        fill_rect(cur_mon->pixmap, gc[GC_CLEAR], 0, 0, cur_mon->width, bh);
+                    } break;
+

                     // Set current monitor used for drawing.
                     case 'S': {