* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009 Michael Stapelberg and contributors (see also: LICENSE)
*
* i3-config-wizard: Program to convert configs using keycodes to configs using
* keysyms.
#include "xcb.h"
#include "libi3.h"
+#define row_y(row) \
+ (((row)-1) * font.height + logical_px(4))
+#define window_height() \
+ (row_y(15) + font.height)
+
enum { STEP_WELCOME,
STEP_GENERATE } current_step = STEP_WELCOME;
enum { MOD_Mod1,
static xcb_get_modifier_mapping_reply_t *modmap_reply;
static i3Font font;
static i3Font bold_font;
+static int char_width;
static char *socket_path;
static xcb_window_t win;
static xcb_pixmap_t pixmap;
}
}
if (walk != beginning) {
- char *str = scalloc(walk - beginning + 1);
+ char *str = scalloc(walk - beginning + 1, 1);
/* We copy manually to handle escaping of characters. */
int inpos, outpos;
for (inpos = 0, outpos = 0;
void debuglog(char *fmt, ...) {
}
-/*
- * This function resolves ~ in pathnames.
- * It may resolve wildcards in the first part of the path, but if no match
- * or multiple matches are found, it just returns a copy of path as given.
- *
- */
-static char *resolve_tilde(const char *path) {
- static glob_t globbuf;
- char *head, *tail, *result;
-
- tail = strchr(path, '/');
- head = strndup(path, tail ? (size_t)(tail - path) : strlen(path));
-
- int res = glob(head, GLOB_TILDE, NULL, &globbuf);
- free(head);
- /* no match, or many wildcard matches are bad */
- if (res == GLOB_NOMATCH || globbuf.gl_pathc != 1)
- result = strdup(path);
- else if (res != 0) {
- err(1, "glob() failed");
- } else {
- head = globbuf.gl_pathv[0];
- result = calloc(1, strlen(head) + (tail ? strlen(tail) : 0) + 1);
- strncpy(result, head, strlen(head));
- if (tail)
- strncat(result, tail, strlen(tail));
- }
- globfree(&globbuf);
-
- return result;
-}
-
/*
* Handles expose events, that is, draws the window contents.
*
*/
static int handle_expose() {
/* re-draw the background */
- xcb_rectangle_t border = {0, 0, 300, (15 * font.height) + 8};
- xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]) {get_colorpixel("#000000")});
+ xcb_rectangle_t border = {0, 0, logical_px(300), window_height()};
+ xcb_change_gc(conn, pixmap_gc, XCB_GC_FOREGROUND, (uint32_t[]){get_colorpixel("#000000")});
xcb_poly_fill_rectangle(conn, pixmap, pixmap_gc, 1, &border);
set_font(&font);
#define txt(x, row, text) \
draw_text_ascii(text, pixmap, pixmap_gc, \
- x, (row - 1) * font.height + 4, 300 - x * 2)
+ x, row_y(row), logical_px(500) - x * 2)
if (current_step == STEP_WELCOME) {
/* restore font color */
- set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#000000"));
+
+ txt(logical_px(10), 2, "You have not configured i3 yet.");
+ txt(logical_px(10), 3, "Do you want me to generate a config at");
- 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");
+ char *msg;
+ sasprintf(&msg, "%s?", config_path);
+ txt(logical_px(10), 4, msg);
+ free(msg);
+
+ txt(logical_px(85), 6, "Yes, generate the config");
+ txt(logical_px(85), 8, "No, I will use the defaults");
/* green */
- set_font_colors(pixmap_gc, get_colorpixel("#00FF00"), get_colorpixel("#000000"));
- txt(25, 5, "<Enter>");
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#00FF00"), draw_util_hex_to_color("#000000"));
+ txt(logical_px(25), 6, "<Enter>");
/* red */
- set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
- txt(31, 7, "<ESC>");
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#FF0000"), draw_util_hex_to_color("#000000"));
+ txt(logical_px(31), 8, "<ESC>");
}
if (current_step == STEP_GENERATE) {
- set_font_colors(pixmap_gc, get_colorpixel("#FFFFFF"), get_colorpixel("#000000"));
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#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 the 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"));
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#FFFFFF"), draw_util_hex_to_color("#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>");
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#00FF00"), draw_util_hex_to_color("#000000"));
+ txt(logical_px(25), 9, "<Enter>");
/* red */
- set_font_colors(pixmap_gc, get_colorpixel("#FF0000"), get_colorpixel("#000000"));
- txt(31, 10, "<ESC>");
+ set_font_colors(pixmap_gc, draw_util_hex_to_color("#FF0000"), draw_util_hex_to_color("#000000"));
+ 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;
finish();
}
+ /* Swap between modifiers when up or down is pressed. */
+ if (sym == XK_Up || sym == XK_Down) {
+ modifier = (modifier == MOD_Mod1) ? MOD_Mod4 : MOD_Mod1;
+ handle_expose();
+ }
+
/* cancel any time */
if (sym == XK_Escape)
exit(0);
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(32) + char_width * 5))
+ return;
+
+ if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) {
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_y >= row_y(5) && event->event_y <= (row_y(5) + font.height)) {
modifier = MOD_Mod1;
handle_expose();
}
}
int main(int argc, char *argv[]) {
- config_path = resolve_tilde("~/.i3/config");
+ char *xdg_config_home;
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[] = {
switch (o) {
case 's':
FREE(socket_path);
- socket_path = strdup(optarg);
+ socket_path = sstrdup(optarg);
break;
case 'v':
printf("i3-config-wizard " I3_VERSION "\n");
}
}
- /* Check if the destination config file does not exist but the path is
- * writable. If not, exit now, this program is not useful in that case. */
- struct stat stbuf;
- if (stat(config_path, &stbuf) == 0) {
- printf("The config file \"%s\" already exists. Exiting.\n", config_path);
+ char *path = get_config_path(NULL, false);
+ if (path != NULL) {
+ printf("The config file \"%s\" already exists. Exiting.\n", path);
+ free(path);
return 0;
}
- /* Create ~/.i3 if it does not yet exist */
- char *config_dir = resolve_tilde("~/.i3");
+ /* Always write to $XDG_CONFIG_HOME/i3/config by default. */
+ if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
+ xdg_config_home = "~/.config";
+
+ xdg_config_home = resolve_tilde(xdg_config_home);
+ sasprintf(&config_path, "%s/i3/config", xdg_config_home);
+
+ /* Create $XDG_CONFIG_HOME/i3 if it does not yet exist */
+ char *config_dir;
+ struct stat stbuf;
+ sasprintf(&config_dir, "%s/i3", xdg_config_home);
if (stat(config_dir, &stbuf) != 0)
- if (mkdir(config_dir, 0755) == -1)
- err(1, "mkdir(%s) failed", config_dir);
+ if (mkdirp(config_dir, DEFAULT_DIR_MODE) != 0)
+ err(EXIT_FAILURE, "mkdirp(%s) failed", config_dir);
free(config_dir);
+ free(xdg_config_home);
int fd;
if ((fd = open(config_path, O_CREAT | O_RDWR, 0644)) == -1) {
font = load_font(pattern, true);
bold_font = load_font(patternbold, true);
+ /* Determine character width in the default font. */
+ set_font(&font);
+ char_width = predict_text_width(i3string_from_utf8("a"));
+
/* Open an input window */
win = xcb_generate_id(conn);
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), window_height(), /* 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,
- (uint32_t[]) {
+ (uint32_t[]){
0, /* back pixel: black */
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_BUTTON_PRESS});
/* 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 */