]> git.sur5r.net Git - i3/i3/commitdiff
naive implementation of 'bindsym --release' (and bindcode)
authorMichael Stapelberg <michael@stapelberg.de>
Thu, 6 Sep 2012 15:04:31 +0000 (17:04 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Thu, 6 Sep 2012 15:04:31 +0000 (17:04 +0200)
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.

include/config.h
include/data.h
src/cfgparse.l
src/cfgparse.y
src/config.c
src/handlers.c
src/key_press.c

index ebb24864cded0cb06548cfaebe11340ca87854be..1a48016a386ef635d8232afda17638ba112f867c 100644 (file)
@@ -312,7 +312,7 @@ void switch_mode(const char *new_mode);
  * 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.
index d60ec3cda6974f74aef977fbdb822b9f4c9e30c8..4b886ae8cfd3bae1c0be0bc1f8e8d7ee4c2c72b8 100644 (file)
@@ -200,6 +200,10 @@ struct regex {
  *
  */
 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
      * stored with the binding to be able to re-convert it into a keycode
      * if the keyboard mapping changes (using Xmodmap for example) */
index 52cde189eef27ed18ee96a4acb06e35c6fcf155e..8ee2a1dad75fdcb5f414b59437c9f46c9662e815 100644 (file)
@@ -66,6 +66,7 @@ EOL     (\r?\n)
 %x BAR_COLOR
 
 %x EXEC
+%x OPTRELEASE
 
 %%
 
@@ -172,12 +173,14 @@ EOL     (\r?\n)
 <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>.                         { 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; }
 bar                             { yy_push_state(BAR); return TOK_BAR; }
 mode                            { return TOKMODE; }
 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; }
-bindsym                         { yy_push_state(BINDSYM_COND); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
+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); yy_push_state(OPTRELEASE); yy_push_state(EAT_WHITESPACE); return TOKBINDSYM; }
 floating_maximum_size           { return TOKFLOATING_MAXIMUM_SIZE; }
 floating_minimum_size           { return TOKFLOATING_MINIMUM_SIZE; }
 floating_modifier               { return TOKFLOATING_MODIFIER; }
index af7d77cf792cc31c9045d6060d2334b1de0e2989..5c1667784cd44aacdb80e64313150f12d5f67a09 100644 (file)
@@ -3,6 +3,8 @@
  * vim:ts=4:sw=4:expandtab
  *
  */
+#undef I3__FILE__
+#define I3__FILE__ "cfgparse.y"
 #include <sys/types.h>
 #include <sys/stat.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_URGENT_WORKSPACE "urgent_workspace"
 %token                  TOK_NO_STARTUP_ID           "--no-startup-id"
+%token                  TOK_RELEASE                 "--release"
 
 %token              TOK_MARK            "mark"
 %token              TOK_CLASS           "class"
@@ -811,6 +814,7 @@ void parse_file(const char *f) {
 %type   <number>        bar_mode_mode
 %type   <number>        bar_modifier_modifier
 %type   <number>        optional_no_startup_id
+%type   <number>        optional_release
 %type   <string>        command
 %type   <string>        word_or_number
 %type   <string>        qstring_or_number
@@ -879,33 +883,40 @@ binding:
     ;
 
 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));
 
-        new->keycode = $2;
-        new->mods = $1;
-        new->command = $3;
+        new->release = $1;
+        new->keycode = $3;
+        new->mods = $2;
+        new->command = $4;
 
         $$ = new;
     }
     ;
 
 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));
 
-        new->symbol = $2;
-        new->mods = $1;
-        new->command = $3;
+        new->release = $1;
+        new->symbol = $3;
+        new->mods = $2;
+        new->command = $4;
 
         $$ = new;
     }
     ;
 
+optional_release:
+    /* empty */ { $$ = false; }
+    | TOK_RELEASE  { $$ = true; }
+    ;
+
 for_window:
     TOK_FOR_WINDOW match command
     {
index 017d40cea275903c1d261071f35d79c814a3101c..41491d8676aede6759cd296083a2040ada9f9d9d 100644 (file)
@@ -54,7 +54,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
  * 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;
 
     TAILQ_FOREACH(bind, bindings, bindings) {
@@ -62,6 +62,10 @@ Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) {
         if (bind->mods != modifiers)
             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
          * the array of translated keycodes for the event’s keycode */
         if (bind->symbol != NULL) {
index 0d087e79896497d7ef7a96ffff38d06e3a458a74..21a873420d989430394f7d1b25043ea76ca6e100 100644 (file)
@@ -1045,6 +1045,7 @@ void handle_event(int type, xcb_generic_event_t *event) {
 
     switch (type) {
         case XCB_KEY_PRESS:
+        case XCB_KEY_RELEASE:
             handle_key_press((xcb_key_press_event_t*)event);
             break;
 
index 2a578d66d65adac4c44e63beafbbe6c6d9c93954..3f466a2e90af0a631e08a3feca631b9f41307610 100644 (file)
@@ -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
- * the bound action to parse_command().
+ * There was a KeyPress or KeyRelease (both events have the same fields). We
+ * 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) {
+    bool key_release = (event->response_type == XCB_KEY_RELEASE);
 
     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 */
     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);
 
     /* 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
      * 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) {
         state_filtered &= ~(BIND_MODE_SWITCH);
         DLOG("no match, new state_filtered = %d\n", state_filtered);
-        if ((bind = get_binding(state_filtered, event->detail)) == NULL) {
-            ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n",
+        if ((bind = get_binding(state_filtered, key_release, event->detail)) == NULL) {
+            /* 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);
             return;
         }