Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/config/parse_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

enum { NUM_TYPE_MINUS, NUM_TYPE_PLUS, NUM_TYPE_DEFAULT };

enum { KEY_TYPE_CODE, KEY_TYPE_SYM };
enum { KEY_TYPE_CODE, KEY_TYPE_SYM, KEY_TYPE_NONE };

typedef struct {
uint32_t keycode1;
Expand Down Expand Up @@ -768,6 +768,22 @@ void create_config_keymap(void) {
KeySymCode parse_key(const char *key_str, bool isbindsym) {
KeySymCode kc = {0}; // 初始化为0

// Handle "none" key for modifier-only bindings
{
char lower_key[10];
int32_t ki = 0;
while (key_str[ki] && ki < 9) {
lower_key[ki] = tolower(key_str[ki]);
ki++;
}
lower_key[ki] = '\0';
if (strcmp(lower_key, "none") == 0) {
kc.type = KEY_TYPE_NONE;
kc.keysym = XKB_KEY_NoSymbol;
return kc;
}
}

if (config.keymap == NULL || config.ctx == NULL) {
// 处理错误
kc.type = KEY_TYPE_SYM;
Expand Down Expand Up @@ -2315,6 +2331,18 @@ bool parse_option(Config *config, char *key, char *value) {
binding->func =
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
arg_value3, arg_value4, arg_value5);

/* KEY_TYPE_NONE bindings must use the release flag (bindr) */
if (binding->keysymcode.type == KEY_TYPE_NONE) {
if (!binding->isreleaseapply) {
fprintf(stderr,
"\033[1m\033[33m[WARN]:\033[0m "
"\"none\" key only works with bindr "
"(release bind), forcing release mode\n");
binding->isreleaseapply = true;
}
}

if (!binding->func || binding->mod == UINT32_MAX ||
(binding->keysymcode.type == KEY_TYPE_SYM &&
binding->keysymcode.keysym == XKB_KEY_NoSymbol)) {
Expand Down
60 changes: 60 additions & 0 deletions src/mango.c
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,8 @@ struct dvec2 *baked_points_opafadeout;
static struct wl_event_source *hide_source;
static bool cursor_hidden = false;
static bool tag_combo = false;
static bool mod_key_used = false;
static uint32_t prev_mods = 0;
static const char *cli_config_path = NULL;
static KeyMode keymode = {
.mode = {'d', 'e', 'f', 'a', 'u', 'l', 't', '\0'},
Expand Down Expand Up @@ -1989,6 +1991,9 @@ buttonpress(struct wl_listener *listener, void *data) {
if (locked)
break;

/* Invalidate modifier-only bindings on mouse click */
mod_key_used = true;

xytonode(cursor->x, cursor->y, &surface, NULL, NULL, NULL, NULL);
if (toplevel_from_wlr_surface(surface, &c, &l) >= 0) {
if (c && c->scene->node.enabled &&
Expand Down Expand Up @@ -3596,6 +3601,10 @@ keybinding(uint32_t state, bool locked, uint32_t mods, xkb_keysym_t sym,
state != WL_KEYBOARD_KEY_STATE_RELEASED)
continue;

/* Skip KEY_TYPE_NONE bindings here; they are handled in keypressmod */
if (config.key_bindings[ji].keysymcode.type == KEY_TYPE_NONE)
continue;

k = &config.key_bindings[ji];
if ((k->iscommonmode || (k->isdefaultmode && keymode.isdefault) ||
(strcmp(keymode.mode, k->mode) == 0)) &&
Expand Down Expand Up @@ -3726,6 +3735,16 @@ void keypress(struct wl_listener *listener, void *data) {
}
}

/* Track non-modifier key presses to invalidate modifier-only bindings.
* Modifier keycodes: 133,134=Super, 37,105=Ctrl, 50,62=Shift, 64,108=Alt */
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED &&
keycode != 133 && keycode != 134 && /* Super_L, Super_R */
keycode != 37 && keycode != 105 && /* Control_L, Control_R */
keycode != 50 && keycode != 62 && /* Shift_L, Shift_R */
keycode != 64 && keycode != 108) { /* Alt_L, Alt_R */
mod_key_used = true;
}

/* On _press_ if there is no active screen locker,
* attempt to process a compositor keybinding. */
for (i = 0; i < nsyms; i++)
Expand Down Expand Up @@ -3782,6 +3801,47 @@ void keypressmod(struct wl_listener *listener, void *data) {
* pressed. We simply communicate this to the client. */
KeyboardGroup *group = wl_container_of(listener, group, modifiers);

uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);

/* Handle modifier-only keybindings (KEY_TYPE_NONE).
* Fire when a modifier is released without any non-modifier key having
* been pressed during the hold. We detect release by checking if a
* modifier bit was set in prev_mods but is now cleared.
* The (prev_mods & ~mods) check ensures a modifier was actually
* released (bits cleared), not just a new modifier added. */
if (!locked && group == kb_group && !mod_key_used && prev_mods != 0 &&
mods != prev_mods && (prev_mods & ~mods) != 0) {
/* A modifier was released — check if the previously held modifier
* combination matches any KEY_TYPE_NONE bindings */
for (int32_t ji = 0; ji < config.key_bindings_count; ji++) {
const KeyBinding *k = &config.key_bindings[ji];
if (k->keysymcode.type != KEY_TYPE_NONE)
continue;
if (!k->func)
continue;
if (!(k->iscommonmode ||
(k->isdefaultmode && keymode.isdefault) ||
(strcmp(keymode.mode, k->mode) == 0)))
continue;
/* The binding's mod should match the modifiers that were held
* before this release */
if (CLEANMASK(prev_mods) == CLEANMASK(k->mod)) {
k->func(&k->arg);
/* Mark as used so we don't fire again during
* subsequent releases of the same combo */
mod_key_used = true;
break;
}
}
}

/* Reset tracking when all modifiers are released */
if (mods == 0) {
mod_key_used = false;
}

prev_mods = mods;

if (!dwl_im_keyboard_grab_forward_modifiers(group)) {

wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
Expand Down