Close & reopen tray selwin when needed

Fixes #1329
Fixes #3317 (duplicate)
Examples where the tray "disappeared":
- Start without a "tray_output", reload config with "tray_output none",
remove "tray_output none" and reload.
- Start with "tray_output primary" (or "tray_output <output>"), disable
the primary output and then reconnect it.

Fixes #2010
Cached in output_for_tray
next
Orestis Floros 2018-10-11 23:19:21 +03:00
parent ffc7ea8c50
commit e240c118c7
No known key found for this signature in database
GPG Key ID: E9AD9F32E401E38F
1 changed files with 72 additions and 101 deletions

View File

@ -92,6 +92,9 @@ static mode binding;
/* Indicates whether a new binding mode was recently activated */
bool activated_mode = false;
/* The output in which the tray should be displayed. */
static i3_output *output_for_tray;
/* The parsed colors */
struct xcb_colors_t {
color_t bar_fg;
@ -758,58 +761,16 @@ static void handle_client_message(xcb_client_message_event_t *event) {
}
DLOG("X window %08x requested docking\n", client);
i3_output *output = NULL;
i3_output *walk = NULL;
tray_output_t *tray_output = NULL;
/* We need to iterate through the tray_output assignments first in
* order to prioritize them. Otherwise, if this bar manages two
* outputs and both are assigned as tray_output as well, the first
* output in our list would receive the tray rather than the first
* one defined via tray_output. */
TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
if (strcasecmp(walk->name, tray_output->output) == 0) {
DLOG("Found tray_output assignment for output %s.\n", walk->name);
output = walk;
break;
}
if (walk->primary && strcasecmp("primary", tray_output->output) == 0) {
DLOG("Found tray_output assignment on primary output %s.\n", walk->name);
output = walk;
break;
}
}
/* If we found an output, we're done. */
if (output != NULL)
break;
}
/* If no tray_output has been specified, we fall back to the first
* available output. */
if (output == NULL && TAILQ_EMPTY(&(config.tray_outputs))) {
SLIST_FOREACH(walk, outputs, slist) {
if (!walk->active)
continue;
DLOG("Falling back to output %s because no primary output is configured\n", walk->name);
output = walk;
break;
}
}
if (output == NULL) {
ELOG("No output found\n");
if (output_for_tray == NULL) {
ELOG("No output found for tray\n");
return;
}
xcb_void_cookie_t rcookie = xcb_reparent_window(xcb_connection,
client,
output->bar.id,
output->rect.w - icon_size - logical_px(config.tray_padding),
output_for_tray->bar.id,
output_for_tray->rect.w - icon_size - logical_px(config.tray_padding),
logical_px(config.tray_padding));
if (xcb_request_failed(rcookie, "Could not reparent window. Maybe it is using an incorrect depth/visual?"))
return;
@ -836,7 +797,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
ev->format = 32;
ev->data.data32[0] = XCB_CURRENT_TIME;
ev->data.data32[1] = XEMBED_EMBEDDED_NOTIFY;
ev->data.data32[2] = output->bar.id;
ev->data.data32[2] = output_for_tray->bar.id;
ev->data.data32[3] = xe_version;
xcb_send_event(xcb_connection,
0,
@ -856,7 +817,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
tc->win = client;
tc->xe_version = xe_version;
tc->mapped = false;
TAILQ_INSERT_TAIL(output->trayclients, tc, tailq);
TAILQ_INSERT_TAIL(output_for_tray->trayclients, tc, tailq);
if (map_it) {
DLOG("Mapping dock client\n");
@ -1609,6 +1570,56 @@ static xcb_void_cookie_t config_strut_partial(i3_output *output) {
&strut_partial);
}
/*
* Returns the output which should hold the tray, if one exists.
*
* An output is returned in these scenarios:
* 1. A specific output was listed in tray_outputs which is also in the list
* of outputs managed by this bar.
* 2. No tray_output directive was specified. In this case, we use the first
* available output.
* 3. 'tray_output primary' was specified. In this case we use the primary
* output.
*
* Three scenarios in which we specifically don't want to use a tray:
* 1. 'tray_output none' was specified.
* 2. A specific output was listed as a tray_output, but is not one of the
* outputs managed by this bar. For example, consider tray_outputs == [VGA-1],
* but outputs == [HDMI-1].
* 3. 'tray_output primary' was specified and no output in the list is
* primary.
*/
static i3_output *get_tray_output(void) {
i3_output *output = NULL;
if (TAILQ_EMPTY(&(config.tray_outputs))) {
/* No tray_output specified, use first active output. */
SLIST_FOREACH(output, outputs, slist) {
if (output->active) {
return output;
}
}
return NULL;
} else if (strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0) {
/* Check for "tray_output none" */
return NULL;
}
/* If one or more tray_output assignments were specified, we ensure that at
* least one of them is actually an output managed by this instance. */
tray_output_t *tray_output;
TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
SLIST_FOREACH(output, outputs, slist) {
if (output->active &&
(strcasecmp(output->name, tray_output->output) == 0 ||
(strcasecmp(tray_output->output, "primary") == 0 && output->primary))) {
return output;
}
}
}
return NULL;
}
/*
* Reconfigure all bars and create new bars for recently activated outputs
*
@ -1616,7 +1627,6 @@ static xcb_void_cookie_t config_strut_partial(i3_output *output) {
void reconfig_windows(bool redraw_bars) {
uint32_t mask;
uint32_t values[6];
static bool tray_configured = false;
i3_output *walk;
SLIST_FOREACH(walk, outputs, slist) {
@ -1744,58 +1754,6 @@ void reconfig_windows(bool redraw_bars) {
exit(EXIT_FAILURE);
}
/* Unless "tray_output none" was specified, we need to initialize the tray. */
bool no_tray = false;
if (!(TAILQ_EMPTY(&(config.tray_outputs)))) {
no_tray = strcasecmp(TAILQ_FIRST(&(config.tray_outputs))->output, "none") == 0;
}
/*
* There are three scenarios in which we need to initialize the tray:
* 1. A specific output was listed in tray_outputs which is also
* in the list of outputs managed by this bar.
* 2. No tray_output directive was specified. In this case, we
* use the first available output.
* 3. 'tray_output primary' was specified. In this case we use the
* primary output.
*
* Three scenarios in which we specifically don't want to
* initialize the tray are:
* 1. 'tray_output none' was specified.
* 2. A specific output was listed as a tray_output, but is not
* one of the outputs managed by this bar. For example, consider
* tray_outputs == [VGA-1], but outputs == [HDMI-1].
* 3. 'tray_output primary' was specified and no output in the list
* is primary.
*/
if (!tray_configured && !no_tray) {
/* If no tray_output was specified, we go ahead and initialize the tray as
* we will be using the first available output. */
if (TAILQ_EMPTY(&(config.tray_outputs))) {
init_tray();
}
/* If one or more tray_output assignments were specified, we ensure that at least one of
* them is actually an output managed by this instance. */
tray_output_t *tray_output;
TAILQ_FOREACH(tray_output, &(config.tray_outputs), tray_outputs) {
i3_output *output;
bool found = false;
SLIST_FOREACH(output, outputs, slist) {
if (strcasecmp(output->name, tray_output->output) == 0 ||
(strcasecmp(tray_output->output, "primary") == 0 && output->primary)) {
found = true;
init_tray();
break;
}
}
if (found)
break;
}
tray_configured = true;
}
} else {
/* We already have a bar, so we just reconfigure it */
mask = XCB_CONFIG_WINDOW_X |
@ -1889,6 +1847,19 @@ void reconfig_windows(bool redraw_bars) {
}
}
}
/* Finally, check if we want to initialize the tray or destroy the selection
* window. The result of get_tray_output() is cached. */
output_for_tray = get_tray_output();
if (output_for_tray) {
if (selwin == XCB_NONE) {
init_tray();
}
} else if (selwin != XCB_NONE) {
DLOG("Destroying tray selection window\n");
xcb_destroy_window(xcb_connection, selwin);
selwin = XCB_NONE;
}
}
/*