diff --git a/dulcepan.cfg b/dulcepan.cfg index feff07f..f285564 100644 --- a/dulcepan.cfg +++ b/dulcepan.cfg @@ -40,6 +40,7 @@ persistence = true # PNG (zlib) compression level, 0-9 png-compression = 6 -# Key bindings +# Key bindings. Each binding is a comma-separated list of key names; empty names +# are ignored. A binding may be empty. quit-key = Escape -save-key = Space +save-key = Space,Enter diff --git a/src/config.c b/src/config.c index 2957bef..a965ac0 100644 --- a/src/config.c +++ b/src/config.c @@ -15,6 +15,21 @@ static inline void bytes_to_color(uint8_t bytes[static 4], float out[static 4]) } } +static void keybinding_init(struct dp_keybinding *kb, xkb_keysym_t *syms, size_t n_syms) { + *kb = (struct dp_keybinding){ + .syms = dp_zalloc(sizeof(*kb->syms) * n_syms), + .n_syms = n_syms, + }; + for (size_t i = 0; i < n_syms; i++) { + xkb_keysym_t sym = syms[i]; + kb->syms[i] = sym; + } +} + +static void keybinding_finish(struct dp_keybinding *kb) { + free(kb->syms); +} + static void load_color(char *value, int line_idx, float out[static 4]) { size_t len = strlen(value); @@ -78,19 +93,31 @@ static void load_bool(char *value, int line_idx, bool *out) { } } -static void load_key(char *value, int line_idx, xkb_keysym_t *out) { - *out = xkb_keysym_from_name(value, XKB_KEYSYM_CASE_INSENSITIVE); - if (*out == XKB_KEY_NoSymbol) { - dp_log_fatal("Config: unknown key %s on line %d", value, line_idx); +static void load_key(char *value, int line_idx, struct dp_keybinding *out) { + size_t n_syms = 0; + xkb_keysym_t syms[32]; + + char *save_ptr = NULL; + for (char *name; (name = strtok_r(value, ",", &save_ptr)) != NULL; value = NULL) { + if (n_syms == sizeof(syms) / sizeof(*syms)) { + // chill out + dp_log_fatal("Config: too many keys on line %d", line_idx); + } + xkb_keysym_t sym = xkb_keysym_from_name(name, XKB_KEYSYM_CASE_INSENSITIVE); + if (sym == XKB_KEY_NoSymbol) { + dp_log_fatal("Config: unknown key \"%s\" on line %d", name, line_idx); + } + syms[n_syms++] = sym; } + + keybinding_finish(out); + keybinding_init(out, syms, n_syms); } void dp_config_load(struct dp_state *state, const char *user_path) { struct dp_config *config = &state->config; *config = (struct dp_config){ - .quit_key = XKB_KEY_Escape, - .save_key = XKB_KEY_space, .border_size = 2, .border_gradient = DP_BORDER_GRADIENT_NONE, .gradient_angle = 45, @@ -100,11 +127,15 @@ void dp_config_load(struct dp_state *state, const char *user_path) { .quick_select = false, .persistence = true, }; + bytes_to_color((uint8_t[]){0xff, 0xff, 0xff, 0x40}, config->unselected_color); bytes_to_color((uint8_t[]){0x00, 0x00, 0x00, 0x00}, config->selected_color); bytes_to_color((uint8_t[]){0xff, 0xff, 0xff, 0xff}, config->border_color); bytes_to_color((uint8_t[]){0x00, 0x00, 0x00, 0xff}, config->border_secondary_color); + keybinding_init(&config->quit_kb, (xkb_keysym_t[]){XKB_KEY_Escape}, 1); + keybinding_init(&config->save_kb, (xkb_keysym_t[]){XKB_KEY_space, XKB_KEY_Return}, 2); + FILE *fp = NULL; if (user_path != NULL) { fp = fopen(user_path, "r"); @@ -215,9 +246,9 @@ void dp_config_load(struct dp_state *state, const char *user_path) { } else if (strcmp(key, "persistence") == 0) { load_bool(value, line_idx, &config->persistence); } else if (strcmp(key, "quit-key") == 0) { - load_key(value, line_idx, &config->quit_key); + load_key(value, line_idx, &config->quit_kb); } else if (strcmp(key, "save-key") == 0) { - load_key(value, line_idx, &config->save_key); + load_key(value, line_idx, &config->save_kb); } else { dp_log_error("Config: unknown key %s on line %d", key, line_idx); } @@ -226,3 +257,10 @@ void dp_config_load(struct dp_state *state, const char *user_path) { fclose(fp); } + +void dp_config_finish(struct dp_state *state) { + struct dp_config *config = &state->config; + + keybinding_finish(&config->quit_kb); + keybinding_finish(&config->save_kb); +} diff --git a/src/dulcepan.h b/src/dulcepan.h index 007dd9a..05fb755 100644 --- a/src/dulcepan.h +++ b/src/dulcepan.h @@ -141,6 +141,11 @@ enum dp_border_gradient { DP_BORDER_GRADIENT_LOOP, }; +struct dp_keybinding { + xkb_keysym_t *syms; + size_t n_syms; +}; + struct dp_config { // RGBA, not premultiplied float unselected_color[4]; @@ -148,8 +153,8 @@ struct dp_config { float border_color[4]; float border_secondary_color[4]; - xkb_keysym_t quit_key; - xkb_keysym_t save_key; + struct dp_keybinding quit_kb; + struct dp_keybinding save_kb; int border_size; // 0 if disabled enum dp_border_gradient border_gradient; @@ -204,6 +209,7 @@ struct dp_state { }; void dp_config_load(struct dp_state *state, const char *user_path); +void dp_config_finish(struct dp_state *state); // When done, data must be unmapped struct wl_buffer *dp_buffer_create(struct dp_state *state, int32_t width, int32_t height, diff --git a/src/main.c b/src/main.c index 660d9e4..e089ec7 100644 --- a/src/main.c +++ b/src/main.c @@ -278,6 +278,8 @@ int main(int argc, char **argv) { run(&state); + dp_config_finish(&state); + cairo_pattern_destroy(state.border_pattern); xkb_context_unref(state.xkb_context); diff --git a/src/seat.c b/src/seat.c index c30bd10..63b019a 100644 --- a/src/seat.c +++ b/src/seat.c @@ -50,6 +50,15 @@ static void keyboard_handle_leave( // Ignored } +static bool match_keybinding(struct dp_keybinding *kb, uint32_t sym) { + for (size_t i = 0; i < kb->n_syms; i++) { + if (sym == kb->syms[i]) { + return true; + } + } + return false; +} + static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time_msec, uint32_t keycode, enum wl_keyboard_key_state key_state) { struct dp_seat *seat = data; @@ -62,9 +71,9 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uin struct dp_state *state = seat->state; struct dp_config *config = &state->config; - if (keysym == config->quit_key) { + if (match_keybinding(&config->quit_kb, keysym)) { state->status = DP_STATUS_QUIT; - } else if (keysym == config->save_key) { + } else if (match_keybinding(&config->save_kb, keysym)) { state->status = DP_STATUS_SAVED; } }