feat: add bindm (modifier-only exclusive keybind)

This commit is contained in:
porl 2026-05-10 15:52:56 +10:00
parent cabafb2393
commit 01a7bf5a8d
2 changed files with 132 additions and 0 deletions

View file

@ -45,6 +45,7 @@ typedef struct {
bool islockapply; bool islockapply;
bool isreleaseapply; bool isreleaseapply;
bool ispassapply; bool ispassapply;
bool ismodonly;
} KeyBinding; } KeyBinding;
typedef struct { typedef struct {
@ -2436,6 +2437,92 @@ bool parse_option(Config *config, char *key, char *value) {
config->key_bindings_count++; config->key_bindings_count++;
} }
} else if (regex_match("^bindm[lp]*$", key)) {
/* bindm: fire on modifier release if no other bind used the hold
* format: bindm[flags]=MODIFIERS,,COMMAND,PARAMS */
config->key_bindings =
realloc(config->key_bindings,
(config->key_bindings_count + 1) * sizeof(KeyBinding));
if (!config->key_bindings) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Failed to allocate "
"memory for key bindings (bindm)\n");
return false;
}
KeyBinding *binding = &config->key_bindings[config->key_bindings_count];
memset(binding, 0, sizeof(KeyBinding));
char mod_str[256], func_name[256],
arg_value[256] = "0\0", arg_value2[256] = "0\0",
arg_value3[256] = "0\0", arg_value4[256] = "0\0",
arg_value5[256] = "0\0";
if (sscanf(value,
"%255[^,],,%255[^,],%255[^,],%255[^,],%255[^,],%255[^\n]",
mod_str, func_name, arg_value, arg_value2,
arg_value3, arg_value4) < 2) {
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Invalid bindm format "
"(expected MODIFIERS,,COMMAND,...): "
"\033[1m\033[31m%s\n",
value);
return false;
}
trim_whitespace(mod_str);
trim_whitespace(func_name);
trim_whitespace(arg_value);
trim_whitespace(arg_value2);
trim_whitespace(arg_value3);
trim_whitespace(arg_value4);
trim_whitespace(arg_value5);
strcpy(binding->mode, config->keymode);
if (strcmp(binding->mode, "common") == 0) {
binding->iscommonmode = true;
binding->isdefaultmode = false;
} else if (strcmp(binding->mode, "default") == 0) {
binding->isdefaultmode = true;
binding->iscommonmode = false;
} else {
binding->isdefaultmode = false;
binding->iscommonmode = false;
}
for (const char *f = key + 5; *f != '\0'; f++) {
if (*f == 'l') binding->islockapply = true;
if (*f == 'p') binding->ispassapply = true;
}
binding->mod = parse_mod(mod_str);
binding->ismodonly = true;
binding->func =
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
arg_value3, arg_value4, arg_value5);
if (!binding->func || binding->mod == UINT32_MAX) {
if (binding->arg.v) {
free(binding->arg.v);
binding->arg.v = NULL;
}
if (binding->arg.v2) {
free(binding->arg.v2);
binding->arg.v2 = NULL;
}
if (binding->arg.v3) {
free(binding->arg.v3);
binding->arg.v3 = NULL;
}
if (!binding->func)
fprintf(stderr,
"\033[1m\033[31m[ERROR]:\033[33m Unknown dispatch in "
"bindm: \033[1m\033[31m%s\n",
func_name);
return false;
} else {
config->key_bindings_count++;
}
} else if (strncmp(key, "mousebind", 9) == 0) { } else if (strncmp(key, "mousebind", 9) == 0) {
config->mouse_bindings = config->mouse_bindings =
realloc(config->mouse_bindings, realloc(config->mouse_bindings,

View file

@ -922,6 +922,10 @@ static struct wl_event_source *hide_cursor_source;
static struct wl_event_source *keep_idle_inhibit_source; static struct wl_event_source *keep_idle_inhibit_source;
static bool cursor_hidden = false; static bool cursor_hidden = false;
static bool tag_combo = false; static bool tag_combo = false;
/* set when a bind fires while modifiers held; cleared on modifier press */
static bool mod_used_for_bind = false;
static const char *cli_config_path = NULL; static const char *cli_config_path = NULL;
static bool cli_debug_log = false; static bool cli_debug_log = false;
static KeyMode keymode = { static KeyMode keymode = {
@ -2332,6 +2336,7 @@ buttonpress(struct wl_listener *listener, void *data) {
event->button == m->button && m->func && event->button == m->button && m->func &&
(CLEANMASK(m->mod) != 0 || (CLEANMASK(m->mod) != 0 ||
(event->button != BTN_LEFT && event->button != BTN_RIGHT))) { (event->button != BTN_LEFT && event->button != BTN_RIGHT))) {
mod_used_for_bind = true;
m->func(&m->arg); m->func(&m->arg);
return; return;
} }
@ -3996,6 +4001,9 @@ keybinding(uint32_t state, bool locked, uint32_t mods, xkb_keysym_t sym,
state != WL_KEYBOARD_KEY_STATE_RELEASED) state != WL_KEYBOARD_KEY_STATE_RELEASED)
continue; continue;
if (config.key_bindings[ji].ismodonly)
continue;
k = &config.key_bindings[ji]; k = &config.key_bindings[ji];
if ((k->iscommonmode || (k->isdefaultmode && keymode.isdefault) || if ((k->iscommonmode || (k->isdefaultmode && keymode.isdefault) ||
(strcmp(keymode.mode, k->mode) == 0)) && (strcmp(keymode.mode, k->mode) == 0)) &&
@ -4014,6 +4022,9 @@ keybinding(uint32_t state, bool locked, uint32_t mods, xkb_keysym_t sym,
else else
handled = 0; handled = 0;
if (!k->ispassapply)
mod_used_for_bind = true;
isbreak = k->func(&k->arg); isbreak = k->func(&k->arg);
if (isbreak) if (isbreak)
@ -4115,6 +4126,15 @@ void keypress(struct wl_listener *listener, void *data) {
wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
bool is_modifier_key =
(keycode == 133 || keycode == 134 ||
keycode == 37 || keycode == 105 ||
keycode == 64 || keycode == 108 ||
keycode == 50 || keycode == 62);
if (is_modifier_key && event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
mod_used_for_bind = false;
// ov tab mode detect moe key release // ov tab mode detect moe key release
if (config.ov_tab_mode && !locked && group == kb_group && if (config.ov_tab_mode && !locked && group == kb_group &&
event->state == WL_KEYBOARD_KEY_STATE_RELEASED && event->state == WL_KEYBOARD_KEY_STATE_RELEASED &&
@ -4132,6 +4152,31 @@ void keypress(struct wl_listener *listener, void *data) {
handled = handled =
keybinding(event->state, locked, mods, syms[i], keycode) || handled; keybinding(event->state, locked, mods, syms[i], keycode) || handled;
/* bindm: fire mod-only binds on release if no combo fired */
if (is_modifier_key && event->state == WL_KEYBOARD_KEY_STATE_RELEASED &&
!mod_used_for_bind) {
int32_t ji;
for (ji = 0; ji < config.key_bindings_count; ji++) {
const KeyBinding *k = &config.key_bindings[ji];
if (!k->ismodonly)
continue;
if (locked && !k->islockapply)
continue;
if (!(k->iscommonmode ||
(k->isdefaultmode && keymode.isdefault) ||
(strcmp(keymode.mode, k->mode) == 0)))
continue;
if (CLEANMASK(mods) != CLEANMASK(k->mod))
continue;
if (!k->func)
continue;
handled = 1;
k->func(&k->arg);
}
}
if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
tag_combo = false; tag_combo = false;
} }