i3 - improved tiling WM


Feature: implement mouse bindings

Patch status: merged

Patch by Tony Crisci

Long description:

A configured mouse binding (for example `bindsym button3 kill`) runs
its command when the mouse button is pressed over parts of a container.

If the binding has no modifer, it will only run when the button is
clicked on the window titlebar.

Otherwise if the binding has a modifier, it will run over the titlebar
or any part of the contained window.

fixes #558

To apply this patch, use:
curl http://cr.i3wm.org/patch/586/raw.patch | git am

b/docs/userguide

27
@@ -394,6 +394,40 @@ umlauts or special characters 'and' having some comfortably reachable key
28
 bindings. For example, when typing, capslock+1 or capslock+2 for switching
29
 workspaces is totally convenient. Try it :-).
30
 
31
+[[mousebindings]]
32
+
33
+=== Mouse bindings
34
+
35
+A mouse binding makes i3 execute a command upon pressing a specific mouse
36
+button in the scope of the clicked container (see <<command_criteria>>). You
37
+can configure mouse bindings in a similar way to key bindings.
38
+
39
+*Syntax*:
40
+----------------------------------
41
+bindsym [Modifiers+]button[n] command
42
+----------------------------------
43
+
44
+If the binding has no modifiers, it will only run when you click on the
45
+titlebar of the window. Otherwise, it will run when any part of the window is
46
+clicked.
47
+
48
+*Examples*:
49
+--------------------------------
50
+# The middle button over a titlebar kills the window
51
+bindsym button2 kill
52
+
53
+# The middle button and a modifer over any part of the window kills the window
54
+bindsym $mod+button2 kill
55
+
56
+# The right button toggles floating
57
+bindsym button3 floating toggle
58
+bindsym $mod+button3 floating toggle
59
+
60
+# The side buttons move the window around
61
+bindsym button9 move left
62
+bindsym button8 move right
63
+--------------------------------
64
+
65
 [[floating_modifier]]
66
 
67
 === The floating modifier

b/include/bindings.h

72
@@ -61,9 +61,10 @@ void switch_mode(const char *new_mode);
73
 void check_for_duplicate_bindings(struct context *context);
74
 
75
 /**
76
- * Runs the given binding and handles parse errors. Returns a CommandResult for
77
- * running the binding's command. Caller should render tree if
78
- * needs_tree_render is true. Free with command_result_free().
79
+ * Runs the given binding and handles parse errors. If con is passed, it will
80
+ * execute the command binding with that container selected by criteria.
81
+ * Returns a CommandResult for running the binding's command. Caller should
82
+ * render tree if needs_tree_render is true. Free with command_result_free().
83
  *
84
  */
85
-CommandResult *run_binding(Binding *bind);
86
+CommandResult *run_binding(Binding *bind, Con *con);

b/src/bindings.c

91
@@ -379,18 +379,25 @@ void check_for_duplicate_bindings(struct context *context) {
92
 }
93
 
94
 /*
95
- * Runs the given binding and handles parse errors. Returns a CommandResult for
96
- * running the binding's command. Caller should render tree if
97
- * needs_tree_render is true. Free with command_result_free().
98
+ * Runs the given binding and handles parse errors. If con is passed, it will
99
+ * execute the command binding with that container selected by criteria.
100
+ * Returns a CommandResult for running the binding's command. Caller should
101
+ * render tree if needs_tree_render is true. Free with command_result_free().
102
  *
103
  */
104
-CommandResult *run_binding(Binding *bind) {
105
+CommandResult *run_binding(Binding *bind, Con *con) {
106
+    char *command;
107
+
108
     /* We need to copy the command since “reload” may be part of the command,
109
      * and then the memory that bind->command points to may not contain the
110
      * same data anymore. */
111
-    char *command_copy = sstrdup(bind->command);
112
-    CommandResult *result = parse_command(command_copy, NULL);
113
-    free(command_copy);
114
+    if (con == NULL)
115
+        command = sstrdup(bind->command);
116
+    else
117
+        sasprintf(&command, "[con_id=\"%d\"] %s", con, bind->command);
118
+
119
+    CommandResult *result = parse_command(command, NULL);
120
+    free(command);
121
 
122
     if (result->needs_tree_render)
123
         tree_render();

b/src/click.c

128
@@ -177,6 +177,29 @@ static int route_click(Con *con, xcb_button_press_event_t *event, const bool mod
129
     if (con->parent->type == CT_DOCKAREA)
130
         goto done;
131
 
132
+    /* if the user has bound an action to this click, it should override the
133
+     * default behavior. */
134
+    if (dest == CLICK_DECORATION || dest == CLICK_INSIDE) {
135
+        Binding *bind = get_binding_from_xcb_event((xcb_generic_event_t *)event);
136
+        /* clicks over a window decoration will always trigger the binding and
137
+         * clicks on the inside of the window will only trigger a binding if it
138
+         * has modifiers. */
139
+        if (bind && (dest == CLICK_DECORATION || (bind->mods && dest == CLICK_INSIDE))) {
140
+            CommandResult *result = run_binding(bind, con);
141
+
142
+            /* ASYNC_POINTER eats the event */
143
+            xcb_allow_events(conn, XCB_ALLOW_ASYNC_POINTER, event->time);
144
+            xcb_flush(conn);
145
+
146
+            if (result->needs_tree_render)
147
+                tree_render();
148
+
149
+            command_result_free(result);
150
+
151
+            return 0;
152
+        }
153
+    }
154
+
155
     /* Any click in a workspace should focus that workspace. If the
156
      * workspace is on another output we need to do a workspace_show in
157
      * order for i3bar (and others) to notice the change in workspace. */
158
@@ -300,6 +323,7 @@ done:
159
     xcb_allow_events(conn, XCB_ALLOW_REPLAY_POINTER, event->time);
160
     xcb_flush(conn);
161
     tree_render();
162
+
163
     return 0;
164
 }
165
 

b/src/key_press.c

170
@@ -30,7 +30,7 @@ void handle_key_press(xcb_key_press_event_t *event) {
171
     if (bind == NULL)
172
         return;
173
 
174
-    CommandResult *result = run_binding(bind);
175
+    CommandResult *result = run_binding(bind, NULL);
176
 
177
     if (result->needs_tree_render)
178
         tree_render();