/* 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;
return 0;
}
-uint32_t get_sep_offset(struct status_block *block) {
+static uint32_t get_sep_offset(struct status_block *block) {
if (!block->no_separator && block->sep_block_width > 0)
return block->sep_block_width / 2 + block->sep_block_width % 2;
return 0;
}
-int get_tray_width(struct tc_head *trayclients) {
+static int get_tray_width(struct tc_head *trayclients) {
trayclient *trayclient;
int tray_width = 0;
TAILQ_FOREACH_REVERSE(trayclient, trayclients, tc_head, tailq) {
}
}
-uint32_t predict_statusline_length(bool use_short_text) {
+static uint32_t predict_statusline_length(bool use_short_text) {
uint32_t width = 0;
struct status_block *block;
/*
* Redraws the statusline to the output's statusline_buffer
*/
-void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
+static void draw_statusline(i3_output *output, uint32_t clip_left, bool use_focus_colors, bool use_short_text) {
struct status_block *block;
color_t bar_color = (use_focus_colors ? colors.focus_bar_bg : colors.bar_bg);
* Hides all bars (unmaps them)
*
*/
-void hide_bars(void) {
+static void hide_bars(void) {
if ((config.hide_on_modifier == M_DOCK) || (config.hidden_state == S_SHOW && config.hide_on_modifier == M_HIDE)) {
return;
}
* Unhides all bars (maps them)
*
*/
-void unhide_bars(void) {
+static void unhide_bars(void) {
if (config.hide_on_modifier != M_HIDE) {
return;
}
* wheel was used and change the workspace appropriately
*
*/
-void handle_button(xcb_button_press_event_t *event) {
+static void handle_button(xcb_button_press_event_t *event) {
/* Determine, which bar was clicked */
i3_output *walk;
xcb_window_t bar = event->event;
if (i3string_get_num_bytes(text) == 0)
continue;
+ const uint32_t full_render_width = render->width + render->x_offset + render->x_append;
const int relative_x = statusline_x - last_block_x;
- if (relative_x >= 0 && (uint32_t)relative_x <= render->width) {
+ if (relative_x >= 0 && (uint32_t)relative_x <= full_render_width) {
send_block_clicked(event->detail, block->name, block->instance,
- event->root_x, event->root_y, relative_x, event->event_y, render->width, bar_height);
+ event->root_x, event->root_y, relative_x, event->event_y, full_render_width, bar_height,
+ event->state);
return;
}
- last_block_x += render->width + render->x_append + render->x_offset + block->sep_block_width;
+ last_block_x += full_render_width + block->sep_block_width;
}
}
}
}
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;
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,
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");
* events from X11, handle them, then flush our outgoing queue.
*
*/
-void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
+static void xcb_prep_cb(struct ev_loop *loop, ev_prepare *watcher, int revents) {
xcb_generic_event_t *event;
if (xcb_connection_has_error(xcb_connection)) {
xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event;
const uint32_t mod = (config.modifier & 0xFFFF);
- mod_pressed = (mod != 0 && (state->mods & mod) == mod);
- if (state->xkbType == XCB_XKB_STATE_NOTIFY && config.modifier != XCB_NONE) {
- if (mod_pressed) {
- activated_mode = false;
- unhide_bars();
- } else if (!activated_mode) {
- hide_bars();
+ const bool new_mod_pressed = (mod != 0 && (state->mods & mod) == mod);
+ if (new_mod_pressed != mod_pressed) {
+ mod_pressed = new_mod_pressed;
+ if (state->xkbType == XCB_XKB_STATE_NOTIFY && config.modifier != XCB_NONE) {
+ if (mod_pressed) {
+ activated_mode = false;
+ unhide_bars();
+ } else if (!activated_mode) {
+ hide_bars();
+ }
}
}
* are triggered
*
*/
-void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
+static void xcb_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
}
/*
* depend on 'config'.
*
*/
-char *init_xcb_early() {
+char *init_xcb_early(void) {
/* FIXME: xcb_connect leaks memory */
xcb_connection = xcb_connect(NULL, &screen);
if (xcb_connection_has_error(xcb_connection)) {
* in xcb.
*
*/
-void register_xkb_keyevents() {
+static void register_xkb_keyevents(void) {
const xcb_query_extension_reply_t *extreply;
extreply = xcb_get_extension_data(conn, &xcb_xkb_id);
if (!extreply->present) {
* Deregister from xkb keyevents.
*
*/
-void deregister_xkb_keyevents() {
+static void deregister_xkb_keyevents(void) {
xcb_xkb_select_events(conn,
XCB_XKB_ID_USE_CORE_KBD,
0,
* atom. Afterwards, tray clients will send ClientMessages to our window.
*
*/
-void init_tray(void) {
+static void init_tray(void) {
DLOG("Initializing system tray functionality\n");
/* request the tray manager atom for the X11 display we are running on */
char atomname[strlen("_NET_SYSTEM_TRAY_S") + 11];
/* Strut partial tells i3 where to reserve space for i3bar. This is determined
* by the `position` bar config directive. */
-xcb_void_cookie_t config_strut_partial(i3_output *output) {
+static xcb_void_cookie_t config_strut_partial(i3_output *output) {
/* A local struct to save the strut_partial property */
struct {
uint32_t left;
&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
*
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) {
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 |
}
}
}
+
+ /* 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;
+ }
}
/*