#include <stdbool.h>
#include <unistd.h>
#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <err.h>
#include <stdint.h>
#include <limits.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <glob.h>
#include <xcb/xcb.h>
#include <xcb/xcb_aux.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
+/* We need SYSCONFDIR for the path to the keycode config template, so raise an
+ * error if it’s not defined for whatever reason */
+#ifndef SYSCONFDIR
+#error "SYSCONFDIR not defined"
+#endif
+
#define FREE(pointer) do { \
if (pointer != NULL) { \
free(pointer); \
while (0)
#include "xcb.h"
+#include "ipc.h"
enum { STEP_WELCOME, STEP_GENERATE } current_step = STEP_WELCOME;
enum { MOD_ALT, MOD_SUPER } modifier = MOD_SUPER;
-static char *config_path = "/tmp/wizout/i3.config";
+static char *config_path;
static xcb_connection_t *conn;
static uint32_t font_id;
static uint32_t font_bold_id;
static char *socket_path;
-static int sockfd;
static int font_height;
static int font_bold_height;
static xcb_window_t win;
xcb_window_t root;
Display *dpy;
+char *rewrite_binding(const char *bindingline);
static void finish();
+/*
+ * 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 ? 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;
+}
+
/*
* Try to get the socket path from X11 and return NULL if it doesn’t work.
* As i3-msg is a short-running tool, we don’t bother with cleaning up the
return 1;
}
+/*
+ * Creates the config file and tells i3 to reload.
+ *
+ */
static void finish() {
- printf("finishing the wizard\n");
+ printf("creating \"%s\"...\n", config_path);
-#if 0
- dpy = XOpenDisplay(NULL);
+ if (!(dpy = XOpenDisplay(NULL)))
+ errx(1, "Could not connect to X11");
+
+ FILE *kc_config = fopen(SYSCONFDIR "/i3/config.keycodes", "r");
+ if (kc_config == NULL)
+ err(1, "Could not open input file \"%s\"", SYSCONFDIR "/i3/config.keycodes");
+
+ FILE *ks_config = fopen(config_path, "w");
+ if (ks_config == NULL)
+ err(1, "Could not open output config file \"%s\"", config_path);
- FILE *kc_config = fopen("../i3.config.kc", "r");
char *line = NULL;
size_t len = 0;
ssize_t read;
+ bool head_of_file = true;
+
+ /* write a header about auto-generation to the output file */
+ fputs("# This file has been auto-generated by i3-config-wizard(1).\n", ks_config);
+ fputs("# It will not be overwritten, so edit it as you like.\n", ks_config);
+ fputs("#\n", ks_config);
+ fputs("# Should you change your keyboard layout somewhen, delete\n", ks_config);
+ fputs("# this file and re-run i3-config-wizard(1).\n", ks_config);
+ fputs("#\n", ks_config);
+
while ((read = getline(&line, &len, kc_config)) != -1) {
- /* See if that line is interesting by skipping leading whitespaces,
- * then checking for 'bindcode' */
+ /* skip the warning block at the beginning of the input file */
+ if (head_of_file &&
+ strncmp("# WARNING", line, strlen("# WARNING")) == 0)
+ continue;
+
+ head_of_file = false;
+
+ /* Skip leading whitespace */
char *walk = line;
while (isspace(*walk) && walk < (line + len))
walk++;
- if (strncmp(walk, "bindcode", strlen("bindcode")) != 0)
+
+ /* Set the modifier the user chose */
+ if (strncmp(walk, "set $mod ", strlen("set $mod ")) == 0) {
+ if (modifier == MOD_ALT)
+ fputs("set $mod Mod1\n", ks_config);
+ else fputs("set $mod Mod4\n", ks_config);
+ continue;
+ }
+
+ /* Check for 'bindcode'. If it’s not a bindcode line, we
+ * just copy it to the output file */
+ if (strncmp(walk, "bindcode", strlen("bindcode")) != 0) {
+ fputs(line, ks_config);
continue;
+ }
char *result = rewrite_binding(walk);
- printf("in: %s", walk);
- printf("out: %s", result);
+ fputs(result, ks_config);
free(result);
-
}
+ /* sync to do our best in order to have the file really stored on disk */
+ fflush(ks_config);
+ fsync(fileno(ks_config));
+
free(line);
fclose(kc_config);
+ fclose(ks_config);
- exit(0);
-#endif
+ /* tell i3 to reload the config file */
+ int sockfd = connect_ipc(socket_path);
+ ipc_send_message(sockfd, strlen("reload"), 0, (uint8_t*)"reload");
+ close(sockfd);
exit(0);
}
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";
socket_path = strdup(optarg);
break;
case 'v':
- printf("i3-config-wizard " I3_VERSION);
+ printf("i3-config-wizard " I3_VERSION "\n");
return 0;
case 'h':
- printf("i3-config-wizard " I3_VERSION);
+ printf("i3-config-wizard " I3_VERSION "\n");
printf("i3-config-wizard [-s <socket>] [-v]\n");
return 0;
}
return 0;
}
+ /* Create ~/.i3 if it does not yet exist */
+ char *config_dir = resolve_tilde("~/.i3");
+ if (stat(config_dir, &stbuf) != 0)
+ if (mkdir(config_dir, 0755) == -1)
+ err(1, "mkdir(%s) failed", config_dir);
+ free(config_dir);
+
int fd;
if ((fd = open(config_path, O_CREAT | O_RDWR, 0644)) == -1) {
printf("Cannot open file \"%s\" for writing: %s. Exiting.\n", config_path, strerror(errno));