Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, x11-utils
Provides: x-window-manager
-Suggests: rxvt-unicode | x-terminal-emulator
-Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl
+Recommends: xfonts-base, fonts-dejavu-core, libanyevent-i3-perl (>= 0.12), libjson-xs-perl, rxvt-unicode | x-terminal-emulator
Description: improved dynamic tiling window manager
Key features of i3 are good documentation, reasonable defaults (changeable in
a simple configuration file) and good multi-monitor support. The user
}
--------------------------------------------------------------
+=== Custom separator symbol
+
+Specifies a custom symbol to be used for the separator as opposed to the vertical,
+one pixel thick separator. Note that you may have to adjust the +sep_block_width+
+property.
+
+*Syntax*:
+-------------------------
+separator_symbol <symbol>
+-------------------------
+
+*Example*:
+------------------------
+bar {
+ separator_symbol ":|:"
+}
+------------------------
+
=== Workspace buttons
Specifies whether workspace buttons should be shown or not. This is useful if
*/
static int handle_expose() {
/* re-draw the background */
- xcb_rectangle_t border = {0, 0, 300, (15 * font.height) + 8};
+ xcb_rectangle_t border = {0, 0, logical_px(300), (logical_px(15) * font.height) + logical_px(8)};
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
#define txt(x, row, text) \
draw_text_ascii(text, pixmap, pixmap_gc, \
- x, (row - 1) * font.height + 4, 300 - x * 2)
+ x, (row - 1) * font.height + logical_px(4), logical_px(500) - x * 2)
if (current_step == STEP_WELCOME) {
/* restore font color */
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
- txt(10, 2, "You have not configured i3 yet.");
- txt(10, 3, "Do you want me to generate ~/.i3/config?");
- txt(85, 5, "Yes, generate ~/.i3/config");
- txt(85, 7, "No, I will use the defaults");
+ txt(logical_px(10), 2, "You have not configured i3 yet.");
+ txt(logical_px(10), 3, "Do you want me to generate ~/.i3/config?");
+ txt(logical_px(85), 5, "Yes, generate ~/.i3/config");
+ txt(logical_px(85), 7, "No, I will use the defaults");
/* green */
set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
- txt(25, 5, "<Enter>");
+ txt(logical_px(25), 5, "<Enter>");
/* red */
set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
- txt(31, 7, "<ESC>");
+ txt(logical_px(31), 7, "<ESC>");
}
if (current_step == STEP_GENERATE) {
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
- txt(10, 2, "Please choose either:");
- txt(85, 4, "Win as default modifier");
- txt(85, 5, "Alt as default modifier");
- txt(10, 7, "Afterwards, press");
- txt(85, 9, "to write ~/.i3/config");
- txt(85, 10, "to abort");
+ txt(logical_px(10), 2, "Please choose either:");
+ txt(logical_px(85), 4, "Win as default modifier");
+ txt(logical_px(85), 5, "Alt as default modifier");
+ txt(logical_px(10), 7, "Afterwards, press");
+ txt(logical_px(85), 9, "to write ~/.i3/config");
+ txt(logical_px(85), 10, "to abort");
/* the not-selected modifier */
if (modifier == MOD_Mod4)
- txt(31, 5, "<Alt>");
+ txt(logical_px(31), 5, "<Alt>");
else
- txt(31, 4, "<Win>");
+ txt(logical_px(31), 4, "<Win>");
/* the selected modifier */
set_font(&bold_font);
set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
if (modifier == MOD_Mod4)
- txt(10, 4, "-> <Win>");
+ txt(logical_px(10), 4, "-> <Win>");
else
- txt(10, 5, "-> <Alt>");
+ txt(logical_px(10), 5, "-> <Alt>");
/* green */
set_font(&font);
set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
- txt(25, 9, "<Enter>");
+ txt(logical_px(25), 9, "<Enter>");
/* red */
set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
- txt(31, 10, "<ESC>");
+ txt(logical_px(31), 10, "<ESC>");
}
/* Copy the contents of the pixmap to the real window */
- xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, 500);
+ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), logical_px(500));
xcb_flush(conn);
return 1;
if (current_step != STEP_GENERATE)
return;
- if (event->event_x >= 32 && event->event_x <= 68 &&
- event->event_y >= 45 && event->event_y <= 54) {
+ if (event->event_x >= logical_px(32) && event->event_x <= logical_px(68) &&
+ event->event_y >= logical_px(45) && event->event_y <= logical_px(54)) {
modifier = MOD_Mod4;
handle_expose();
}
- if (event->event_x >= 32 && event->event_x <= 68 &&
- event->event_y >= 56 && event->event_y <= 70) {
+ if (event->event_x >= logical_px(32) && event->event_x <= logical_px(68) &&
+ event->event_y >= logical_px(56) && event->event_y <= logical_px(70)) {
modifier = MOD_Mod1;
handle_expose();
}
int main(int argc, char *argv[]) {
config_path = resolve_tilde("~/.i3/config");
socket_path = getenv("I3SOCK");
- char *pattern = "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1";
- char *patternbold = "-misc-fixed-bold-r-normal--13-120-75-75-C-70-iso10646-1";
+ char *pattern = "pango:monospace 8";
+ char *patternbold = "pango:monospace bold 8";
int o, option_index = 0;
static struct option long_options[] = {
xcb_create_window(
conn,
XCB_COPY_FROM_PARENT,
- win, /* the window id */
- root, /* parent == root */
- 490, 297, 300, 205, /* dimensions */
- 0, /* X11 border = 0, we draw our own */
+ win, /* the window id */
+ root, /* parent == root */
+ logical_px(490), logical_px(297), logical_px(300), logical_px(205), /* dimensions */
+ 0, /* X11 border = 0, we draw our own */
XCB_WINDOW_CLASS_INPUT_OUTPUT,
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
/* Create pixmap */
pixmap = xcb_generate_id(conn);
pixmap_gc = xcb_generate_id(conn);
- xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, 500);
+ xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), logical_px(500));
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
/* Grab the keyboard to get all input */
printf("expose!\n");
/* re-draw the background */
- xcb_rectangle_t border = {0, 0, 500, font.height + 8}, inner = {2, 2, 496, font.height + 8 - 4};
+ xcb_rectangle_t border = {0, 0, logical_px(500), font.height + logical_px(8)},
+ inner = {logical_px(2), logical_px(2), logical_px(496), font.height + logical_px(8) - logical_px(4)};
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#FF0000")});
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
/* draw the prompt … */
if (prompt != NULL) {
- draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492);
+ draw_text(prompt, pixmap, pixmap_gc, logical_px(4), logical_px(4), logical_px(492));
}
/* … and the text */
if (input_position > 0) {
i3String *input = i3string_from_ucs2(glyphs_ucs, input_position);
- draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492);
+ draw_text(input, pixmap, pixmap_gc, prompt_offset + logical_px(4), logical_px(4), logical_px(492));
i3string_free(input);
}
/* Copy the contents of the pixmap to the real window */
- xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, /* */ 500, font.height + 8);
+ xcb_copy_area(conn, pixmap, win, pixmap_gc, 0, 0, 0, 0, logical_px(500), font.height + logical_px(8));
xcb_flush(conn);
return 1;
static int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_t *event) {
printf("Keypress %d, state raw = %d\n", event->detail, event->state);
+ // TODO: port the input handling code from i3lock once libxkbcommon ≥ 0.5.0
+ // is available in distros.
+
/* See the documentation of xcb_key_symbols_get_keysym for this one.
* Basically: We get either col 0 or col 1, depending on whether shift is
* pressed. */
int main(int argc, char *argv[]) {
format = strdup("%s");
socket_path = getenv("I3SOCK");
- char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
+ char *pattern = sstrdup("pango:monospace 8");
int o, option_index = 0;
static struct option long_options[] = {
xcb_create_window(
conn,
XCB_COPY_FROM_PARENT,
- win, /* the window id */
- root, /* parent == root */
- 50, 50, 500, font.height + 8, /* dimensions */
- 0, /* X11 border = 0, we draw our own */
+ win, /* the window id */
+ root, /* parent == root */
+ logical_px(50), logical_px(50), logical_px(500), font.height + logical_px(8), /* dimensions */
+ 0, /* X11 border = 0, we draw our own */
XCB_WINDOW_CLASS_INPUT_OUTPUT,
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
/* Create pixmap */
pixmap = xcb_generate_id(conn);
pixmap_gc = xcb_generate_id(conn);
- xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
+ xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, logical_px(500), font.height + logical_px(8));
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
/* Set input focus (we have override_redirect=1, so the wm will not do
printf("button released on x = %d, y = %d\n",
event->event_x, event->event_y);
/* If the user hits the close button, we exit(0) */
- if (event->event_x >= (rect.width - 32))
+ if (event->event_x >= (rect.width - logical_px(32)))
exit(0);
button_t *button = get_button_at(event->event_x, event->event_y);
if (!button)
/* restore font color */
set_font_colors(pixmap_gc, color_text, color_background);
draw_text(prompt, pixmap, pixmap_gc,
- 4 + 4, 4 + 4, rect.width - 4 - 4);
+ logical_px(4) + logical_px(4),
+ logical_px(4) + logical_px(4),
+ rect.width - logical_px(4) - logical_px(4));
/* render close button */
const char *close_button_label = "X";
- int line_width = 4;
+ int line_width = logical_px(4);
/* set width to the width of the label */
int w = predict_text_width(i3string_from_utf8(close_button_label));
/* account for left/right padding, which seems to be set to 8px (total) below */
- w += 8;
+ w += logical_px(8);
int y = rect.width;
uint32_t values[3];
values[0] = color_button_background;
xcb_point_t points[] = {
{y - w - (2 * line_width), line_width / 2},
{y - (line_width / 2), line_width / 2},
- {y - (line_width / 2), (rect.height - (line_width / 2)) - 2},
- {y - w - (2 * line_width), (rect.height - (line_width / 2)) - 2},
+ {y - (line_width / 2), (rect.height - (line_width / 2)) - logical_px(2)},
+ {y - w - (2 * line_width), (rect.height - (line_width / 2)) - logical_px(2)},
{y - w - (2 * line_width), line_width / 2}};
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points);
values[0] = 1;
set_font_colors(pixmap_gc, color_text, color_button_background);
/* the x term here seems to set left/right padding */
- draw_text_ascii(close_button_label, pixmap, pixmap_gc, y - w - line_width + w / 2 - 4,
- 4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4);
+ draw_text_ascii(close_button_label, pixmap, pixmap_gc,
+ y - w - line_width + w / 2 - logical_px(4),
+ logical_px(4) + logical_px(3),
+ rect.width - y + w + line_width - w / 2 + logical_px(4));
y -= w;
- y -= 20;
+ y -= logical_px(20);
/* render custom buttons */
line_width = 1;
/* set w to the width of the label */
w = predict_text_width(buttons[c].label);
/* account for left/right padding, which seems to be set to 12px (total) below */
- w += 12;
- y -= 30;
+ w += logical_px(12);
+ y -= logical_px(30);
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_button_background});
- close = (xcb_rectangle_t){y - w - (2 * line_width), 2, w + (2 * line_width), rect.height - 6};
+ close = (xcb_rectangle_t){y - w - (2 * line_width), logical_px(2), w + (2 * line_width), rect.height - logical_px(6)};
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &close);
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){color_border});
buttons[c].x = y - w - (2 * line_width);
buttons[c].width = w;
xcb_point_t points2[] = {
- {y - w - (2 * line_width), (line_width / 2) + 2},
- {y - (line_width / 2), (line_width / 2) + 2},
- {y - (line_width / 2), (rect.height - 4 - (line_width / 2))},
- {y - w - (2 * line_width), (rect.height - 4 - (line_width / 2))},
- {y - w - (2 * line_width), (line_width / 2) + 2}};
+ {y - w - (2 * line_width), (line_width / 2) + logical_px(2)},
+ {y - (line_width / 2), (line_width / 2) + logical_px(2)},
+ {y - (line_width / 2), (rect.height - logical_px(4) - (line_width / 2))},
+ {y - w - (2 * line_width), (rect.height - logical_px(4) - (line_width / 2))},
+ {y - w - (2 * line_width), (line_width / 2) + logical_px(2)}};
xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, pixmap, pixmap_gc, 5, points2);
values[0] = color_text;
set_font_colors(pixmap_gc, color_text, color_button_background);
/* the x term seems to set left/right padding */
draw_text(buttons[c].label, pixmap, pixmap_gc,
- y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6);
+ y - w - line_width + logical_px(6),
+ logical_px(4) + logical_px(3),
+ rect.width - y + w + line_width - logical_px(6));
y -= w;
}
/* border line at the bottom */
- line_width = 2;
+ line_width = logical_px(2);
values[0] = color_border_bottom;
values[1] = line_width;
xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND | XCB_GC_LINE_WIDTH, values);
argv0 = argv[0];
- char *pattern = sstrdup("-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1");
+ char *pattern = sstrdup("pango:monospace 8");
int o, option_index = 0;
enum { TYPE_ERROR = 0,
TYPE_WARNING = 1 } bar_type = TYPE_ERROR;
xcb_create_window(
conn,
XCB_COPY_FROM_PARENT,
- win, /* the window id */
- root, /* parent == root */
- 50, 50, 500, font.height + 8 + 8 /* 8 px padding */, /* dimensions */
- 0, /* x11 border = 0, we draw our own */
+ win, /* the window id */
+ root, /* parent == root */
+ 50, 50, 500, font.height + logical_px(8) + logical_px(8) /* 8 px padding */, /* dimensions */
+ 0, /* x11 border = 0, we draw our own */
XCB_WINDOW_CLASS_INPUT_OUTPUT,
XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
} __attribute__((__packed__)) strut_partial;
memset(&strut_partial, 0, sizeof(strut_partial));
- strut_partial.top = font.height + 6;
+ strut_partial.top = font.height + logical_px(6);
strut_partial.top_start_x = 0;
strut_partial.top_end_x = 800;
/* Create pixmap */
pixmap = xcb_generate_id(conn);
pixmap_gc = xcb_generate_id(conn);
- xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + 8);
+ xcb_create_pixmap(conn, root_screen->root_depth, pixmap, win, 500, font.height + logical_px(8));
xcb_create_gc(conn, pixmap_gc, pixmap, 0, 0);
/* Grab the keyboard to get all input */
exec $terminal "$@"
fi
done
+
+i3-nagbar -m 'i3-sensible-terminal could not find a terminal emulator. Please install one.'
* i3bar - an xcb-based status- and ws-bar for i3
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
*
- * child.c: Getting Input for the statusline
+ * child.c: Getting input for the statusline
*
*/
#pragma once
char *bar_id;
char *command;
char *fontname;
+ i3String *separator_symbol;
char *tray_output;
int num_outputs;
char **outputs;
void destroy_connection(void);
/*
- * Sends a Message to i3.
+ * Sends a message to i3.
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
*
*/
} \
} while (0)
-/* Securely fee single-linked list */
+/* Securely free single-linked list */
#define FREE_SLIST(l, type) \
do { \
type *walk = SLIST_FIRST(l); \
} \
} while (0)
-/* Securely fee tail-queues */
+/* Securely free tail queue */
#define FREE_TAILQ(l, type) \
do { \
type *walk = TAILQ_FIRST(l); \
/*
* We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
- * to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
+ * to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
* they assume a light background.
* See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
*
* i3bar - an xcb-based status- and ws-bar for i3
* © 2010-2012 Axel Wagner and contributors (see also: LICENSE)
*
- * child.c: Getting Input for the statusline
+ * child.c: Getting input for the statusline
*
*/
#include <stdlib.h>
return 1;
}
+ if (!strcmp(cur_key, "separator_symbol")) {
+ DLOG("separator = %.*s\n", len, val);
+ I3STRING_FREE(config.separator_symbol);
+ config.separator_symbol = i3string_from_utf8_with_length((const char *)val, len);
+ return 1;
+ }
+
if (!strcmp(cur_key, "outputs")) {
DLOG("+output %.*s\n", len, val);
int new_num_outputs = config.num_outputs + 1;
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
- /* FIXME: Proper errorhandling for JSON-parsing */
+ /* FIXME: Proper error handling for JSON parsing */
switch (state) {
case yajl_status_ok:
break;
*
*/
void got_workspace_reply(char *reply) {
- DLOG("Got Workspace-Data!\n");
+ DLOG("Got workspace data!\n");
parse_workspaces_json(reply);
draw_bars(false);
}
*
*/
void got_subscribe_reply(char *reply) {
- DLOG("Got Subscribe Reply: %s\n", reply);
+ DLOG("Got subscribe reply: %s\n", reply);
/* TODO: Error handling for subscribe commands */
}
*
*/
void got_output_reply(char *reply) {
- DLOG("Parsing Outputs-JSON...\n");
+ DLOG("Parsing outputs JSON...\n");
parse_outputs_json(reply);
- DLOG("Reconfiguring Windows...\n");
+ DLOG("Reconfiguring windows...\n");
realloc_sl_buffer();
reconfig_windows(false);
FREE(config.command);
}
-/* Data-structure to easily call the reply handlers later */
+/* Data structure to easily call the reply handlers later */
handler_t reply_handlers[] = {
&got_command_reply,
&got_workspace_reply,
*
*/
void got_workspace_event(char *event) {
- DLOG("Got Workspace Event!\n");
+ DLOG("Got workspace event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
}
/*
- * Called, when an output event arrives (i.e. the screen-configuration changed)
+ * Called, when an output event arrives (i.e. the screen configuration changed)
*
*/
void got_output_event(char *event) {
- DLOG("Got Output Event!\n");
+ DLOG("Got output event!\n");
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_OUTPUTS, NULL);
if (!config.disable_ws) {
i3_send_msg(I3_IPC_MESSAGE_TYPE_GET_WORKSPACES, NULL);
*
*/
void got_mode_event(char *event) {
- DLOG("Got Mode Event!\n");
+ DLOG("Got mode event!\n");
parse_mode_json(event);
draw_bars(false);
}
draw_bars(false);
}
-/* Data-structure to easily call the event handlers later */
+/* Data structure to easily call the event handlers later */
handler_t event_handlers[] = {
&got_workspace_event,
&got_output_event,
uint32_t header_len = strlen(I3_IPC_MAGIC) + sizeof(uint32_t) * 2;
char *header = smalloc(header_len);
- /* We first parse the fixed-length IPC-header, to know, how much data
+ /* We first parse the fixed-length IPC header, to know, how much data
* we have to expect */
uint32_t rec = 0;
while (rec < header_len) {
}
/*
- * Sends a Message to i3.
+ * Sends a message to i3.
* type must be a valid I3_IPC_MESSAGE_TYPE (see i3/ipc.h for further information)
*
*/
}
if (socket_path == NULL) {
- ELOG("No Socket Path Specified, default to %s\n", i3_default_sock_path);
+ ELOG("No socket path specified, default to %s\n", i3_default_sock_path);
socket_path = expand_path(i3_default_sock_path);
}
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
- /* FIXME: Propper errorhandling for JSON-parsing */
+ /* FIXME: Propper error handling for JSON parsing */
switch (state) {
case yajl_status_ok:
break;
params->workspaces_walk->name_width =
predict_text_width(params->workspaces_walk->name);
- DLOG("Got Workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
+ DLOG("Got workspace canonical: %s, name: '%s', name_width: %d, glyphs: %zu\n",
params->workspaces_walk->canonical_name,
i3string_as_utf8(params->workspaces_walk->name),
params->workspaces_walk->name_width,
state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
- /* FIXME: Propper errorhandling for JSON-parsing */
+ /* FIXME: Propper error handling for JSON parsing */
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
case yajl_status_error:
- ELOG("Could not parse workspaces-reply!\n");
+ ELOG("Could not parse workspaces reply!\n");
exit(EXIT_FAILURE);
break;
}
#include "common.h"
#include "libi3.h"
-/* We save the Atoms in an easy to access array, indexed by an enum */
+/* We save the atoms in an easy to access array, indexed by an enum */
enum {
#define ATOM_DO(name) name,
#include "xcb_atoms.def"
/* Overall height of the bar (based on font size) */
int bar_height;
+/* Cached width of the custom separator if one was set */
+int separator_symbol_width;
+
/* These are only relevant for XKB, which we only need for grabbing modifiers */
int xkb_base;
int mod_pressed = 0;
xcb_pixmap_t statusline_pm;
uint32_t statusline_width;
-/* Event-Watchers, to interact with the user */
+/* Event watchers, to interact with the user */
ev_prepare *xcb_prep;
ev_check *xcb_chk;
ev_io *xcb_io;
return tray_width;
}
+/*
+ * Draws a separator for the given block if necessary.
+ *
+ */
+static void draw_separator(uint32_t x, struct status_block *block) {
+ uint32_t sep_offset = get_sep_offset(block);
+ if (TAILQ_NEXT(block, blocks) == NULL || sep_offset == 0)
+ return;
+
+ uint32_t center_x = x - sep_offset;
+ if (config.separator_symbol == NULL) {
+ /* Draw a classic one pixel, vertical separator. */
+ uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
+ uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
+ xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
+ xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm, statusline_ctx, 2,
+ (xcb_point_t[]){{center_x, logical_px(sep_voff_px)},
+ {center_x, bar_height - logical_px(sep_voff_px)}});
+ } else {
+ /* Draw a custom separator. */
+ uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2);
+ set_font_colors(statusline_ctx, colors.sep_fg, colors.bar_bg);
+ draw_text(config.separator_symbol, statusline_pm, statusline_ctx,
+ separator_x, logical_px(ws_voff_px), x - separator_x);
+ }
+}
+
/*
* Redraws the statusline to the buffer
*
draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, logical_px(ws_voff_px), block->width);
x += block->width + block->sep_block_width + block->x_offset + block->x_append;
- uint32_t sep_offset = get_sep_offset(block);
- if (TAILQ_NEXT(block, blocks) != NULL && sep_offset > 0) {
- /* This is not the last block, draw a separator. */
- uint32_t mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_LINE_WIDTH;
- uint32_t values[] = {colors.sep_fg, colors.bar_bg, logical_px(1)};
- xcb_change_gc(xcb_connection, statusline_ctx, mask, values);
- xcb_poly_line(xcb_connection, XCB_COORD_MODE_ORIGIN, statusline_pm,
- statusline_ctx, 2,
- (xcb_point_t[]){{x - sep_offset, logical_px(sep_voff_px)},
- {x - sep_offset, bar_height - logical_px(sep_voff_px)}});
- }
+ /* If this is not the last block, draw a separator. */
+ draw_separator(x, block);
}
}
redraw_bars();
break;
case XCB_BUTTON_PRESS:
- /* Button press events are mouse-buttons clicked on one of our bars */
+ /* Button press events are mouse buttons clicked on one of our bars */
handle_button((xcb_button_press_event_t *)event);
break;
case XCB_CLIENT_MESSAGE:
}
/*
- * Dummy Callback. We only need this, so that the Prepare- and Check-Watchers
+ * Dummy callback. We only need this, so that the prepare and check watchers
* are triggered
*
*/
*
*/
char *init_xcb_early() {
- /* FIXME: xcb_connect leaks Memory */
+ /* FIXME: xcb_connect leaks memory */
xcb_connection = xcb_connect(NULL, &screen);
if (xcb_connection_has_error(xcb_connection)) {
ELOG("Cannot open display\n");
root_screen->width_in_pixels,
root_screen->height_in_pixels);
- /* The various Watchers to communicate with xcb */
+ /* The various watchers to communicate with xcb */
xcb_io = smalloc(sizeof(ev_io));
xcb_prep = smalloc(sizeof(ev_prepare));
xcb_chk = smalloc(sizeof(ev_check));
DLOG("Calculated font height: %d\n", font.height);
bar_height = font.height + 2 * logical_px(ws_voff_px);
+ if (config.separator_symbol)
+ separator_symbol_width = predict_text_width(config.separator_symbol);
+
xcb_flush(xcb_connection);
if (config.hide_on_modifier == M_HIDE)
/*
* We need to set the _NET_SYSTEM_TRAY_COLORS atom on the tray selection window
- * to make GTK+ 3 applets with Symbolic Icons visible. If the colors are unset,
+ * to make GTK+ 3 applets with symbolic icons visible. If the colors are unset,
* they assume a light background.
* See also https://bugzilla.gnome.org/show_bug.cgi?id=679591
*
}
}
+/* 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) {
+ /* A local struct to save the strut_partial property */
+ struct {
+ uint32_t left;
+ uint32_t right;
+ uint32_t top;
+ uint32_t bottom;
+ uint32_t left_start_y;
+ uint32_t left_end_y;
+ uint32_t right_start_y;
+ uint32_t right_end_y;
+ uint32_t top_start_x;
+ uint32_t top_end_x;
+ uint32_t bottom_start_x;
+ uint32_t bottom_end_x;
+ } __attribute__((__packed__)) strut_partial;
+ memset(&strut_partial, 0, sizeof(strut_partial));
+
+ switch (config.position) {
+ case POS_NONE:
+ break;
+ case POS_TOP:
+ strut_partial.top = bar_height;
+ strut_partial.top_start_x = output->rect.x;
+ strut_partial.top_end_x = output->rect.x + output->rect.w;
+ break;
+ case POS_BOT:
+ strut_partial.bottom = bar_height;
+ strut_partial.bottom_start_x = output->rect.x;
+ strut_partial.bottom_end_x = output->rect.x + output->rect.w;
+ break;
+ }
+ return xcb_change_property(xcb_connection,
+ XCB_PROP_MODE_REPLACE,
+ output->bar,
+ atoms[_NET_WM_STRUT_PARTIAL],
+ XCB_ATOM_CARDINAL,
+ 32,
+ 12,
+ &strut_partial);
+}
+
/*
* Reconfigure all bars and create new bars for recently activated outputs
*
1,
(unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]);
- /* We need to tell i3, where to reserve space for i3bar */
- /* left, right, top, bottom, left_start_y, left_end_y,
- * right_start_y, right_end_y, top_start_x, top_end_x, bottom_start_x,
- * bottom_end_x */
- /* A local struct to save the strut_partial property */
- struct {
- uint32_t left;
- uint32_t right;
- uint32_t top;
- uint32_t bottom;
- uint32_t left_start_y;
- uint32_t left_end_y;
- uint32_t right_start_y;
- uint32_t right_end_y;
- uint32_t top_start_x;
- uint32_t top_end_x;
- uint32_t bottom_start_x;
- uint32_t bottom_end_x;
- } __attribute__((__packed__)) strut_partial;
- memset(&strut_partial, 0, sizeof(strut_partial));
-
- switch (config.position) {
- case POS_NONE:
- break;
- case POS_TOP:
- strut_partial.top = bar_height;
- strut_partial.top_start_x = walk->rect.x;
- strut_partial.top_end_x = walk->rect.x + walk->rect.w;
- break;
- case POS_BOT:
- strut_partial.bottom = bar_height;
- strut_partial.bottom_start_x = walk->rect.x;
- strut_partial.bottom_end_x = walk->rect.x + walk->rect.w;
- break;
- }
- xcb_void_cookie_t strut_cookie = xcb_change_property(xcb_connection,
- XCB_PROP_MODE_REPLACE,
- walk->bar,
- atoms[_NET_WM_STRUT_PARTIAL],
- XCB_ATOM_CARDINAL,
- 32,
- 12,
- &strut_partial);
+ xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
/* We also want a graphics context for the bars (it defines the properties
* with which we draw to them) */
values[3] = bar_height;
values[4] = XCB_STACK_MODE_ABOVE;
+ DLOG("Reconfiguring strut partial property for output %s\n", walk->name);
+ xcb_void_cookie_t strut_cookie = config_strut_partial(walk);
+
DLOG("Destroying buffer for output %s\n", walk->name);
xcb_free_pixmap(xcb_connection, walk->buffer);
if (xcb_request_failed(cfg_cookie, "Could not reconfigure window") ||
xcb_request_failed(chg_cookie, "Could not change window") ||
xcb_request_failed(pm_cookie, "Could not create pixmap") ||
+ xcb_request_failed(strut_cookie, "Could not set strut") ||
(redraw_bars && (xcb_request_failed(umap_cookie, "Could not unmap window") ||
(config.hide_on_modifier == M_DOCK && xcb_request_failed(map_cookie, "Could not map window"))))) {
exit(EXIT_FAILURE);
/** Font specification for all text rendered on the bar. */
char *font;
+ /** A custom separator to use instead of a vertical line. */
+ char *separator_symbol;
+
/** Hide workspace buttons? Configuration option is 'workspace_buttons no'
* but we invert the bool to get the correct default when initializing with
* zero. */
CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *whole_window, const char *command);
CFGFUN(bar_font, const char *font);
+CFGFUN(bar_separator_symbol, const char *separator);
CFGFUN(bar_mode, const char *mode);
CFGFUN(bar_hidden_state, const char *hidden_state);
CFGFUN(bar_id, const char *bar_id);
'output' -> BAR_OUTPUT
'tray_output' -> BAR_TRAY_OUTPUT
'font' -> BAR_FONT
+ 'separator_symbol' -> BAR_SEPARATOR_SYMBOL
'binding_mode_indicator' -> BAR_BINDING_MODE_INDICATOR
'workspace_buttons' -> BAR_WORKSPACE_BUTTONS
'strip_workspace_numbers' -> BAR_STRIP_WORKSPACE_NUMBERS
font = string
-> call cfg_bar_font($font); BAR
+state BAR_SEPARATOR_SYMBOL:
+ separator = string
+ -> call cfg_bar_separator_symbol($separator); BAR
+
state BAR_BINDING_MODE_INDICATOR:
value = word
-> call cfg_bar_binding_mode_indicator($value); BAR
current_bar.font = sstrdup(font);
}
+CFGFUN(bar_separator_symbol, const char *separator) {
+ FREE(current_bar.separator_symbol);
+ current_bar.separator_symbol = sstrdup(separator);
+}
+
CFGFUN(bar_mode, const char *mode) {
current_bar.mode = (strcmp(mode, "dock") == 0 ? M_DOCK : (strcmp(mode, "hide") == 0 ? M_HIDE : M_INVISIBLE));
}
return true;
}
+/*
+ * Handles the _NET_WM_STRUT_PARTIAL property for allocating space for dock clients.
+ *
+ */
+static bool handle_strut_partial_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
+ xcb_atom_t name, xcb_get_property_reply_t *prop) {
+ DLOG("strut partial change for window 0x%08x\n", window);
+
+ Con *con;
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL) {
+ return false;
+ }
+
+ if (prop == NULL) {
+ xcb_generic_error_t *err = NULL;
+ xcb_get_property_cookie_t strut_cookie = xcb_get_property(conn, false, window, A__NET_WM_STRUT_PARTIAL,
+ XCB_GET_PROPERTY_TYPE_ANY, 0, UINT32_MAX);
+ prop = xcb_get_property_reply(conn, strut_cookie, &err);
+
+ if (err != NULL) {
+ DLOG("got error when getting strut partial property: %d\n", err->error_code);
+ free(err);
+ return false;
+ }
+
+ if (prop == NULL) {
+ return false;
+ }
+ }
+
+ DLOG("That is con %p / %s\n", con, con->name);
+
+ window_update_strut_partial(con->window, prop);
+
+ /* we only handle this change for dock clients */
+ if (con->parent == NULL || con->parent->type != CT_DOCKAREA) {
+ return true;
+ }
+
+ Con *search_at = croot;
+ Con *output = con_get_output(con);
+ if (output != NULL) {
+ DLOG("Starting search at output %s\n", output->name);
+ search_at = output;
+ }
+
+ /* find out the desired position of this dock window */
+ if (con->window->reserved.top > 0 && con->window->reserved.bottom == 0) {
+ DLOG("Top dock client\n");
+ con->window->dock = W_DOCK_TOP;
+ } else if (con->window->reserved.top == 0 && con->window->reserved.bottom > 0) {
+ DLOG("Bottom dock client\n");
+ con->window->dock = W_DOCK_BOTTOM;
+ } else {
+ DLOG("Ignoring invalid reserved edges (_NET_WM_STRUT_PARTIAL), using position as fallback:\n");
+ if (con->geometry.y < (int16_t)(search_at->rect.height / 2)) {
+ DLOG("geom->y = %d < rect.height / 2 = %d, it is a top dock client\n",
+ con->geometry.y, (search_at->rect.height / 2));
+ con->window->dock = W_DOCK_TOP;
+ } else {
+ DLOG("geom->y = %d >= rect.height / 2 = %d, it is a bottom dock client\n",
+ con->geometry.y, (search_at->rect.height / 2));
+ con->window->dock = W_DOCK_BOTTOM;
+ }
+ }
+
+ /* find the dockarea */
+ Con *dockarea = con_for_window(search_at, con->window, NULL);
+ assert(dockarea != NULL);
+
+ /* attach the dock to the dock area */
+ con_detach(con);
+ con->parent = dockarea;
+ TAILQ_INSERT_HEAD(&(dockarea->focus_head), con, focused);
+ TAILQ_INSERT_HEAD(&(dockarea->nodes_head), con, nodes);
+
+ tree_render();
+
+ return true;
+}
+
/* Returns false if the event could not be processed (e.g. the window could not
* be found), true otherwise */
typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
{0, UINT_MAX, handle_clientleader_change},
{0, UINT_MAX, handle_transient_for},
{0, 128, handle_windowrole_change},
- {0, 128, handle_class_change}};
+ {0, 128, handle_class_change},
+ {0, UINT_MAX, handle_strut_partial_change}};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
/*
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
property_handlers[6].atom = A_WM_WINDOW_ROLE;
property_handlers[7].atom = XCB_ATOM_WM_CLASS;
+ property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
y(array_open);
Match *match;
TAILQ_FOREACH(match, &(con->swallow_head), matches) {
+ /* We will generate a new restart_mode match specification after this
+ * loop, so skip this one. */
+ if (match->restart_mode)
+ continue;
y(map_open);
if (match->dock != -1) {
ystr("dock");
YSTR_IF_SET(status_command);
YSTR_IF_SET(font);
+ if (config->separator_symbol) {
+ ystr("separator_symbol");
+ ystr(config->separator_symbol);
+ }
+
ystr("workspace_buttons");
y(bool, !config->hide_workspace_buttons);
static void open_placeholder_window(Con *con) {
if (con_is_leaf(con) &&
- (con->window == NULL || con->window->id == XCB_NONE)) {
+ (con->window == NULL || con->window->id == XCB_NONE) &&
+ !TAILQ_EMPTY(&(con->swallow_head)) &&
+ con->type == CT_CON) {
xcb_window_t placeholder = create_window(
restore_conn,
con->rect,
config.client.placeholder.background,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
});
+ /* Make i3 not focus this window. */
+ xcb_icccm_wm_hints_t hints;
+ xcb_icccm_wm_hints_set_none(&hints);
+ xcb_icccm_wm_hints_set_input(&hints, 0);
+ xcb_icccm_set_wm_hints(restore_conn, placeholder, &hints);
/* Set the same name as was stored in the layout file. While perhaps
* slightly confusing in the first instant, this brings additional
* clarity to which placeholder is waiting for which actual window. */
TAILQ_INSERT_HEAD(&(croot->nodes_head), __i3, nodes);
}
+ restore_open_placeholder_windows(croot);
+
return true;
}
@docked = get_dock_clients('top');
is(@docked, 1, 'dock client on top');
+# now change strut_partial to reserve space on the bottom and the dock should
+# be moved to the bottom dock area
+$x->change_property(
+ PROP_MODE_REPLACE,
+ $window->id,
+ $atomname->id,
+ $atomtype->id,
+ 32, # 32 bit integer
+ 12,
+ pack('L12', 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 1280, 0)
+);
+
+sync_with_i3;
+@docked = get_dock_clients('bottom');
+is(@docked, 1, 'dock client on bottom');
+
$window->destroy;
wait_for_unmap $window;
$expected = <<'EOT';
cfg_bar_output(LVDS-1)
-ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
+ERROR: CONFIG: Expected one of these tokens: <end>, '#', 'set', 'i3bar_command', 'status_command', 'socket_path', 'mode', 'hidden_state', 'id', 'modifier', 'wheel_up_cmd', 'wheel_down_cmd', 'position', 'output', 'tray_output', 'font', 'separator_symbol', 'binding_mode_indicator', 'workspace_buttons', 'strip_workspace_numbers', 'verbose', 'colors', '}'
ERROR: CONFIG: (in file <stdin>)
ERROR: CONFIG: Line 1: bar {
ERROR: CONFIG: Line 2: output LVDS-1