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.
=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>
Dock the bar at the bottom of the screen.
@ -143,6 +147,11 @@ First/last 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

View File

@ -41,7 +41,8 @@ typedef struct font_t {
} font_t;
typedef struct monitor_t {
int x, y, width;
char *name;
int x, y, width, height;
xcb_window_t window;
xcb_pixmap_t pixmap;
struct monitor_t *prev, *next;
@ -127,6 +128,8 @@ static XftDraw *xft_draw;
#define MAX_WIDTHS (1 << 16)
static wchar_t xft_char[MAX_WIDTHS];
static char xft_width[MAX_WIDTHS];
static int num_outputs = 0;
static char **output_names = NULL;
void
update_gc (void)
@ -652,6 +655,15 @@ parse (char *text)
{ cur_mon = monhead; }
else if (*p == 'l')
{ 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))
{ cur_mon = monhead;
for (int i = 0; i != *p-'0' && cur_mon->next; i++)
@ -900,7 +912,7 @@ set_ewmh_atoms (void)
}
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;
@ -910,6 +922,7 @@ monitor_new (int x, int y, int width, int height)
exit(EXIT_FAILURE);
}
ret->name = name;
ret->x = x;
ret->y = (topbar ? by : height - bh - by) + y;
ret->width = width;
@ -947,17 +960,17 @@ monitor_add (monitor_t *mon)
}
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 xcb_rectangle_t *r2 = (xcb_rectangle_t *)p2;
const monitor_t *m1 = (monitor_t *)p1;
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;
}
if (r1->x > r2->x || r1->y + r1->height > r2->y)
if (m1->x > m2->x || m1->y + m1->height > m2->y)
{
return 1;
}
@ -966,19 +979,20 @@ rect_sort_cb (const void *p1, const void *p2)
}
void
monitor_create_chain (xcb_rectangle_t *rects, const int num)
monitor_create_chain (monitor_t *mons, const int num)
{
int i;
int width = 0, height = 0;
int left = bx;
// 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++) {
int h = rects[i].y + rects[i].height;
int h = mons[i].y + mons[i].height;
// Accumulated width of all monitors
width += rects[i].width;
width += mons[i].width;
// Get height of screen from y_offset + height of lowest monitor
if (h >= height)
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
width = bw;
for (i = 0; i < num; i++) {
if (rects[i].y + rects[i].height < by)
if (mons[i].y + mons[i].height < by)
continue;
if (rects[i].width > left) {
if (mons[i].width > left) {
monitor_t *mon = monitor_new(
rects[i].x + left,
rects[i].y,
min(width, rects[i].width - left),
rects[i].height);
mons[i].x + left,
mons[i].y,
min(width, mons[i].width - left),
mons[i].height,
mons[i].name);
if (!mon)
break;
monitor_add(mon);
width -= rects[i].width - left;
width -= mons[i].width - left;
// No need to check for other monitors
if (width <= 0)
break;
}
left -= rects[i].width;
left -= mons[i].width;
if (left < 0)
left = 0;
@ -1052,7 +1068,7 @@ get_randr_monitors (void)
return;
}
xcb_rectangle_t rects[num];
monitor_t mons[num];
// Get all outputs
for (i = 0; i < num; i++) {
@ -1064,24 +1080,65 @@ get_randr_monitors (void)
// Output disconnected or not attached to any CRTC ?
if (!oi_reply || oi_reply->crtc == XCB_NONE || oi_reply->connection != XCB_RANDR_CONNECTION_CONNECTED) {
free(oi_reply);
rects[i].width = 0;
mons[i].width = 0;
continue;
}
ci_reply = xcb_randr_get_crtc_info_reply(c,
xcb_randr_get_crtc_info(c, oi_reply->crtc, XCB_CURRENT_TIME), NULL);
free(oi_reply);
if (!ci_reply) {
fprintf(stderr, "Failed to get RandR ctrc info\n");
free(rres_reply);
return;
}
// There's no need to handle rotated screens here (see #69)
rects[i] = (xcb_rectangle_t){ ci_reply->x, ci_reply->y, ci_reply->width, ci_reply->height };
if (num_outputs) {
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);
valid++;
@ -1089,18 +1146,21 @@ get_randr_monitors (void)
free(rres_reply);
if (num_outputs)
free(output_names);
// Check for clones and inactive outputs
for (i = 0; i < num; i++) {
if (rects[i].width == 0)
if (mons[i].width == 0)
continue;
for (j = 0; j < num; j++) {
// Does I contain J ?
if (i != j && rects[j].width) {
if (rects[j].x >= rects[i].x && rects[j].x + rects[j].width <= rects[i].x + rects[i].width &&
rects[j].y >= rects[i].y && rects[j].y + rects[j].height <= rects[i].y + rects[i].height) {
rects[j].width = 0;
if (i != j && mons[j].width && !mons[j].name) {
if (mons[j].x >= mons[i].x && mons[j].x + mons[j].width <= mons[i].x + mons[i].width &&
mons[j].y >= mons[i].y && mons[j].y + mons[j].height <= mons[i].y + mons[i].height) {
mons[j].width = 0;
valid--;
}
}
@ -1112,13 +1172,13 @@ get_randr_monitors (void)
return;
}
xcb_rectangle_t r[valid];
monitor_t m[valid];
for (i = j = 0; i < num && j < valid; i++)
if (rects[i].width != 0)
r[j++] = rects[i];
if (mons[i].width != 0)
m[j++] = mons[i];
monitor_create_chain(r, valid);
monitor_create_chain(m, valid);
}
#ifdef WITH_XINERAMA
@ -1129,26 +1189,32 @@ get_xinerama_monitors (void)
xcb_xinerama_screen_info_iterator_t iter;
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,
xcb_xinerama_query_screens_unchecked(c), NULL);
iter = xcb_xinerama_query_screens_screen_info_iterator(xqs_reply);
screens = iter.rem;
xcb_rectangle_t rects[screens];
monitor_t mons[screens];
// Fetch all the screens first
for (int i = 0; iter.rem; i++) {
rects[i].x = iter.data->x_org;
rects[i].y = iter.data->y_org;
rects[i].width = iter.data->width;
rects[i].height = iter.data->height;
mons[i].name = NULL;
mons[i].x = iter.data->x_org;
mons[i].y = iter.data->y_org;
mons[i].width = iter.data->width;
mons[i].height = iter.data->height;
xcb_xinerama_screen_info_next(&iter);
}
free(xqs_reply);
monitor_create_chain(rects, screens);
monitor_create_chain(mons, screens);
}
#endif
@ -1223,6 +1289,20 @@ parse_geometry_string (char *str, int *tmp)
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
xconn (void)
{
@ -1301,6 +1381,10 @@ init (char *wm_name, char *wm_instance)
#endif
if (!monhead) {
if (num_outputs) {
fprintf(stderr, "Failed to find any specified outputs\n");
exit(EXIT_FAILURE);
}
// If I fits I sits
if (bw < 0)
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
monhead = monitor_new(0, 0, bw, scr->height_in_pixels);
monhead = monitor_new(0, 0, bw, scr->height_in_pixels, NULL);
}
if (!monhead)
@ -1472,11 +1556,11 @@ 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:o:")) != -1) {
while ((ch = getopt(argc, argv, "hg:o:bdf:a:pu:B:F:U:n:")) != -1) {
switch (ch) {
case 'h':
printf ("lemonbar version %s patched with XFT support\n", VERSION);
printf ("usage: %s [-h | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n"
printf ("lemonbar version %s\n", VERSION);
printf ("usage: %s [-h | -o | -g | -b | -d | -f | -a | -p | -n | -u | -B | -F]\n"
"\t-h Show this help\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"
@ -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]);
exit (EXIT_SUCCESS);
case 'g': (void)parse_geometry_string(optarg, geom_v); break;
case 'o': (void)parse_output_string(optarg); break;
case 'p': permanent = true; break;
case 'n': wm_name = strdup(optarg); break;
case 'b': topbar = false; break;