From 3fe8e003566079c454cdbcd433c59aa8a3669091 Mon Sep 17 00:00:00 2001 From: nixo Date: Sun, 10 May 2020 00:40:57 +0200 Subject: [PATCH] [meson]: add more programs, [i3]: add bindsym --- i3-config-wizard/atoms.xmacro | 6 - i3-config-wizard/main.c | 993 ---------------------------------- i3-config-wizard/xcb.h | 8 - include/bindings.h | 3 +- libi3/font.c | 3 + libi3/get_config_path.c | 10 +- meson.build | 26 + src/bindings.c | 10 +- src/config.c | 13 +- src/config_parser.c | 410 +------------- src/main.c | 34 ++ 11 files changed, 98 insertions(+), 1418 deletions(-) delete mode 100644 i3-config-wizard/atoms.xmacro delete mode 100644 i3-config-wizard/main.c delete mode 100644 i3-config-wizard/xcb.h diff --git a/i3-config-wizard/atoms.xmacro b/i3-config-wizard/atoms.xmacro deleted file mode 100644 index 8f94ceaa..00000000 --- a/i3-config-wizard/atoms.xmacro +++ /dev/null @@ -1,6 +0,0 @@ -xmacro(_NET_WM_NAME) -xmacro(_NET_WM_WINDOW_TYPE) -xmacro(_NET_WM_WINDOW_TYPE_DIALOG) -xmacro(ATOM) -xmacro(CARDINAL) -xmacro(UTF8_STRING) diff --git a/i3-config-wizard/main.c b/i3-config-wizard/main.c deleted file mode 100644 index 9e07d1f7..00000000 --- a/i3-config-wizard/main.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * i3 - an improved dynamic tiling window manager - * © 2009 Michael Stapelberg and contributors (see also: LICENSE) - * - * i3-config-wizard: Program to convert configs using keycodes to configs using - * keysyms. - * - */ -#include - -#include "libi3.h" - -#if defined(__FreeBSD__) -#include -#endif - -/* For systems without getline, fall back to fgetln */ -#if defined(__APPLE__) -#define USE_FGETLN -#elif defined(__FreeBSD__) -/* Defining this macro before including stdio.h is necessary in order to have - * a prototype for getline in FreeBSD. */ -#define _WITH_GETLINE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define SN_API_NOT_YET_FROZEN 1 -#include - -#include -#include -#include - -/* 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 { \ - free(pointer); \ - pointer = NULL; \ - } while (0) - -#include "xcb.h" -xcb_visualtype_t *visual_type = NULL; - -#define TEXT_PADDING logical_px(4) -#define WIN_POS_X logical_px(490) -#define WIN_POS_Y logical_px(297) -#define WIN_WIDTH logical_px(300) -#define WIN_HEIGHT (15 * font.height + TEXT_PADDING) - -#define col_x(col) \ - (((col)-1) * char_width + TEXT_PADDING) -#define row_y(row) \ - (((row)-1) * font.height + TEXT_PADDING) - -enum { STEP_WELCOME, - STEP_GENERATE } current_step = STEP_WELCOME; -enum { MOD_Mod1, - MOD_Mod4 } modifier = MOD_Mod4; - -static char *config_path; -static uint32_t xcb_numlock_mask; -xcb_connection_t *conn; -static xcb_key_symbols_t *keysyms; -xcb_screen_t *root_screen; -static xcb_get_modifier_mapping_reply_t *modmap_reply; -static i3Font font; -static i3Font bold_font; -static int char_width; -static char *socket_path = NULL; -static xcb_window_t win; -static surface_t surface; -static xcb_key_symbols_t *symbols; -xcb_window_t root; -static struct xkb_keymap *xkb_keymap; -static uint8_t xkb_base_event; -static uint8_t xkb_base_error; - -static void finish(void); - -#include "GENERATED_config_enums.h" - -typedef struct token { - char *name; - char *identifier; - /* This might be __CALL */ - cmdp_state next_state; - union { - uint16_t call_identifier; - } extra; -} cmdp_token; - -typedef struct tokenptr { - cmdp_token *array; - int n; -} cmdp_token_ptr; - -#include "GENERATED_config_tokens.h" - -static cmdp_state state; -/* A list which contains the states that lead to the current state, e.g. - * INITIAL, WORKSPACE_LAYOUT. - * When jumping back to INITIAL, statelist_idx will simply be set to 1 - * (likewise for other states, e.g. MODE or BAR). - * This list is used to process the nearest error token. */ -static cmdp_state statelist[10] = {INITIAL}; -/* NB: statelist_idx points to where the next entry will be inserted */ -static int statelist_idx = 1; - -struct stack_entry { - /* Just a pointer, not dynamically allocated. */ - const char *identifier; - enum { - STACK_STR = 0, - STACK_LONG = 1, - } type; - union { - char *str; - long num; - } val; -}; - -/* 10 entries should be enough for everybody. */ -static struct stack_entry stack[10]; - -/* - * Pushes a string (identified by 'identifier') on the stack. We simply use a - * single array, since the number of entries we have to store is very small. - * - */ -static void push_string(const char *identifier, const char *str) { - for (int c = 0; c < 10; c++) { - if (stack[c].identifier != NULL && - strcmp(stack[c].identifier, identifier) != 0) - continue; - if (stack[c].identifier == NULL) { - /* Found a free slot, let’s store it here. */ - stack[c].identifier = identifier; - stack[c].val.str = sstrdup(str); - stack[c].type = STACK_STR; - } else { - /* Append the value. */ - char *prev = stack[c].val.str; - sasprintf(&(stack[c].val.str), "%s,%s", prev, str); - free(prev); - } - return; - } - - /* When we arrive here, the stack is full. This should not happen and - * means there’s either a bug in this parser or the specification - * contains a command with more than 10 identified tokens. */ - fprintf(stderr, "BUG: commands_parser stack full. This means either a bug " - "in the code, or a new command which contains more than " - "10 identified tokens.\n"); - exit(1); -} - -static void push_long(const char *identifier, long num) { - for (int c = 0; c < 10; c++) { - if (stack[c].identifier != NULL) - continue; - /* Found a free slot, let’s store it here. */ - stack[c].identifier = identifier; - stack[c].val.num = num; - stack[c].type = STACK_LONG; - return; - } - - /* When we arrive here, the stack is full. This should not happen and - * means there’s either a bug in this parser or the specification - * contains a command with more than 10 identified tokens. */ - fprintf(stderr, "BUG: commands_parser stack full. This means either a bug " - "in the code, or a new command which contains more than " - "10 identified tokens.\n"); - exit(1); -} - -static const char *get_string(const char *identifier) { - for (int c = 0; c < 10; c++) { - if (stack[c].identifier == NULL) - break; - if (strcmp(identifier, stack[c].identifier) == 0) - return stack[c].val.str; - } - return NULL; -} - -static void clear_stack(void) { - for (int c = 0; c < 10; c++) { - if (stack[c].type == STACK_STR) - free(stack[c].val.str); - stack[c].identifier = NULL; - stack[c].val.str = NULL; - stack[c].val.num = 0; - } -} - -/* - * Returns true if sym is bound to any key except for 'except_keycode' on the - * first four layers (normal, shift, mode_switch, mode_switch + shift). - * - */ -static bool keysym_used_on_other_key(KeySym sym, xcb_keycode_t except_keycode) { - xcb_keycode_t i, - min_keycode = xcb_get_setup(conn)->min_keycode, - max_keycode = xcb_get_setup(conn)->max_keycode; - - for (i = min_keycode; i && i <= max_keycode; i++) { - if (i == except_keycode) - continue; - for (int level = 0; level < 4; level++) { - if (xcb_key_symbols_get_keysym(keysyms, i, level) != sym) - continue; - return true; - } - } - return false; -} - -static char *next_state(const cmdp_token *token) { - cmdp_state _next_state = token->next_state; - - if (token->next_state == __CALL) { - const char *modifiers = get_string("modifiers"); - int keycode = atoi(get_string("key")); - int level = 0; - if (modifiers != NULL && - strstr(modifiers, "Shift") != NULL) { - /* When shift is included, we really need to use the second-level - * symbol (upper-case). The lower-case symbol could be on a - * different key than the upper-case one (unlikely for letters, but - * more likely for special characters). */ - level = 1; - - /* Try to use the keysym on the first level (lower-case). In case - * this doesn’t make it ambiguous (think of a keyboard layout - * having '1' on two different keys, but '!' only on keycode 10), - * we’ll stick with the keysym of the first level. - * - * This reduces a lot of confusion for users who switch keyboard - * layouts from qwerty to qwertz or other slight variations of - * qwerty (yes, that happens quite often). */ - const xkb_keysym_t *syms; - int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, 0, &syms); - if (num == 0) - errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode); - if (!keysym_used_on_other_key(syms[0], keycode)) - level = 0; - } - - const xkb_keysym_t *syms; - int num = xkb_keymap_key_get_syms_by_level(xkb_keymap, keycode, 0, level, &syms); - if (num == 0) - errx(1, "xkb_keymap_key_get_syms_by_level returned no symbols for keycode %d", keycode); - if (num > 1) - printf("xkb_keymap_key_get_syms_by_level (keycode = %d) returned %d symbolsinstead of 1, using only the first one.\n", keycode, num); - - char str[4096]; - if (xkb_keysym_get_name(syms[0], str, sizeof(str)) == -1) - errx(EXIT_FAILURE, "xkb_keysym_get_name(%u) failed", syms[0]); - const char *release = get_string("release"); - char *res; - char *modrep = (modifiers == NULL ? sstrdup("") : sstrdup(modifiers)); - char *comma; - while ((comma = strchr(modrep, ',')) != NULL) { - *comma = '+'; - } - sasprintf(&res, "bindsym %s%s%s %s%s\n", (modifiers == NULL ? "" : modrep), (modifiers == NULL ? "" : "+"), str, (release == NULL ? "" : release), get_string("command")); - clear_stack(); - free(modrep); - return res; - } - - state = _next_state; - - /* See if we are jumping back to a state in which we were in previously - * (statelist contains INITIAL) and just move statelist_idx accordingly. */ - for (int i = 0; i < statelist_idx; i++) { - if (statelist[i] != _next_state) - continue; - statelist_idx = i + 1; - return NULL; - } - - /* Otherwise, the state is new and we add it to the list */ - statelist[statelist_idx++] = _next_state; - return NULL; -} - -static char *rewrite_binding(const char *input) { - state = INITIAL; - statelist_idx = 1; - - const char *walk = input; - const size_t len = strlen(input); - int c; - const cmdp_token *token; - char *result = NULL; - - /* The "<=" operator is intentional: We also handle the terminating 0-byte - * explicitly by looking for an 'end' token. */ - while ((size_t)(walk - input) <= len) { - /* Skip whitespace before every token, newlines are relevant since they - * separate configuration directives. */ - while ((*walk == ' ' || *walk == '\t') && *walk != '\0') - walk++; - - //printf("remaining input: %s\n", walk); - - cmdp_token_ptr *ptr = &(tokens[state]); - for (c = 0; c < ptr->n; c++) { - token = &(ptr->array[c]); - - /* A literal. */ - if (token->name[0] == '\'') { - if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) { - if (token->identifier != NULL) - push_string(token->identifier, token->name + 1); - walk += strlen(token->name) - 1; - if ((result = next_state(token)) != NULL) - return result; - break; - } - continue; - } - - if (strcmp(token->name, "number") == 0) { - /* Handle numbers. We only accept decimal numbers for now. */ - char *end = NULL; - errno = 0; - long int num = strtol(walk, &end, 10); - if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) || - (errno != 0 && num == 0)) - continue; - - /* No valid numbers found */ - if (end == walk) - continue; - - if (token->identifier != NULL) - push_long(token->identifier, num); - - /* Set walk to the first non-number character */ - walk = end; - if ((result = next_state(token)) != NULL) - return result; - break; - } - - if (strcmp(token->name, "string") == 0 || - strcmp(token->name, "word") == 0) { - const char *beginning = walk; - /* Handle quoted strings (or words). */ - if (*walk == '"') { - beginning++; - walk++; - while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\')) - walk++; - } else { - if (token->name[0] == 's') { - while (*walk != '\0' && *walk != '\r' && *walk != '\n') - walk++; - } else { - /* For a word, the delimiters are white space (' ' or - * '\t'), closing square bracket (]), comma (,) and - * semicolon (;). */ - while (*walk != ' ' && *walk != '\t' && - *walk != ']' && *walk != ',' && - *walk != ';' && *walk != '\r' && - *walk != '\n' && *walk != '\0') - walk++; - } - } - if (walk != beginning) { - char *str = scalloc(walk - beginning + 1, 1); - /* We copy manually to handle escaping of characters. */ - int inpos, outpos; - for (inpos = 0, outpos = 0; - inpos < (walk - beginning); - inpos++, outpos++) { - /* We only handle escaped double quotes to not break - * backwards compatibility with people using \w in - * regular expressions etc. */ - if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"') - inpos++; - str[outpos] = beginning[inpos]; - } - if (token->identifier) - push_string(token->identifier, str); - free(str); - /* If we are at the end of a quoted string, skip the ending - * double quote. */ - if (*walk == '"') - walk++; - if ((result = next_state(token)) != NULL) - return result; - break; - } - } - - if (strcmp(token->name, "end") == 0) { - //printf("checking for end: *%s*\n", walk); - if (*walk == '\0' || *walk == '\n' || *walk == '\r') { - if ((result = next_state(token)) != NULL) - return result; - /* To make sure we start with an appropriate matching - * datastructure for commands which do *not* specify any - * criteria, we re-initialize the criteria system after - * every command. */ - // TODO: make this testable - walk++; - break; - } - } - } - } - - return NULL; -} - -/* - * Having verboselog(), errorlog() and debuglog() is necessary when using libi3. - * - */ -void verboselog(char *fmt, ...) { - va_list args; - - va_start(args, fmt); - vfprintf(stdout, fmt, args); - va_end(args); -} - -void errorlog(char *fmt, ...) { - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - -void debuglog(char *fmt, ...) { -} - -static void txt(int col, int row, char *text, color_t fg, color_t bg) { - int x = col_x(col); - int y = row_y(row); - i3String *string = i3string_from_utf8(text); - draw_util_text(string, &surface, fg, bg, x, y, WIN_WIDTH - x - TEXT_PADDING); - i3string_free(string); -} - -/* - * Handles expose events, that is, draws the window contents. - * - */ -static int handle_expose(void) { - const color_t black = draw_util_hex_to_color("#000000"); - const color_t white = draw_util_hex_to_color("#FFFFFF"); - const color_t green = draw_util_hex_to_color("#00FF00"); - const color_t red = draw_util_hex_to_color("#FF0000"); - - /* draw background */ - draw_util_clear_surface(&surface, black); - - set_font(&font); - - if (current_step == STEP_WELCOME) { - txt(2, 2, "You have not configured i3 yet.", white, black); - txt(2, 3, "Do you want me to generate a config at", white, black); - - char *msg; - sasprintf(&msg, "%s?", config_path); - txt(2, 4, msg, white, black); - free(msg); - - txt(13, 6, "Yes, generate the config", white, black); - txt(13, 8, "No, I will use the defaults", white, black); - - txt(4, 6, "", green, black); - - txt(5, 8, "", red, black); - } - - if (current_step == STEP_GENERATE) { - txt(2, 2, "Please choose either:", white, black); - txt(13, 4, "Win as default modifier", white, black); - txt(13, 5, "Alt as default modifier", white, black); - txt(2, 7, "Afterwards, press", white, black); - txt(13, 9, "to write the config", white, black); - txt(13, 10, "to abort", white, black); - - /* the not-selected modifier */ - if (modifier == MOD_Mod4) - txt(5, 5, "", white, black); - else - txt(5, 4, "", white, black); - - /* the selected modifier */ - set_font(&bold_font); - if (modifier == MOD_Mod4) - txt(2, 4, "-> ", white, black); - else - txt(2, 5, "-> ", white, black); - - set_font(&font); - txt(4, 9, "", green, black); - - txt(5, 10, "", red, black); - } - - 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); - - /* Remove the numlock bit, all other bits are modifiers we can bind to */ - uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); - /* Only use the lower 8 bits of the state (modifier masks) so that mouse - * button masks are filtered out */ - state_filtered &= 0xFF; - - xcb_keysym_t sym = xcb_key_press_lookup_keysym(symbols, event, state_filtered); - - printf("sym = %c (%d)\n", sym, sym); - - if (sym == XK_Return || sym == XK_KP_Enter) { - if (current_step == STEP_WELCOME) { - current_step = STEP_GENERATE; - /* Set window title */ - xcb_change_property(conn, - XCB_PROP_MODE_REPLACE, - win, - A__NET_WM_NAME, - A_UTF8_STRING, - 8, - strlen("i3: generate config"), - "i3: generate config"); - xcb_flush(conn); - } else - 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); - - /* Check if this is Mod1 or Mod4. The modmap contains Shift, Lock, Control, - * Mod1, Mod2, Mod3, Mod4, Mod5 (in that order) */ - xcb_keycode_t *modmap = xcb_get_modifier_mapping_keycodes(modmap_reply); - /* Mod1? */ - int mask = 3; - for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) { - xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i]; - if (code == XCB_NONE) - continue; - printf("Modifier keycode for Mod1: 0x%02x\n", code); - if (code == event->detail) { - modifier = MOD_Mod1; - printf("This is Mod1!\n"); - } - } - - /* Mod4? */ - mask = 6; - for (int i = 0; i < modmap_reply->keycodes_per_modifier; i++) { - xcb_keycode_t code = modmap[(mask * modmap_reply->keycodes_per_modifier) + i]; - if (code == XCB_NONE) - continue; - printf("Modifier keycode for Mod4: 0x%02x\n", code); - if (code == event->detail) { - modifier = MOD_Mod4; - printf("This is Mod4!\n"); - } - } - - handle_expose(); - return 1; -} - -/* - * Handle button presses to make clicking on "" and "" work - * - */ -static void handle_button_press(xcb_button_press_event_t *event) { - if (current_step != STEP_GENERATE) - return; - - if (event->event_x < col_x(5) || event->event_x > col_x(10)) - return; - - if (event->event_y >= row_y(4) && event->event_y <= (row_y(4) + font.height)) { - modifier = MOD_Mod4; - handle_expose(); - } - - if (event->event_y >= row_y(5) && event->event_y <= (row_y(5) + font.height)) { - modifier = MOD_Mod1; - handle_expose(); - } -} - -/* - * Creates the config file and tells i3 to reload. - * - */ -static void finish(void) { - printf("creating \"%s\"...\n", config_path); - - struct xkb_context *xkb_context; - - if ((xkb_context = xkb_context_new(0)) == NULL) - errx(1, "could not create xkbcommon context"); - - int32_t device_id = xkb_x11_get_core_keyboard_device_id(conn); - if ((xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, conn, device_id, 0)) == NULL) - errx(1, "xkb_x11_keymap_new_from_device failed"); - - 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); - free(config_path); - - char *line = NULL; - size_t len = 0; -#ifndef USE_FGETLN - ssize_t read; -#endif - 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 some time, delete\n", ks_config); - fputs("# this file and re-run i3-config-wizard(1).\n", ks_config); - fputs("#\n", ks_config); - -#ifdef USE_FGETLN - char *buf = NULL; - while ((buf = fgetln(kc_config, &len)) != NULL) { - /* fgetln does not return null-terminated strings */ - FREE(line); - sasprintf(&line, "%.*s", len, buf); -#else - size_t linecap = 0; - while ((read = getline(&line, &linecap, kc_config)) != -1) { - len = strlen(line); -#endif - /* 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)) { - /* Pre-output the skipped whitespaces to keep proper indentation */ - fputc(*walk, ks_config); - walk++; - } - - /* Set the modifier the user chose */ - if (strncmp(walk, "set $mod ", strlen("set $mod ")) == 0) { - if (modifier == MOD_Mod1) - 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(walk, ks_config); - continue; - } - char *result = rewrite_binding(walk); - 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)); - -#ifndef USE_FGETLN - free(line); -#endif - - fclose(kc_config); - fclose(ks_config); - - /* tell i3 to reload the config file */ - int sockfd = ipc_connect(socket_path); - ipc_send_message(sockfd, strlen("reload"), 0, (uint8_t *)"reload"); - close(sockfd); - - exit(0); -} - -int main(int argc, char *argv[]) { - char *xdg_config_home; - char *pattern = "pango:monospace 8"; - char *patternbold = "pango:monospace bold 8"; - int o, option_index = 0; - bool headless_run = false; - - static struct option long_options[] = { - {"socket", required_argument, 0, 's'}, - {"version", no_argument, 0, 'v'}, - {"modifier", required_argument, 0, 'm'}, - {"limit", required_argument, 0, 'l'}, - {"prompt", required_argument, 0, 'P'}, - {"prefix", required_argument, 0, 'p'}, - {"font", required_argument, 0, 'f'}, - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0}}; - - char *options_string = "sm:vh"; - - while ((o = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { - switch (o) { - case 's': - FREE(socket_path); - socket_path = sstrdup(optarg); - break; - case 'v': - printf("i3-config-wizard " I3_VERSION "\n"); - return 0; - case 'm': - headless_run = true; - if (strcmp(optarg, "alt") == 0) - modifier = MOD_Mod1; - else if (strcmp(optarg, "win") == 0) - modifier = MOD_Mod4; - else - err(EXIT_FAILURE, "Invalid modifier key %s", optarg); - break; - case 'h': - printf("i3-config-wizard " I3_VERSION "\n"); - printf("i3-config-wizard [-s ] [-m win|alt] [-v] [-h]\n"); - return 0; - } - } - - char *path = get_config_path(NULL, false); - if (path != NULL) { - printf("The config file \"%s\" already exists. Exiting.\n", path); - free(path); - return 0; - } - - /* 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 (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) { - printf("Cannot open file \"%s\" for writing: %s. Exiting.\n", config_path, strerror(errno)); - return 0; - } - close(fd); - unlink(config_path); - - int screen; - if ((conn = xcb_connect(NULL, &screen)) == NULL || - xcb_connection_has_error(conn)) - errx(1, "Cannot open display"); - - if (xkb_x11_setup_xkb_extension(conn, - XKB_X11_MIN_MAJOR_XKB_VERSION, - XKB_X11_MIN_MINOR_XKB_VERSION, - 0, - NULL, - NULL, - &xkb_base_event, - &xkb_base_error) != 1) - errx(EXIT_FAILURE, "Could not setup XKB extension."); - - keysyms = xcb_key_symbols_alloc(conn); - xcb_get_modifier_mapping_cookie_t modmap_cookie; - modmap_cookie = xcb_get_modifier_mapping(conn); - symbols = xcb_key_symbols_alloc(conn); - - if (headless_run) { - finish(); - return 0; - } - -/* Place requests for the atoms we need as soon as possible */ -#define xmacro(atom) \ - xcb_intern_atom_cookie_t atom##_cookie = xcb_intern_atom(conn, 0, strlen(#atom), #atom); -#include "atoms.xmacro" -#undef xmacro - - /* Init startup notification. */ - SnDisplay *sndisplay = sn_xcb_display_new(conn, NULL, NULL); - SnLauncheeContext *sncontext = sn_launchee_context_new_from_environment(sndisplay, screen); - sn_display_unref(sndisplay); - - root_screen = xcb_aux_get_screen(conn, screen); - root = root_screen->root; - - if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL))) - errx(EXIT_FAILURE, "Could not get modifier mapping"); - - xcb_numlock_mask = get_mod_mask_for(XCB_NUM_LOCK, symbols, modmap_reply); - - init_dpi(); - 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 */ - WIN_POS_X, WIN_POS_Y, WIN_WIDTH, WIN_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[]){ - 0, /* back pixel: black */ - XCB_EVENT_MASK_EXPOSURE | - XCB_EVENT_MASK_BUTTON_PRESS}); - if (sncontext) { - sn_launchee_context_setup_window(sncontext, win); - } - - /* Map the window (make it visible) */ - xcb_map_window(conn, win); - -/* Setup NetWM atoms */ -#define xmacro(name) \ - do { \ - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, name##_cookie, NULL); \ - if (!reply) \ - errx(EXIT_FAILURE, "Could not get atom " #name); \ - \ - A_##name = reply->atom; \ - free(reply); \ - } while (0); -#include "atoms.xmacro" -#undef xmacro - - /* Set dock mode */ - xcb_change_property(conn, - XCB_PROP_MODE_REPLACE, - win, - A__NET_WM_WINDOW_TYPE, - A_ATOM, - 32, - 1, - (unsigned char *)&A__NET_WM_WINDOW_TYPE_DIALOG); - - /* Set window title */ - xcb_change_property(conn, - XCB_PROP_MODE_REPLACE, - win, - A__NET_WM_NAME, - A_UTF8_STRING, - 8, - strlen("i3: first configuration"), - "i3: first configuration"); - - /* Initialize drawable surface */ - draw_util_surface_init(conn, &surface, win, get_visualtype(root_screen), WIN_WIDTH, WIN_HEIGHT); - - /* Grab the keyboard to get all input */ - xcb_flush(conn); - - /* Try (repeatedly, if necessary) to grab the keyboard. We might not - * get the keyboard at the first attempt because of the keybinding - * still being active when started via a wm’s keybinding. */ - xcb_grab_keyboard_cookie_t cookie; - xcb_grab_keyboard_reply_t *reply = NULL; - - int count = 0; - while ((reply == NULL || reply->status != XCB_GRAB_STATUS_SUCCESS) && (count++ < 500)) { - cookie = xcb_grab_keyboard(conn, false, win, XCB_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); - reply = xcb_grab_keyboard_reply(conn, cookie, NULL); - usleep(1000); - } - - if (reply->status != XCB_GRAB_STATUS_SUCCESS) { - fprintf(stderr, "Could not grab keyboard, status = %d\n", reply->status); - exit(-1); - } - - /* Startup complete. */ - if (sncontext) { - sn_launchee_context_complete(sncontext); - sn_launchee_context_unref(sncontext); - } - - xcb_flush(conn); - - xcb_generic_event_t *event; - while ((event = xcb_wait_for_event(conn)) != NULL) { - if (event->response_type == 0) { - fprintf(stderr, "X11 Error received! sequence %x\n", event->sequence); - continue; - } - - /* Strip off the highest bit (set if the event is generated) */ - int type = (event->response_type & 0x7F); - - /* TODO: handle mappingnotify */ - switch (type) { - case XCB_KEY_PRESS: - handle_key_press(NULL, conn, (xcb_key_press_event_t *)event); - break; - - case XCB_BUTTON_PRESS: - handle_button_press((xcb_button_press_event_t *)event); - break; - - case XCB_EXPOSE: - if (((xcb_expose_event_t *)event)->count == 0) { - handle_expose(); - } - - break; - } - - free(event); - } - - /* Dismiss drawable surface */ - draw_util_surface_free(conn, &surface); - - return 0; -} diff --git a/i3-config-wizard/xcb.h b/i3-config-wizard/xcb.h deleted file mode 100644 index f3e204e8..00000000 --- a/i3-config-wizard/xcb.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -/* from X11/keysymdef.h */ -#define XCB_NUM_LOCK 0xff7f - -#define xmacro(atom) xcb_atom_t A_##atom; -#include "atoms.xmacro" -#undef xmacro diff --git a/include/bindings.h b/include/bindings.h index df3c32a5..1ceecd0a 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -10,6 +10,7 @@ #pragma once #include +#include extern pid_t command_error_nagbar_pid; @@ -27,7 +28,7 @@ extern const char *DEFAULT_BINDING_MODE; */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *border, const char *whole_window, - const char *exclude_titlebar, const char *command, const char *mode, + const char *exclude_titlebar, SCM command, const char *mode, bool pango_markup); /** diff --git a/libi3/font.c b/libi3/font.c index 5ed58b8d..4d33f7d5 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -495,6 +495,9 @@ static int predict_text_width_xcb(const xcb_char2b_t *input, size_t text_len) { int predict_text_width(i3String *text) { assert(savedFont != NULL); + /* // FIXME: HOW TO SET? */ + /* return predict_text_width_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), */ + /* i3string_is_markup(text)); */ switch (savedFont->type) { case FONT_TYPE_NONE: /* Nothing to do */ diff --git a/libi3/get_config_path.c b/libi3/get_config_path.c index 4909e116..d25d7ce0 100644 --- a/libi3/get_config_path.c +++ b/libi3/get_config_path.c @@ -44,11 +44,11 @@ char *get_config_path(const char *override_configpath, bool use_system_paths) { /* 1: check for $XDG_CONFIG_HOME/i3/config */ if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL) { - xdg_config_home = "~/.config"; + xdg_config_home = "~/.config.scm"; } xdg_config_home = resolve_tilde(xdg_config_home); - sasprintf(&config_path, "%s/i3/config", xdg_config_home); + sasprintf(&config_path, "%s/i3/config.scm", xdg_config_home); free(xdg_config_home); if (path_exists(config_path)) { @@ -57,7 +57,7 @@ char *get_config_path(const char *override_configpath, bool use_system_paths) { free(config_path); /* 2: check the traditional path under the home directory */ - config_path = resolve_tilde("~/.i3/config"); + config_path = resolve_tilde("~/.i3/config.scm"); if (path_exists(config_path)) { return config_path; } @@ -78,7 +78,7 @@ char *get_config_path(const char *override_configpath, bool use_system_paths) { char *tok = strtok(buf, ":"); while (tok != NULL) { tok = resolve_tilde(tok); - sasprintf(&config_path, "%s/i3/config", tok); + sasprintf(&config_path, "%s/i3/config.scm", tok); free(tok); if (path_exists(config_path)) { free(buf); @@ -90,7 +90,7 @@ char *get_config_path(const char *override_configpath, bool use_system_paths) { free(buf); /* 4: check the traditional path under /etc */ - config_path = SYSCONFDIR "/i3/config"; + config_path = SYSCONFDIR "/i3/config.scm"; if (path_exists(config_path)) { return sstrdup(config_path); } diff --git a/meson.build b/meson.build index d542ce0f..6f26e147 100644 --- a/meson.build +++ b/meson.build @@ -173,3 +173,29 @@ executable('i3-nagbar', './i3-nagbar/main.c', include_directories: include_directories('include'), dependencies: [ pango_dep, sn_dep ], link_with: i3lib) + + +executable('i3bar', + [ + 'i3bar/src/child.c', + 'i3bar/src/config.c', + 'i3bar/src/ipc.c', + 'i3bar/src/main.c', + 'i3bar/src/mode.c', + 'i3bar/src/outputs.c', + 'i3bar/src/parse_json_header.c', + 'i3bar/src/workspaces.c', + 'i3bar/src/xcb.c', + ], + include_directories: + [include_directories('i3bar/include'), + include_directories('include'), + ], + dependencies: [ pango_dep, sn_dep ], + link_args: ['-lrt', '-lev'], + link_with: i3lib) + + +executable('i3-msg', 'i3-msg/main.c', + include_directories: include_directories('include'), + dependencies: pango_dep, link_with: i3lib) diff --git a/src/bindings.c b/src/bindings.c index e29f2c2d..516c77d3 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -58,7 +58,7 @@ static struct Mode *mode_from_name(const char *name, bool pango_markup) { */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *border, const char *whole_window, - const char *exclude_titlebar, const char *command, const char *modename, + const char *exclude_titlebar, SCM command, const char *modename, bool pango_markup) { Binding *new_binding = scalloc(1, sizeof(Binding)); DLOG("Binding %p bindtype %s, modifiers %s, input code %s, release %s\n", new_binding, bindtype, modifiers, input_code, release); @@ -83,7 +83,9 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch new_binding->keycode = keycode; new_binding->input_type = B_KEYBOARD; } - new_binding->command = sstrdup(command); + // FIXME: !!!Unprotect!!!! + scm_gc_protect_object(command); + new_binding->command = command; new_binding->event_state_mask = event_state_from_str(modifiers); int group_bits_set = 0; if ((new_binding->event_state_mask >> 16) & I3_XKB_GROUP_MASK_1) @@ -815,6 +817,10 @@ void binding_free(Binding *bind) { * */ CommandResult *run_binding(Binding *bind, Con *con) { + if (scm_procedure_p(bind->command)) { + scm_call_0(bind->command); + } + return NULL; char *command; /* We need to copy the binding and command since “reload” may be part of diff --git a/src/config.c b/src/config.c index 73ef5be5..75802abd 100644 --- a/src/config.c +++ b/src/config.c @@ -12,6 +12,8 @@ #include +#include + char *current_configpath = NULL; char *current_config = NULL; Config config; @@ -227,11 +229,12 @@ bool load_configuration(const char *override_configpath, config_load_t load_type LOG("Parsing configfile %s\n", current_configpath); const bool result = parse_file(current_configpath, load_type != C_VALIDATE); - if (config.font.type == FONT_TYPE_NONE && load_type != C_VALIDATE) { - ELOG("You did not specify required configuration option \"font\"\n"); - config.font = load_font("fixed", true); - set_font(&config.font); - } + config.font = load_font("fixed", true); + set_font(&config.font); + + /* /\* i3Font font = load_font(scm_to_locale_string(scm_variable_ref(scm_c_lookup("font"))), false); *\/ */ + /* i3Font font = load_font("pango:monospace 20", false); */ + /* set_font(&font); */ if (load_type == C_RELOAD) { translate_keysyms(); diff --git a/src/config_parser.c b/src/config_parser.c index f78e75f8..7dea2e6f 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -564,61 +564,6 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context) return &command_output; } -/******************************************************************************* - * Code for building the stand-alone binary test.commands_parser which is used - * by t/187-commands-parser.t. - ******************************************************************************/ - -#ifdef TEST_PARSER - -/* - * Logs the given message to stdout while prefixing the current time to it, - * but only if debug logging was activated. - * This is to be called by DLOG() which includes filename/linenumber - * - */ -void debuglog(char *fmt, ...) { - va_list args; - - va_start(args, fmt); - fprintf(stdout, "# "); - vfprintf(stdout, fmt, args); - va_end(args); -} - -void errorlog(char *fmt, ...) { - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} - -static int criteria_next_state; - -void cfg_criteria_init(I3_CFG, int _state) { - criteria_next_state = _state; -} - -void cfg_criteria_add(I3_CFG, const char *ctype, const char *cvalue) { -} - -void cfg_criteria_pop_state(I3_CFG) { - result->next_state = criteria_next_state; -} - -int main(int argc, char *argv[]) { - if (argc < 2) { - fprintf(stderr, "Syntax: %s \n", argv[0]); - return 1; - } - struct context context; - context.filename = ""; - parse_config(argv[1], &context); -} - -#else - /* * Goes through each line of buf (separated by \n) and checks for statements / * commands which only occur in i3 v4 configuration files. If it finds any, it @@ -685,108 +630,6 @@ static int detect_version(char *buf) { return 3; } -/* - * Calls i3-migrate-config-to-v4 to migrate a configuration file (input - * buffer). - * - * Returns the converted config file or NULL if there was an error (for - * example the script could not be found in $PATH or the i3 executable’s - * directory). - * - */ -static char *migrate_config(char *input, off_t size) { - int writepipe[2]; - int readpipe[2]; - - if (pipe(writepipe) != 0 || - pipe(readpipe) != 0) { - warn("migrate_config: Could not create pipes"); - return NULL; - } - - pid_t pid = fork(); - if (pid == -1) { - warn("Could not fork()"); - return NULL; - } - - /* child */ - if (pid == 0) { - /* close writing end of writepipe, connect reading side to stdin */ - close(writepipe[1]); - dup2(writepipe[0], 0); - - /* close reading end of readpipe, connect writing side to stdout */ - close(readpipe[0]); - dup2(readpipe[1], 1); - - static char *argv[] = { - NULL, /* will be replaced by the executable path */ - NULL}; - exec_i3_utility("i3-migrate-config-to-v4", argv); - } - - /* parent */ - - /* close reading end of the writepipe (connected to the script’s stdin) */ - close(writepipe[0]); - - /* write the whole config file to the pipe, the script will read everything - * immediately */ - if (writeall(writepipe[1], input, size) == -1) { - warn("Could not write to pipe"); - return NULL; - } - close(writepipe[1]); - - /* close writing end of the readpipe (connected to the script’s stdout) */ - close(readpipe[1]); - - /* read the script’s output */ - int conv_size = 65535; - char *converted = scalloc(conv_size, 1); - int read_bytes = 0, ret; - do { - if (read_bytes == conv_size) { - conv_size += 65535; - converted = srealloc(converted, conv_size); - } - ret = read(readpipe[0], converted + read_bytes, conv_size - read_bytes); - if (ret == -1) { - warn("Cannot read from pipe"); - FREE(converted); - return NULL; - } - read_bytes += ret; - } while (ret > 0); - - /* get the returncode */ - int status; - wait(&status); - if (!WIFEXITED(status)) { - fprintf(stderr, "Child did not terminate normally, using old config file (will lead to broken behaviour)\n"); - FREE(converted); - return NULL; - } - - int returncode = WEXITSTATUS(status); - if (returncode != 0) { - fprintf(stderr, "Migration process exit code was != 0\n"); - if (returncode == 2) { - fprintf(stderr, "could not start the migration script\n"); - /* TODO: script was not found. tell the user to fix their system or create a v4 config */ - } else if (returncode == 1) { - fprintf(stderr, "This already was a v4 config. Please add the following line to your config file:\n"); - fprintf(stderr, "# i3 config file (v4)\n"); - /* TODO: nag the user with a message to include a hint for i3 in their config file */ - } - FREE(converted); - return NULL; - } - - return converted; -} - /** * Launch nagbar to indicate errors in the configuration file. */ @@ -883,247 +726,18 @@ static char *get_resource(char *name) { * */ bool parse_file(const char *f, bool use_nagbar) { - struct variables_head variables = SLIST_HEAD_INITIALIZER(&variables); - int fd; - struct stat stbuf; - char *buf; - FILE *fstr; - char buffer[4096], key[512], value[4096], *continuation = NULL; + if (database != NULL) { + xcb_xrm_database_free(database); + /* Explicitly set the database to NULL again in case the config gets reloaded. */ + database = NULL; + } + + // A possibility is to call a function that must be defined inside + // the config + scm_c_primitive_load(f); + extract_workspace_names_from_bindings(); + check_for_duplicate_bindings(context); - if ((fd = open(f, O_RDONLY)) == -1) - die("Could not open configuration file: %s\n", strerror(errno)); - - if (fstat(fd, &stbuf) == -1) - die("Could not fstat file: %s\n", strerror(errno)); - - buf = scalloc(stbuf.st_size + 1, 1); - - if ((fstr = fdopen(fd, "r")) == NULL) - die("Could not fdopen: %s\n", strerror(errno)); - - FREE(current_config); - current_config = scalloc(stbuf.st_size + 1, 1); - if ((ssize_t)fread(current_config, 1, stbuf.st_size, fstr) != stbuf.st_size) { - die("Could not fread: %s\n", strerror(errno)); - } - rewind(fstr); - - bool invalid_sets = false; - - while (!feof(fstr)) { - if (!continuation) - continuation = buffer; - if (fgets(continuation, sizeof(buffer) - (continuation - buffer), fstr) == NULL) { - if (feof(fstr)) - break; - die("Could not read configuration file\n"); - } - if (buffer[strlen(buffer) - 1] != '\n' && !feof(fstr)) { - ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer)); - } - - /* sscanf implicitly strips whitespace. */ - value[0] = '\0'; - const bool skip_line = (sscanf(buffer, "%511s %4095[^\n]", key, value) < 1 || strlen(key) < 3); - const bool comment = (key[0] == '#'); - value[4095] = '\n'; - - continuation = strstr(buffer, "\\\n"); - if (continuation) { - if (!comment) { - continue; - } - DLOG("line continuation in comment is ignored: \"%.*s\"\n", (int)strlen(buffer) - 1, buffer); - continuation = NULL; - } - - strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1); - - /* Skip comments and empty lines. */ - if (skip_line || comment) { - continue; - } - - if (strcasecmp(key, "set") == 0 && *value != '\0') { - char v_key[512]; - char v_value[4096] = {'\0'}; - - if (sscanf(value, "%511s %4095[^\n]", v_key, v_value) < 1) { - ELOG("Failed to parse variable specification '%s', skipping it.\n", value); - invalid_sets = true; - continue; - } - - if (v_key[0] != '$') { - ELOG("Malformed variable assignment, name has to start with $\n"); - invalid_sets = true; - continue; - } - - upsert_variable(&variables, v_key, v_value); - continue; - } else if (strcasecmp(key, "set_from_resource") == 0) { - char res_name[512] = {'\0'}; - char v_key[512]; - char fallback[4096] = {'\0'}; - - /* Ensure that this string is terminated. For example, a user might - * want a variable to be empty if the resource can't be found and - * uses - * set_from_resource $foo i3wm.foo - * Without explicitly terminating the string first, sscanf() will - * leave it uninitialized, causing garbage in the config.*/ - fallback[0] = '\0'; - - if (sscanf(value, "%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) { - ELOG("Failed to parse resource specification '%s', skipping it.\n", value); - invalid_sets = true; - continue; - } - - if (v_key[0] != '$') { - ELOG("Malformed variable assignment, name has to start with $\n"); - invalid_sets = true; - continue; - } - - char *res_value = get_resource(res_name); - if (res_value == NULL) { - DLOG("Could not get resource '%s', using fallback '%s'.\n", res_name, fallback); - res_value = sstrdup(fallback); - } - - upsert_variable(&variables, v_key, res_value); - FREE(res_value); - continue; - } - } - fclose(fstr); - - if (database != NULL) { - xcb_xrm_database_free(database); - /* Explicitly set the database to NULL again in case the config gets reloaded. */ - database = NULL; - } - - /* For every custom variable, see how often it occurs in the file and - * how much extra bytes it requires when replaced. */ - struct Variable *current, *nearest; - int extra_bytes = 0; - /* We need to copy the buffer because we need to invalidate the - * variables (otherwise we will count them twice, which is bad when - * 'extra' is negative) */ - char *bufcopy = sstrdup(buf); - SLIST_FOREACH (current, &variables, variables) { - int extra = (strlen(current->value) - strlen(current->key)); - char *next; - for (next = bufcopy; - next < (bufcopy + stbuf.st_size) && - (next = strcasestr(next, current->key)) != NULL; - next += strlen(current->key)) { - *next = '_'; - extra_bytes += extra; - } - } - FREE(bufcopy); - - /* Then, allocate a new buffer and copy the file over to the new one, - * but replace occurrences of our variables */ - char *walk = buf, *destwalk; - char *new = scalloc(stbuf.st_size + extra_bytes + 1, 1); - destwalk = new; - while (walk < (buf + stbuf.st_size)) { - /* Find the next variable */ - SLIST_FOREACH (current, &variables, variables) { - current->next_match = strcasestr(walk, current->key); - } - nearest = NULL; - int distance = stbuf.st_size; - SLIST_FOREACH (current, &variables, variables) { - if (current->next_match == NULL) - continue; - if ((current->next_match - walk) < distance) { - distance = (current->next_match - walk); - nearest = current; - } - } - if (nearest == NULL) { - /* If there are no more variables, we just copy the rest */ - strncpy(destwalk, walk, (buf + stbuf.st_size) - walk); - destwalk += (buf + stbuf.st_size) - walk; - *destwalk = '\0'; - break; - } else { - /* Copy until the next variable, then copy its value */ - strncpy(destwalk, walk, distance); - strncpy(destwalk + distance, nearest->value, strlen(nearest->value)); - walk += distance + strlen(nearest->key); - destwalk += distance + strlen(nearest->value); - } - } - - /* analyze the string to find out whether this is an old config file (3.x) - * or a new config file (4.x). If it’s old, we run the converter script. */ - int version = detect_version(buf); - if (version == 3) { - /* We need to convert this v3 configuration */ - char *converted = migrate_config(new, strlen(new)); - if (converted != NULL) { - ELOG("\n"); - ELOG("****************************************************************\n"); - ELOG("NOTE: Automatically converted configuration file from v3 to v4.\n"); - ELOG("\n"); - ELOG("Please convert your config file to v4. You can use this command:\n"); - ELOG(" mv %s %s.O\n", f, f); - ELOG(" i3-migrate-config-to-v4 %s.O > %s\n", f, f); - ELOG("****************************************************************\n"); - ELOG("\n"); - free(new); - new = converted; - } else { - LOG("\n"); - LOG("**********************************************************************\n"); - LOG("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4\n"); - LOG("was not correctly installed on your system?\n"); - LOG("**********************************************************************\n"); - LOG("\n"); - } - } - - context = scalloc(1, sizeof(struct context)); - context->filename = f; - - struct ConfigResultIR *config_output = parse_config(new, context); - yajl_gen_free(config_output->json_gen); - - extract_workspace_names_from_bindings(); - check_for_duplicate_bindings(context); - reorder_bindings(); - - if (use_nagbar && (context->has_errors || context->has_warnings || invalid_sets)) { - ELOG("FYI: You are using i3 version %s\n", i3_version); - if (version == 3) - ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n"); - - start_config_error_nagbar(f, context->has_errors || invalid_sets); - } - - bool has_errors = context->has_errors; - - FREE(context->line_copy); - free(context); - free(new); - free(buf); - - while (!SLIST_EMPTY(&variables)) { - current = SLIST_FIRST(&variables); - FREE(current->key); - FREE(current->value); - SLIST_REMOVE_HEAD(&variables, variables); - FREE(current); - } - - return !has_errors; + return true; } -#endif diff --git a/src/main.c b/src/main.c index 4f4c3fd6..d5c0729e 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,8 @@ #include #include +#include + #ifdef I3_ASAN_ENABLED #include #endif @@ -258,7 +260,39 @@ static int parse_restart_fd(void) { return fd; } +void set_defaults(void){ + scm_c_define("font", scm_from_locale_string("fixed")); +} + +SCM guile_binding(SCM g_bindtype, SCM g_modifiers, SCM g_key, SCM g_release, + SCM g_border, SCM g_whole_window, SCM g_exclude_titlebar, + SCM g_command) { + const char * bindtype = scm_to_locale_string (g_bindtype); + const char * modifiers = scm_to_locale_string (g_modifiers); + const char * key = scm_to_locale_string (g_key); + const char * release = scm_to_locale_string (g_release); + const char * border = scm_to_locale_string (g_border); + const char * whole_window = scm_to_locale_string (g_whole_window); + const char * exclude_titlebar = scm_to_locale_string (g_exclude_titlebar); + // procedure + configure_binding(bindtype, modifiers, key, release, border, whole_window, exclude_titlebar, g_command, DEFAULT_BINDING_MODE, false); + return scm_from_bool(true); +} + + +static void* +register_functions (void* data) { + set_defaults(); + scm_c_define_gsubr ("set-binding", 8, 0, 0, &guile_binding); + + return NULL; +} + int main(int argc, char *argv[]) { + /* ELOG("GUILE PRE\n"); */ + scm_with_guile (®ister_functions, NULL); + /* scm_shell (argc, argv); */ + /* ELOG("GUILE POST\n"); */ /* Keep a symbol pointing to the I3_VERSION string constant so that we have * it in gdb backtraces. */ static const char *_i3_version __attribute__((used)) = I3_VERSION;