317 lines
7.8 KiB
C
317 lines
7.8 KiB
C
#include "core/binding.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#include <zdwm/action.h>
|
|
#include <zdwm/types.h>
|
|
|
|
#include "base/array.h"
|
|
#include "base/macros.h"
|
|
#include "base/memory.h"
|
|
#include "core/types.h"
|
|
|
|
typedef struct binding_mode_t {
|
|
zdwm_binding_mode_id_t id;
|
|
const char *name;
|
|
key_binding_t *items;
|
|
size_t count;
|
|
size_t capacity;
|
|
} binding_mode_t;
|
|
|
|
typedef struct binding_table_t {
|
|
binding_mode_t *modes;
|
|
size_t count;
|
|
size_t capacity;
|
|
zdwm_binding_mode_id_t default_mode;
|
|
zdwm_binding_mode_id_t current_mode;
|
|
} binding_table_t;
|
|
|
|
zdwm_binding_mode_id_t
|
|
binding_table_add_mode(binding_table_t *table, const char *mode_name) {
|
|
if (!table || !mode_name || strlen(mode_name) == 0) {
|
|
return ZDWM_BINDING_MODE_ID_INVALID;
|
|
}
|
|
|
|
for (size_t i = 0; i < table->count; ++i) {
|
|
if (strcmp(table->modes[i].name, mode_name) == 0) {
|
|
return ZDWM_BINDING_MODE_ID_INVALID;
|
|
}
|
|
}
|
|
|
|
zdwm_binding_mode_id_t id = (zdwm_binding_mode_id_t)table->count;
|
|
auto new_mode = array_push(table->modes, table->count, table->capacity);
|
|
new_mode->name = mode_name;
|
|
new_mode->id = id;
|
|
|
|
return id;
|
|
}
|
|
|
|
static binding_mode_t *
|
|
binding_table_get_mode(binding_table_t *table, zdwm_binding_mode_id_t mode_id) {
|
|
if (mode_id >= table->count) return nullptr;
|
|
|
|
binding_mode_t *mode = &table->modes[mode_id];
|
|
if (mode->id == mode_id) return mode;
|
|
|
|
for (size_t i = 0; i < table->count; ++i) {
|
|
mode = &table->modes[i];
|
|
if (mode->id == mode_id) return mode;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
typedef struct mod_entry_t {
|
|
const char *name;
|
|
modifier_mask_t mask;
|
|
} mod_entry_t;
|
|
|
|
static const mod_entry_t mod_table[] = {
|
|
{"Shift", ZDWM_MOD_SHIFT},
|
|
{"Control", ZDWM_MOD_CONTROL},
|
|
{"Mod1", ZDWM_MOD_1},
|
|
{"Mod2", ZDWM_MOD_2},
|
|
{"Mod3", ZDWM_MOD_3},
|
|
{"Mod4", ZDWM_MOD_4},
|
|
{"Mod5", ZDWM_MOD_5},
|
|
};
|
|
|
|
static modifier_mask_t lookup_modifier(const char *name, size_t len) {
|
|
for (size_t i = 0; i < countof(mod_table); ++i) {
|
|
if (
|
|
strlen(mod_table[i].name) == len &&
|
|
strncmp(name, mod_table[i].name, len) == 0
|
|
) {
|
|
return mod_table[i].mask;
|
|
}
|
|
}
|
|
return ZDWM_MOD_NONE;
|
|
}
|
|
|
|
/**
|
|
* @brief 解析按键序列
|
|
*
|
|
* @details 按键序列不合法解析失败,解析成功会设置 modifiers 与 keysym 对应的值
|
|
*
|
|
* @notice
|
|
* 1. modifier 仅支持 Mod1 ~ Mod5, Control, Shift
|
|
* 2. 必须保证一个且仅能有一个除 modifier 外的按键
|
|
* 3. 按键序列通过 + 链接
|
|
* 4. + 前后可以有多个空格
|
|
* 5. 两个 + 之间必须有内容,如 "++", " + + "
|
|
* 6. 不能以 + 开头和结尾
|
|
* 7. 按键符号必须能被 xkbcommon 解析,否则按键序列也不合法
|
|
*
|
|
* @param key_sequence 按键序列
|
|
* @param modifiers 返回修饰键的指针
|
|
* @param keysym 返回按键符号的指针
|
|
*
|
|
* @return 解析成功返回 true 否则返回 false
|
|
*/
|
|
static bool parse_key_sequence(
|
|
const char *key_sequence,
|
|
modifier_mask_t *modifiers,
|
|
xkb_keysym_t *keysym
|
|
) {
|
|
if (!key_sequence || !modifiers || !keysym) return false;
|
|
|
|
modifier_mask_t mods = ZDWM_MOD_NONE;
|
|
xkb_keysym_t key = XKB_KEY_NoSymbol;
|
|
|
|
const char *p = key_sequence;
|
|
const char *end = key_sequence + strlen(key_sequence);
|
|
|
|
while (p < end) {
|
|
const char *plus = strchr(p, '+');
|
|
const char *seg_end = plus ? plus : end;
|
|
|
|
const char *s = p;
|
|
while (s < seg_end && isspace((unsigned char)*s)) ++s;
|
|
|
|
const char *e = seg_end;
|
|
while (e > s && isspace((unsigned char)*(e - 1))) --e;
|
|
|
|
if (s == e) return false;
|
|
|
|
if (plus) {
|
|
modifier_mask_t mod = lookup_modifier(s, (size_t)(e - s));
|
|
if (mod == ZDWM_MOD_NONE) return false;
|
|
mods |= mod;
|
|
p = plus + 1;
|
|
} else {
|
|
char key_buf[64];
|
|
size_t key_len = (size_t)(e - s);
|
|
if (key_len >= sizeof(key_buf)) return false;
|
|
memcpy(key_buf, s, key_len);
|
|
key_buf[key_len] = '\0';
|
|
|
|
key = xkb_keysym_from_name(key_buf, XKB_KEYSYM_CASE_INSENSITIVE);
|
|
if (key == XKB_KEY_NoSymbol) {
|
|
return false;
|
|
} else {
|
|
*modifiers = mods;
|
|
*keysym = key;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool binding_table_add_bind(
|
|
binding_table_t *table,
|
|
zdwm_binding_mode_id_t mode_id,
|
|
const char *key_sequence,
|
|
zdwm_action_t action
|
|
) {
|
|
binding_mode_t *mode = binding_table_get_mode(table, mode_id);
|
|
if (!mode) return false;
|
|
|
|
modifier_mask_t modifiers = 0;
|
|
xkb_keysym_t keysym = XKB_KEY_NoSymbol;
|
|
if (!parse_key_sequence(key_sequence, &modifiers, &keysym)) return false;
|
|
|
|
auto item = array_push(mode->items, mode->count, mode->capacity);
|
|
item->modifiers = modifiers;
|
|
item->keysym = keysym;
|
|
item->action = action;
|
|
|
|
return true;
|
|
}
|
|
|
|
void binding_table_destroy(binding_table_t *table) {
|
|
if (!table) return;
|
|
|
|
for (size_t i = 0; i < table->count; ++i) {
|
|
binding_mode_t *mode = &table->modes[i];
|
|
p_delete(&mode->items);
|
|
mode->count = 0;
|
|
mode->capacity = 0;
|
|
}
|
|
|
|
p_delete(&table->modes);
|
|
table->count = 0;
|
|
table->capacity = 0;
|
|
table->default_mode = ZDWM_BINDING_MODE_ID_INVALID;
|
|
|
|
p_delete(&table);
|
|
}
|
|
|
|
binding_table_t *binding_table_create(void) {
|
|
binding_table_t *table = p_new(binding_table_t, 1);
|
|
|
|
/* 默认情况下,默认模式和当前模式都是第一个添加的模式 */
|
|
table->default_mode = 0;
|
|
table->current_mode = 0;
|
|
|
|
return table;
|
|
}
|
|
|
|
bool binding_table_set_default_mode(
|
|
binding_table_t *table,
|
|
zdwm_binding_mode_id_t mode_id
|
|
) {
|
|
if (mode_id >= table->count) return false;
|
|
|
|
table->default_mode = mode_id;
|
|
return true;
|
|
}
|
|
|
|
bool binding_table_set_current_mode(
|
|
binding_table_t *table,
|
|
zdwm_binding_mode_id_t mode_id
|
|
) {
|
|
if (mode_id >= table->count || mode_id == table->current_mode) return false;
|
|
|
|
table->current_mode = mode_id;
|
|
return true;
|
|
}
|
|
|
|
zdwm_binding_mode_id_t
|
|
binding_table_cycle_mode(const binding_table_t *table, int32_t delta) {
|
|
if (!table || !table->modes || table->count <= 1) {
|
|
return ZDWM_BINDING_MODE_ID_INVALID;
|
|
}
|
|
|
|
bool matched = false;
|
|
size_t index = (size_t)table->current_mode;
|
|
if (table->modes[index].id == table->current_mode) {
|
|
matched = true;
|
|
} else {
|
|
for (size_t i = 0; i < table->count; ++i) {
|
|
if (table->modes[i].id == table->current_mode) {
|
|
index = i;
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!matched) return ZDWM_BINDING_MODE_ID_INVALID;
|
|
|
|
int64_t count = (int64_t)table->count;
|
|
int64_t next_index = ((int64_t)index + (int64_t)delta) % count;
|
|
if (next_index < 0) next_index += count;
|
|
|
|
return table->modes[next_index].id;
|
|
}
|
|
|
|
const key_binding_t *
|
|
binding_table_get_current_bindings(binding_table_t *table, size_t *count) {
|
|
auto mode = binding_table_get_mode(table, table->current_mode);
|
|
if (!mode || !mode->count) return nullptr;
|
|
|
|
*count = mode->count;
|
|
return mode->items;
|
|
}
|
|
|
|
size_t binding_table_mode_count(const binding_table_t *table) {
|
|
if (!table) return 0;
|
|
return table->count;
|
|
}
|
|
|
|
zdwm_binding_mode_id_t binding_table_get_default_mode(
|
|
const binding_table_t *table
|
|
) {
|
|
if (!table) return ZDWM_BINDING_MODE_ID_INVALID;
|
|
return table->default_mode;
|
|
}
|
|
|
|
zdwm_binding_mode_id_t binding_table_get_current_mode(
|
|
const binding_table_t *table
|
|
) {
|
|
if (!table) return ZDWM_BINDING_MODE_ID_INVALID;
|
|
return table->current_mode;
|
|
}
|
|
|
|
static const binding_mode_t *binding_table_get_mode_const(
|
|
const binding_table_t *table,
|
|
zdwm_binding_mode_id_t mode_id
|
|
) {
|
|
if (!table || mode_id >= table->count) return nullptr;
|
|
|
|
const binding_mode_t *mode = &table->modes[mode_id];
|
|
if (mode->id == mode_id) return mode;
|
|
|
|
for (size_t i = 0; i < table->count; ++i) {
|
|
mode = &table->modes[i];
|
|
if (mode->id == mode_id) return mode;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const char *binding_table_get_mode_name(
|
|
const binding_table_t *table,
|
|
zdwm_binding_mode_id_t mode_id
|
|
) {
|
|
auto mode = binding_table_get_mode_const(table, mode_id);
|
|
if (!mode) return nullptr;
|
|
return mode->name;
|
|
}
|