naive implementation of 'bindsym --release' (and bindcode)
The implementation is naive because the user has to generate exactly the event he specified. That is, if you use this binding: bindsym --release $mod+x exec import /tmp/latest-screenshot.png Then it will only be triggered if you hit $mod, hit x, release x, release $mod. It will not be triggered if you hit $mod, hit x, release $mod, release x. The reason is that the KeyRelease event in the latter case will not have the modifier in its flags, so it doesn’t match the configured binding.
This commit is contained in:
parent
f44d4ce4b3
commit
c6c6d3a952
|
@ -312,7 +312,7 @@ void switch_mode(const char *new_mode);
|
||||||
* or NULL if no such binding exists.
|
* or NULL if no such binding exists.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode);
|
Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kills the configerror i3-nagbar process, if any.
|
* Kills the configerror i3-nagbar process, if any.
|
||||||
|
|
|
@ -200,6 +200,10 @@ struct regex {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct Binding {
|
struct Binding {
|
||||||
|
/** If true, the binding should be executed upon a KeyRelease event, not a
|
||||||
|
* KeyPress (the default). */
|
||||||
|
bool release;
|
||||||
|
|
||||||
/** Symbol the user specified in configfile, if any. This needs to be
|
/** Symbol the user specified in configfile, if any. This needs to be
|
||||||
* stored with the binding to be able to re-convert it into a keycode
|
* stored with the binding to be able to re-convert it into a keycode
|
||||||
* if the keyboard mapping changes (using Xmodmap for example) */
|
* if the keyboard mapping changes (using Xmodmap for example) */
|
||||||
|
|
|
@ -66,6 +66,7 @@ EOL (\r?\n)
|
||||||
%x BAR_COLOR
|
%x BAR_COLOR
|
||||||
|
|
||||||
%x EXEC
|
%x EXEC
|
||||||
|
%x OPTRELEASE
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
@ -172,12 +173,14 @@ EOL (\r?\n)
|
||||||
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
|
<ASSIGN_TARGET_COND>[ \t]+ { BEGIN(WANT_STRING); }
|
||||||
<EXEC>--no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; }
|
<EXEC>--no-startup-id { printf("no startup id\n"); yy_pop_state(); return TOK_NO_STARTUP_ID; }
|
||||||
<EXEC>. { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
<EXEC>. { printf("anything else: *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
||||||
|
<OPTRELEASE>--release { printf("--release\n"); yy_pop_state(); return TOK_RELEASE; }
|
||||||
|
<OPTRELEASE>. { printf("anything else (optrelease): *%s*\n", yytext); yyless(0); yy_pop_state(); yy_pop_state(); }
|
||||||
[0-9-]+ { yylval.number = atoi(yytext); return NUMBER; }
|
[0-9-]+ { yylval.number = atoi(yytext); return NUMBER; }
|
||||||
bar { yy_push_state(BAR); return TOK_BAR; }
|
bar { yy_push_state(BAR); return TOK_BAR; }
|
||||||
mode { return TOKMODE; }
|
mode { return TOKMODE; }
|
||||||
bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
bind { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
||||||
bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
bindcode { yy_push_state(WANT_STRING); yy_push_state(EAT_WHITESPACE); yy_push_state(EAT_WHITESPACE); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDCODE; }
|
||||||
bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
|
bindsym { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
|
||||||
floating_maximum_size { return TOKFLOATING_MAXIMUM_SIZE; }
|
floating_maximum_size { return TOKFLOATING_MAXIMUM_SIZE; }
|
||||||
floating_minimum_size { return TOKFLOATING_MINIMUM_SIZE; }
|
floating_minimum_size { return TOKFLOATING_MINIMUM_SIZE; }
|
||||||
floating_modifier { return TOKFLOATING_MODIFIER; }
|
floating_modifier { return TOKFLOATING_MODIFIER; }
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
* vim:ts=4:sw=4:expandtab
|
* vim:ts=4:sw=4:expandtab
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#undef I3__FILE__
|
||||||
|
#define I3__FILE__ "cfgparse.y"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
@ -782,6 +784,7 @@ void parse_file(const char *f) {
|
||||||
%token TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
|
%token TOK_BAR_COLOR_INACTIVE_WORKSPACE "inactive_workspace"
|
||||||
%token TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
|
%token TOK_BAR_COLOR_URGENT_WORKSPACE "urgent_workspace"
|
||||||
%token TOK_NO_STARTUP_ID "--no-startup-id"
|
%token TOK_NO_STARTUP_ID "--no-startup-id"
|
||||||
|
%token TOK_RELEASE "--release"
|
||||||
|
|
||||||
%token TOK_MARK "mark"
|
%token TOK_MARK "mark"
|
||||||
%token TOK_CLASS "class"
|
%token TOK_CLASS "class"
|
||||||
|
@ -811,6 +814,7 @@ void parse_file(const char *f) {
|
||||||
%type <number> bar_mode_mode
|
%type <number> bar_mode_mode
|
||||||
%type <number> bar_modifier_modifier
|
%type <number> bar_modifier_modifier
|
||||||
%type <number> optional_no_startup_id
|
%type <number> optional_no_startup_id
|
||||||
|
%type <number> optional_release
|
||||||
%type <string> command
|
%type <string> command
|
||||||
%type <string> word_or_number
|
%type <string> word_or_number
|
||||||
%type <string> qstring_or_number
|
%type <string> qstring_or_number
|
||||||
|
@ -879,33 +883,40 @@ binding:
|
||||||
;
|
;
|
||||||
|
|
||||||
bindcode:
|
bindcode:
|
||||||
binding_modifiers NUMBER command
|
optional_release binding_modifiers NUMBER command
|
||||||
{
|
{
|
||||||
printf("\tFound keycode binding mod%d with key %d and command %s\n", $1, $2, $3);
|
DLOG("bindcode: release = %d, mod = %d, key = %d, command = %s\n", $1, $2, $3, $4);
|
||||||
Binding *new = scalloc(sizeof(Binding));
|
Binding *new = scalloc(sizeof(Binding));
|
||||||
|
|
||||||
new->keycode = $2;
|
new->release = $1;
|
||||||
new->mods = $1;
|
new->keycode = $3;
|
||||||
new->command = $3;
|
new->mods = $2;
|
||||||
|
new->command = $4;
|
||||||
|
|
||||||
$$ = new;
|
$$ = new;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
bindsym:
|
bindsym:
|
||||||
binding_modifiers word_or_number command
|
optional_release binding_modifiers word_or_number command
|
||||||
{
|
{
|
||||||
printf("\tFound keysym binding mod%d with key %s and command %s\n", $1, $2, $3);
|
DLOG("bindsym: release = %d, mod = %d, key = %s, command = %s\n", $1, $2, $3, $4);
|
||||||
Binding *new = scalloc(sizeof(Binding));
|
Binding *new = scalloc(sizeof(Binding));
|
||||||
|
|
||||||
new->symbol = $2;
|
new->release = $1;
|
||||||
new->mods = $1;
|
new->symbol = $3;
|
||||||
new->command = $3;
|
new->mods = $2;
|
||||||
|
new->command = $4;
|
||||||
|
|
||||||
$$ = new;
|
$$ = new;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
optional_release:
|
||||||
|
/* empty */ { $$ = false; }
|
||||||
|
| TOK_RELEASE { $$ = true; }
|
||||||
|
;
|
||||||
|
|
||||||
for_window:
|
for_window:
|
||||||
TOK_FOR_WINDOW match command
|
TOK_FOR_WINDOW match command
|
||||||
{
|
{
|
||||||
|
|
|
@ -54,7 +54,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
|
||||||
* or NULL if no such binding exists.
|
* or NULL if no such binding exists.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) {
|
Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) {
|
||||||
Binding *bind;
|
Binding *bind;
|
||||||
|
|
||||||
TAILQ_FOREACH(bind, bindings, bindings) {
|
TAILQ_FOREACH(bind, bindings, bindings) {
|
||||||
|
@ -62,6 +62,10 @@ Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) {
|
||||||
if (bind->mods != modifiers)
|
if (bind->mods != modifiers)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Check if the binding is for a KeyPress or a KeyRelease event */
|
||||||
|
if (bind->release != key_release)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* If a symbol was specified by the user, we need to look in
|
/* If a symbol was specified by the user, we need to look in
|
||||||
* the array of translated keycodes for the event’s keycode */
|
* the array of translated keycodes for the event’s keycode */
|
||||||
if (bind->symbol != NULL) {
|
if (bind->symbol != NULL) {
|
||||||
|
|
|
@ -1045,6 +1045,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case XCB_KEY_PRESS:
|
case XCB_KEY_PRESS:
|
||||||
|
case XCB_KEY_RELEASE:
|
||||||
handle_key_press((xcb_key_press_event_t*)event);
|
handle_key_press((xcb_key_press_event_t*)event);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -227,15 +227,17 @@ static yajl_callbacks command_error_callbacks = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There was a key press. We compare this key code with our bindings table and pass
|
* There was a KeyPress or KeyRelease (both events have the same fields). We
|
||||||
* the bound action to parse_command().
|
* compare this key code with our bindings table and pass the bound action to
|
||||||
|
* parse_command().
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void handle_key_press(xcb_key_press_event_t *event) {
|
void handle_key_press(xcb_key_press_event_t *event) {
|
||||||
|
bool key_release = (event->response_type == XCB_KEY_RELEASE);
|
||||||
|
|
||||||
last_timestamp = event->time;
|
last_timestamp = event->time;
|
||||||
|
|
||||||
DLOG("Keypress %d, state raw = %d\n", event->detail, event->state);
|
DLOG("%s %d, state raw = %d\n", (key_release ? "KeyRelease" : "KeyPress"), event->detail, event->state);
|
||||||
|
|
||||||
/* Remove the numlock bit, all other bits are modifiers we can bind to */
|
/* 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);
|
uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK);
|
||||||
|
@ -251,7 +253,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
|
||||||
DLOG("(checked mode_switch, state %d)\n", state_filtered);
|
DLOG("(checked mode_switch, state %d)\n", state_filtered);
|
||||||
|
|
||||||
/* Find the binding */
|
/* Find the binding */
|
||||||
Binding *bind = get_binding(state_filtered, event->detail);
|
Binding *bind = get_binding(state_filtered, key_release, event->detail);
|
||||||
|
|
||||||
/* No match? Then the user has Mode_switch enabled but does not have a
|
/* No match? Then the user has Mode_switch enabled but does not have a
|
||||||
* specific keybinding. Fall back to the default keybindings (without
|
* specific keybinding. Fall back to the default keybindings (without
|
||||||
|
@ -260,8 +262,13 @@ void handle_key_press(xcb_key_press_event_t *event) {
|
||||||
if (bind == NULL) {
|
if (bind == NULL) {
|
||||||
state_filtered &= ~(BIND_MODE_SWITCH);
|
state_filtered &= ~(BIND_MODE_SWITCH);
|
||||||
DLOG("no match, new state_filtered = %d\n", state_filtered);
|
DLOG("no match, new state_filtered = %d\n", state_filtered);
|
||||||
if ((bind = get_binding(state_filtered, event->detail)) == NULL) {
|
if ((bind = get_binding(state_filtered, key_release, event->detail)) == NULL) {
|
||||||
ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n",
|
/* This is not a real error since we can have release and
|
||||||
|
* non-release keybindings. On a KeyPress event for which there is
|
||||||
|
* only a !release-binding, but no release-binding, the
|
||||||
|
* corresponding KeyRelease event will trigger this. No problem,
|
||||||
|
* though. */
|
||||||
|
DLOG("Could not lookup key binding (modifiers %d, keycode %d)\n",
|
||||||
state_filtered, event->detail);
|
state_filtered, event->detail);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue