Add support for named rand outputs

This commit is contained in:
jvvv 2017-04-02 14:32:16 +01:00 committed by Thomas Adam
parent 89010c508d
commit 170a7261fa
2 changed files with 138 additions and 44 deletions

View File

@ -24,6 +24,10 @@ Display the help and exit.
Set the window geometry. If a parameter is omitted it's filled with the default value. If the I<y> parameter is specified along with the B<-b> switch then the position is relative to the bottom of the screen. Set the window geometry. If a parameter is omitted it's filled with the default value. If the I<y> parameter is specified along with the B<-b> switch then the position is relative to the bottom of the screen.
=item B<-o> I<name>
Set next output to I<name>. May be used multiple times; order is significant. If any B<-o> options are given, only B<-o> specified monitors will be used. Invalid output names are silently ignored. (only supported on randr configurations at this time)
=item B<-b> =item B<-b>
Dock the bar at the bottom of the screen. Dock the bar at the bottom of the screen.
@ -143,6 +147,11 @@ First/last monitor.
Nth monitor. Nth monitor.
=item I<n>B<NAME>
Named monitor.
Eg. I<%{SnHDMI-0} This text will show up on the HDMI-0 output>
=back =back
=back =back

View File

@ -41,7 +41,8 @@ typedef struct font_t {
} font_t; } font_t;
typedef struct monitor_t { typedef struct monitor_t {
int x, y, width; char *name;
int x, y, width, height;
xcb_window_t window; xcb_window_t window;
xcb_pixmap_t pixmap; xcb_pixmap_t pixmap;
struct monitor_t *prev, *next; struct monitor_t *prev, *next;
@ -127,6 +128,8 @@ static XftDraw *xft_draw;
#define MAX_WIDTHS (1 << 16) #define MAX_WIDTHS (1 << 16)
static wchar_t xft_char[MAX_WIDTHS]; static wchar_t xft_char[MAX_WIDTHS];
static char xft_width[MAX_WIDTHS]; static char xft_width[MAX_WIDTHS];
static int num_outputs = 0;
static char **output_names = NULL;
void void
update_gc (void) update_gc (void)
@ -652,6 +655,15 @@ parse (char *text)
{ cur_mon = monhead; } { cur_mon = monhead; }
else if (*p == 'l') else if (*p == 'l')
{ cur_mon = montail ? montail : monhead; } { cur_mon = montail ? montail : monhead; }
else if (*p == 'n')
{ cur_mon = monhead;
while (cur_mon->next) {
if (cur_mon->name && !strncmp(cur_mon->name, p+1, (block_end-p)-1)) {
break;
}
cur_mon = cur_mon->next;
}
}
else if (isdigit(*p)) else if (isdigit(*p))
{ cur_mon = monhead; { cur_mon = monhead;
for (int i = 0; i != *p-'0' && cur_mon->next; i++) for (int i = 0; i != *p-'0' && cur_mon->next; i++)
@ -900,7 +912,7 @@ set_ewmh_atoms (void)
} }
monitor_t * monitor_t *
monitor_new (int x, int y, int width, int height) monitor_new (int x, int y, int width, int height, char *name)
{ {
monitor_t *ret; monitor_t *ret;
@ -910,6 +922,7 @@ monitor_new (int x, int y, int width, int height)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ret->name = name;
ret->x = x; ret->x = x;
ret->y = (topbar ? by : height - bh - by) + y; ret->y = (topbar ? by : height - bh - by) + y;
ret->width = width; ret->width = width;
@ -947,17 +960,17 @@ monitor_add (monitor_t *mon)
} }
int int
rect_sort_cb (const void *p1, const void *p2) mon_sort_cb (const void *p1, const void *p2)
{ {
const xcb_rectangle_t *r1 = (xcb_rectangle_t *)p1; const monitor_t *m1 = (monitor_t *)p1;
const xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2; const monitor_t *m2 = (monitor_t *)p2;
if (r1->x < r2->x || r1->y + r1->height <= r2->y) if (m1->x < m2->x || m1->y + m1->height <= m2->y)
{ {
return -1; return -1;
} }
if (r1->x > r2->x || r1->y + r1->height > r2->y) if (m1->x > m2->x || m1->y + m1->height > m2->y)
{ {
return 1; return 1;
} }
@ -966,19 +979,20 @@ rect_sort_cb (const void *p1, const void *p2)
} }
void void
monitor_create_chain (xcb_rectangle_t *rects, const int num) monitor_create_chain (monitor_t *mons, const int num)
{ {
int i; int i;
int width = 0, height = 0; int width = 0, height = 0;
int left = bx; int left = bx;
// Sort before use // Sort before use
qsort(rects, num, sizeof(xcb_rectangle_t), rect_sort_cb); if (!num_outputs)
qsort(mons, num, sizeof(monitor_t), mon_sort_cb);
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
int h = rects[i].y + rects[i].height; int h = mons[i].y + mons[i].height;
// Accumulated width of all monitors // Accumulated width of all monitors
width += rects[i].width; width += mons[i].width;
// Get height of screen from y_offset + height of lowest monitor // Get height of screen from y_offset + height of lowest monitor
if (h >= height) if (h >= height)
height = h; height = h;
@ -1000,27 +1014,29 @@ monitor_create_chain (xcb_rectangle_t *rects, const int num)
// Left is a positive number or zero therefore monitors with zero width are excluded // Left is a positive number or zero therefore monitors with zero width are excluded
width = bw; width = bw;
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
if (rects[i].y + rects[i].height < by) if (mons[i].y + mons[i].height < by)
continue; continue;
if (rects[i].width > left) { if (mons[i].width > left) {
monitor_t *mon = monitor_new( monitor_t *mon = monitor_new(
rects[i].x + left, mons[i].x + left,
rects[i].y, mons[i].y,
min(width, rects[i].width - left), min(width, mons[i].width - left),
rects[i].height); mons[i].height,
mons[i].name);
if (!mon) if (!mon)
break; break;
monitor_add(mon); monitor_add(mon);
width -= rects[i].width - left; width -= mons[i].width - left;
// No need to check for other monitors // No need to check for other monitors
if (width <= 0) if (width <= 0)
break; break;
} }
left -= rects[i].width; left -= mons[i].width;
if (left < 0) if (left < 0)
left = 0; left = 0;
@ -1052,7 +1068,7 @@ get_randr_monitors (void)
return; return;
} }
xcb_rectangle_t rects[num]; monitor_t mons[num];
// Get all outputs // Get all outputs
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
@ -1064,24 +1080,65 @@ get_randr_monitors (void)
// Output disconnected or not attached to any CRTC ? // Output disconnected or not attached to any CRTC ?
if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) { if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) {
free(oi_reply); free(oi_reply);
rects[i].width = 0; mons[i].width = 0;
continue; continue;
} }
ci_reply = xcb_randr_get_crtc_info_reply(c, ci_reply = xcb_randr_get_crtc_info_reply(c,
xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL); xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL);
free(oi_reply);
if (!ci_reply) { if (!ci_reply) {
fprintf(stderr, "Failed to get RandR ctrc info\n"); fprintf(stderr, "Failed to get RandR ctrc info\n");
free(rres_reply); free(rres_reply);
return; return;
} }
// There's no need to handle rotated screens here (see #69) if (num_outputs) {
rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height }; for (j = 0; j < num_outputs; j++) {
int namelen;
uint8_t *str;
char *name;
fprintf(stderr, "output_names: %s\n", output_names[j]);
// if this output name has been allocated, skip it
if (!output_names[j])
continue;
namelen = xcb_randr_get_output_info_name_length(oi_reply);
name = malloc(namelen+1);
if (!name) {
fprintf(stderr, "Failed to allocate randr output name\n");
exit(EXIT_FAILURE);
}
str = xcb_randr_get_output_info_name(oi_reply);
memcpy(name, str, namelen);
name[namelen] = '\0';
if (!memcmp(output_names[j], name, namelen)) {
mons[j] = (monitor_t){ name, ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height, 0, 0, NULL, NULL };
output_names[j] = NULL;
break;
}
else
free(name);
}
// if this output is not in the list, skip it
if (j == num_outputs) {
mons[i].width = 0;
free(oi_reply);
continue;
}
}
else {
// There's no need to handle rotated screens here (see #69)
mons[i] = (monitor_t){ NULL, ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height, 0, 0, NULL, NULL };
}
free(oi_reply);
free(ci_reply); free(ci_reply);
valid++; valid++;
@ -1089,18 +1146,21 @@ get_randr_monitors (void)
free(rres_reply); free(rres_reply);
if (num_outputs)
free(output_names);
// Check for clones and inactive outputs // Check for clones and inactive outputs
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
if (rects[i].width == 0) if (mons[i].width == 0)
continue; continue;
for (j = 0; j < num; j++) { for (j = 0; j < num; j++) {
// Does I contain J ? // Does I contain J ?
if (i != j && rects[j].width) { if (i != j && mons[j].width && !mons[j].name) {
if (rects[j].x >= rects[i].x && rects[j].x + rects[j].width <= rects[i].x + rects[i].width && if (mons[j].x >= mons[i].x && mons[j].x + mons[j].width <= mons[i].x + mons[i].width &&
rects[j].y >= rects[i].y && rects[j].y + rects[j].height <= rects[i].y + rects[i].height) { mons[j].y >= mons[i].y && mons[j].y + mons[j].height <= mons[i].y + mons[i].height) {
rects[j].width = 0; mons[j].width = 0;
valid--; valid--;
} }
} }
@ -1112,13 +1172,13 @@ get_randr_monitors (void)
return; return;
} }
xcb_rectangle_t r[valid]; monitor_t m[valid];
for (i = j = 0; i < num && j < valid; i++) for (i = j = 0; i < num && j < valid; i++)
if (rects[i].width != 0) if (mons[i].width != 0)
r[j++] = rects[i]; m[j++] = mons[i];
monitor_create_chain(r, valid); monitor_create_chain(m, valid);
} }
#ifdef WITH_XINERAMA #ifdef WITH_XINERAMA
@ -1129,26 +1189,32 @@ get_xinerama_monitors (void)
xcb_xinerama_screen_info_iterator_t iter; xcb_xinerama_screen_info_iterator_t iter;
int screens; int screens;
if (num_outputs) {
fprintf(stderr, "Using output names with Xinerama is not yet supported\n");
return;
}
xqs_reply = xcb_xinerama_query_screens_reply(c, xqs_reply = xcb_xinerama_query_screens_reply(c,
xcb_xinerama_query_screens_unchecked(c), NULL); xcb_xinerama_query_screens_unchecked(c), NULL);
iter = xcb_xinerama_query_screens_screen_info_iterator(xqs_reply); iter = xcb_xinerama_query_screens_screen_info_iterator(xqs_reply);
screens = iter.rem; screens = iter.rem;
xcb_rectangle_t rects[screens]; monitor_t mons[screens];
// Fetch all the screens first // Fetch all the screens first
for (int i = 0; iter.rem; i++) { for (int i = 0; iter.rem; i++) {
rects[i].x = iter.data->x_org; mons[i].name = NULL;
rects[i].y = iter.data->y_org; mons[i].x = iter.data->x_org;
rects[i].width = iter.data->width; mons[i].y = iter.data->y_org;
rects[i].height = iter.data->height; mons[i].width = iter.data->width;
mons[i].height = iter.data->height;
xcb_xinerama_screen_info_next(&iter); xcb_xinerama_screen_info_next(&iter);
} }
free(xqs_reply); free(xqs_reply);
monitor_create_chain(rects, screens); monitor_create_chain(mons, screens);
} }
#endif #endif
@ -1223,6 +1289,20 @@ parse_geometry_string (char *str, int *tmp)
return true; return true;
} }
void
parse_output_string(char *str)
{
if (!str || !*str)
return;
num_outputs++;
output_names = realloc(output_names, num_outputs * sizeof(char *));
if (!output_names) {
fprintf(stderr, "failed to allocate output name\n");
exit(EXIT_FAILURE);
}
output_names[num_outputs-1] = str;
}
void void
xconn (void) xconn (void)
{ {
@ -1301,6 +1381,10 @@ init (char *wm_name, char *wm_instance)
#endif #endif
if (!monhead) { if (!monhead) {
if (num_outputs) {
fprintf(stderr, "Failed to find any specified outputs\n");
exit(EXIT_FAILURE);
}
// If I fits I sits // If I fits I sits
if (bw < 0) if (bw < 0)
bw = scr->width_in_pixels - bx; bw = scr->width_in_pixels - bx;
@ -1316,7 +1400,7 @@ init (char *wm_name, char *wm_instance)
} }
// If no RandR outputs or Xinerama screens, fall back to using whole screen // If no RandR outputs or Xinerama screens, fall back to using whole screen
monhead = monitor_new(0, 0, bw, scr->height_in_pixels); monhead = monitor_new(0, 0, bw, scr->height_in_pixels, NULL);
} }
if (!monhead) if (!monhead)
@ -1472,11 +1556,11 @@ main (int argc, char **argv)
// Connect to the Xserver and initialize scr // Connect to the Xserver and initialize scr
xconn(); xconn();
while ((ch = getopt(argc, argv, "hg:bdf:a:pu:B:F:U:n:o:")) != -1) { while ((ch = getopt(argc, argv, "hg:o:bdf:a:pu:B:F:U:n:")) != -1) {
switch (ch) { switch (ch) {
case 'h': case 'h':
printf ("lemonbar version %s patched with XFT support\n", VERSION); printf ("lemonbar version %s\n", VERSION);
printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n" printf ("usage: %s [-h | -o | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n"
"\t-h Show this help\n" "\t-h Show this help\n"
"\t-g Set the bar geometry {width}x{height}+{xoffset}+{yoffset}\n" "\t-g Set the bar geometry {width}x{height}+{xoffset}+{yoffset}\n"
"\t-b Put the bar at the bottom of the screen\n" "\t-b Put the bar at the bottom of the screen\n"
@ -1491,6 +1575,7 @@ main (int argc, char **argv)
"\t-o Add a vertical offset to the text, it can be negative\n", argv[0]); "\t-o Add a vertical offset to the text, it can be negative\n", argv[0]);
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
case 'g': (void)parse_geometry_string(optarg, geom_v); break; case 'g': (void)parse_geometry_string(optarg, geom_v); break;
case 'o': (void)parse_output_string(optarg); break;
case 'p': permanent = true; break; case 'p': permanent = true; break;
case 'n': wm_name = strdup(optarg); break; case 'n': wm_name = strdup(optarg); break;
case 'b': topbar = false; break; case 'b': topbar = false; break;