From 0d6f35383b6254d9702fab29e687567da1ffa362 Mon Sep 17 00:00:00 2001 From: Jesper Jensen Date: Sat, 20 Feb 2016 15:59:12 +0100 Subject: [PATCH] Support for RandR This should allow lemonbar to dynamically detect and handle RandR changes. When ever a RandR ouput change notification is received we fetch all the randr monitors and resize the bars to fit them. Creating new ones where they are missing. --- lemonbar.c | 185 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 149 insertions(+), 36 deletions(-) diff --git a/lemonbar.c b/lemonbar.c index 2198731..eb7bfa9 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -85,6 +85,7 @@ static xcb_screen_t *scr; static xcb_gcontext_t gc[GC_MAX]; static xcb_visualid_t visual; static xcb_colormap_t colormap; +static const xcb_query_extension_reply_t *randr; static monitor_t *monhead, *montail; static font_t *font_list[MAX_FONT_COUNT]; static int font_count = 0; @@ -92,6 +93,7 @@ static int font_index = -1; static uint32_t attrs = 0; static bool dock = false; static bool topbar = true; +static char *wm_name; static int bw = -1, bh = -1, bx = 0, by = 0; static int bu = 1; // Underline height static rgba_t fgc, bgc, ugc; @@ -731,7 +733,7 @@ set_ewmh_atoms (void) xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_DESKTOP], XCB_ATOM_CARDINAL, 32, 1, (const uint32_t []){ -1 } ); xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT_PARTIAL], XCB_ATOM_CARDINAL, 32, 12, strut); xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, atom_list[NET_WM_STRUT], XCB_ATOM_CARDINAL, 32, 4, strut); - xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, "bar"); + xcb_change_property(c, XCB_PROP_MODE_REPLACE, mon->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, 3, wm_name); } } @@ -801,23 +803,8 @@ rect_sort_cb (const void *p1, const void *p2) } void -monitor_create_chain (xcb_rectangle_t *rects, const int num) +bar_calculate_size (int width, int height) { - int i; - int width = 0, height = 0; - int left = bx; - - // Sort before use - qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb); - - for (i = 0; i < num; i++) { - int h = rects[i].y + rects[i].height; - // Accumulated width of all monitors - width += rects[i].width; - // Get height of screen from y_offset + height of lowest monitor - if (h >= height) - height = h; - } if (bw < 0) bw = width - bx; @@ -831,6 +818,35 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num) fprintf(stderr, "The geometry specified doesn't fit the screen!\n"); exit(EXIT_FAILURE); } +} + +void +calculate_screen_size(xcb_rectangle_t *rects, const int num, int* width, int* height) +{ + int i; + // Sort before use + qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb); + + for (i = 0; i < num; i++) { + int h = rects[i].y + rects[i].height; + // Accumulated width of all monitors + *width += rects[i].width; + // Get height of screen from y_offset + height of lowest monitor + if (h >= *height) + *height = h; + } +} + +void +monitor_create_chain (xcb_rectangle_t *rects, const int num) +{ + int i; + int width = 0, height = 0; + int left = bx; + + calculate_screen_size(rects, num, &width, &height); + + bar_calculate_size(width, height); // Left is a positive number or zero therefore monitors with zero width are excluded width = bw; @@ -863,6 +879,77 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num) } } +void +monitor_update_chain (xcb_rectangle_t *rects, const int num) +{ + int i; + int width = 0, height = 0; + int left = bx; + + calculate_screen_size(rects, num, &width, &height); + + bar_calculate_size(width, height); + + // Left is a positive number or zero therefore monitors with zero width are excluded + width = bw; + monitor_t *this = monhead; + for (i = 0; i < num; i++) { + if (rects[i].y + rects[i].height < by) + continue; + if (rects[i].width > left) { + if (this == NULL) { + this = monitor_new( + rects[i].x + left, + rects[i].y, + min(width, rects[i].width - left), + rects[i].height); + + if (!this) + break; + + monitor_add(this); + } else { + this->x = rects[i].x + left; + this->y = (topbar ? by : height - bh - by) + rects[i].y; + this->width = min(width, rects[i].width - left); + + xcb_configure_window(c, this->window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (const uint32_t []){ this->x, this->y, this->width, bh }); + + xcb_free_pixmap(c, this->pixmap); + this->pixmap = xcb_generate_id(c); + int depth = (visual == scr->root_visual) ? XCB_COPY_FROM_PARENT : 32; + xcb_create_pixmap(c, depth, this->pixmap, this->window, this->width, bh); + + } + this = this->next; + + width -= rects[i].width - left; + + // No need to check for other monitors + if (width <= 0) + break; + } + + left -= rects[i].width; + + if (left < 0) + left = 0; + } + + //Do we have too many monitors (Some were disconnected) + if(this) { + montail = this->prev; + this->prev->next = NULL; + while(this) { + monitor_t *next = this->next; + xcb_destroy_window(c, this->window); + xcb_free_pixmap(c, this->window); + free(this); + this = next; + } + } +} + void get_randr_monitors (void) { @@ -954,7 +1041,10 @@ get_randr_monitors (void) if (rects[i].width != 0) r[j++] = rects[i]; - monitor_create_chain(r, valid); + if (!monhead) + monitor_create_chain(r, valid); + else + monitor_update_chain(r, valid); } void @@ -1076,10 +1166,26 @@ xconn (void) colormap = xcb_generate_id(c); xcb_create_colormap(c, XCB_COLORMAP_ALLOC_NONE, colormap, scr->root, visual); + + randr = xcb_get_extension_data(c, &xcb_randr_id); + xcb_randr_select_input(c, scr->root, XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE); } void -init (char *wm_name) +map_monitors (void) +{ + for (monitor_t *mon = monhead; mon; mon = mon->next) { + fill_rect(mon->pixmap, gc[GC_CLEAR], 0, 0, mon->width, bh); + xcb_map_window(c, mon->window); + + // Make sure that the window really gets in the place it's supposed to be + // Some WM such as Openbox need this + xcb_configure_window(c, mon->window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (const uint32_t []){ mon->x, mon->y }); + } +} + +void +init (void) { // Try to load a default font if (!font_count) @@ -1159,19 +1265,7 @@ init (char *wm_name) gc[GC_ATTR] = xcb_generate_id(c); xcb_create_gc(c, gc[GC_ATTR], monhead->pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ ugc.v }); - // Make the bar visible and clear the pixmap - for (monitor_t *mon = monhead; mon; mon = mon->next) { - fill_rect(mon->pixmap, gc[GC_CLEAR], 0, 0, mon->width, bh); - xcb_map_window(c, mon->window); - - // Make sure that the window really gets in the place it's supposed to be - // Some WM such as Openbox need this - xcb_configure_window(c, mon->window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, (const uint32_t []){ mon->x, mon->y }); - - // Set the WM_NAME atom to the user specified value - if (wm_name) - xcb_change_property(c, XCB_PROP_MODE_REPLACE, monhead->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8 ,strlen(wm_name), wm_name); - } + map_monitors(); xcb_flush(c); } @@ -1228,7 +1322,6 @@ main (int argc, char **argv) bool permanent = false; int geom_v[4] = { -1, -1, 0, 0 }; int ch, areas; - char *wm_name; // Install the parachute! atexit(cleanup); @@ -1242,7 +1335,7 @@ main (int argc, char **argv) // A safe default areas = 10; - wm_name = NULL; + wm_name = "bar"; // Connect to the Xserver and initialize scr xconn(); @@ -1300,7 +1393,7 @@ main (int argc, char **argv) by = geom_v[3]; // Do the heavy lifting - init(wm_name); + init(); // Get the fd to Xserver pollin[1].fd = xcb_get_file_descriptor(c); @@ -1327,7 +1420,8 @@ main (int argc, char **argv) while ((ev = xcb_poll_for_event(c))) { expose_ev = (xcb_expose_event_t *)ev; - switch (ev->response_type & 0x7F) { + uint8_t type = ev->response_type & 0x7F; + switch (type) { case XCB_EXPOSE: if (expose_ev->count == 0) redraw = true; @@ -1343,6 +1437,25 @@ main (int argc, char **argv) } } break; + default: + { + if(type == randr->first_event + XCB_RANDR_NOTIFY) { + xcb_randr_notify_event_t *event = (void *)ev; + switch(event->subCode) { + case XCB_RANDR_NOTIFY_OUTPUT_CHANGE: + bw = geom_v[0]; + bh = geom_v[1]; + bx = geom_v[2]; + by = geom_v[3]; + get_randr_monitors(); + set_ewmh_atoms(); + map_monitors(); + parse(input); + redraw = true; + break; + } + } + } } free(ev);