]> Dogcows Code - chaz/openbox/blob - openbox/mouse.c
Merge branch 'backport' into work
[chaz/openbox] / openbox / mouse.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 mouse.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "openbox.h"
21 #include "config.h"
22 #include "actions.h"
23 #include "event.h"
24 #include "client.h"
25 #include "grab.h"
26 #include "frame.h"
27 #include "translate.h"
28 #include "mouse.h"
29 #include "gettext.h"
30 #include "obt/display.h"
31
32 #include <glib.h>
33
34 typedef struct {
35 guint state;
36 guint button;
37 GSList *actions[OB_NUM_MOUSE_ACTIONS]; /* lists of Action pointers */
38 } ObMouseBinding;
39
40 #define FRAME_CONTEXT(co, cl) ((cl && cl->type != OB_CLIENT_TYPE_DESKTOP) ? \
41 co == OB_FRAME_CONTEXT_FRAME : FALSE)
42 #define CLIENT_CONTEXT(co, cl) ((cl && cl->type == OB_CLIENT_TYPE_DESKTOP) ? \
43 co == OB_FRAME_CONTEXT_DESKTOP : \
44 co == OB_FRAME_CONTEXT_CLIENT)
45
46 /* Array of GSList*s of ObMouseBinding*s. */
47 static GSList *bound_contexts[OB_FRAME_NUM_CONTEXTS];
48
49 ObFrameContext mouse_button_frame_context(ObFrameContext context,
50 guint button,
51 guint state)
52 {
53 GSList *it;
54 ObFrameContext x = context;
55
56 for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
57 ObMouseBinding *b = it->data;
58
59 if (b->button == button && b->state == state)
60 return context;
61 }
62
63 switch (context) {
64 case OB_FRAME_CONTEXT_NONE:
65 case OB_FRAME_CONTEXT_DESKTOP:
66 case OB_FRAME_CONTEXT_CLIENT:
67 case OB_FRAME_CONTEXT_TITLEBAR:
68 case OB_FRAME_CONTEXT_FRAME:
69 case OB_FRAME_CONTEXT_MOVE_RESIZE:
70 case OB_FRAME_CONTEXT_LEFT:
71 case OB_FRAME_CONTEXT_RIGHT:
72 break;
73 case OB_FRAME_CONTEXT_ROOT:
74 x = OB_FRAME_CONTEXT_DESKTOP;
75 break;
76 case OB_FRAME_CONTEXT_BOTTOM:
77 case OB_FRAME_CONTEXT_BLCORNER:
78 case OB_FRAME_CONTEXT_BRCORNER:
79 x = OB_FRAME_CONTEXT_BOTTOM;
80 break;
81 case OB_FRAME_CONTEXT_TLCORNER:
82 case OB_FRAME_CONTEXT_TRCORNER:
83 case OB_FRAME_CONTEXT_TOP:
84 case OB_FRAME_CONTEXT_MAXIMIZE:
85 case OB_FRAME_CONTEXT_ALLDESKTOPS:
86 case OB_FRAME_CONTEXT_SHADE:
87 case OB_FRAME_CONTEXT_ICONIFY:
88 case OB_FRAME_CONTEXT_ICON:
89 case OB_FRAME_CONTEXT_CLOSE:
90 x = OB_FRAME_CONTEXT_TITLEBAR;
91 break;
92 case OB_FRAME_NUM_CONTEXTS:
93 g_assert_not_reached();
94 }
95
96 /* allow for multiple levels of fall-through */
97 if (x != context)
98 return mouse_button_frame_context(x, button, state);
99 else
100 return x;
101 }
102
103 void mouse_grab_for_client(ObClient *client, gboolean grab)
104 {
105 gint i;
106 GSList *it;
107
108 for (i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i)
109 for (it = bound_contexts[i]; it; it = g_slist_next(it)) {
110 /* grab/ungrab the button */
111 ObMouseBinding *b = it->data;
112 Window win;
113 gint mode;
114 guint mask;
115
116 if (FRAME_CONTEXT(i, client)) {
117 win = client->frame->window;
118 mode = GrabModeAsync;
119 mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
120 } else if (CLIENT_CONTEXT(i, client)) {
121 win = client->window;
122 mode = GrabModeSync; /* this is handled in event */
123 mask = ButtonPressMask; /* can't catch more than this with Sync
124 mode the release event is
125 manufactured in event() */
126 } else continue;
127
128 if (grab)
129 grab_button_full(b->button, b->state, win, mask, mode,
130 OB_CURSOR_NONE);
131 else
132 ungrab_button(b->button, b->state, win);
133 }
134 }
135
136 static void grab_all_clients(gboolean grab)
137 {
138 GList *it;
139
140 for (it = client_list; it; it = g_list_next(it))
141 mouse_grab_for_client(it->data, grab);
142 }
143
144 void mouse_unbind_all(void)
145 {
146 gint i;
147 GSList *it;
148
149 for(i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i) {
150 for (it = bound_contexts[i]; it; it = g_slist_next(it)) {
151 ObMouseBinding *b = it->data;
152 gint j;
153
154 for (j = 0; j < OB_NUM_MOUSE_ACTIONS; ++j) {
155 GSList *jt;
156
157 for (jt = b->actions[j]; jt; jt = g_slist_next(jt))
158 actions_act_unref(jt->data);
159 g_slist_free(b->actions[j]);
160 }
161 g_free(b);
162 }
163 g_slist_free(bound_contexts[i]);
164 bound_contexts[i] = NULL;
165 }
166 }
167
168 static ObUserAction mouse_action_to_user_action(ObMouseAction a)
169 {
170 switch (a) {
171 case OB_MOUSE_ACTION_PRESS: return OB_USER_ACTION_MOUSE_PRESS;
172 case OB_MOUSE_ACTION_RELEASE: return OB_USER_ACTION_MOUSE_RELEASE;
173 case OB_MOUSE_ACTION_CLICK: return OB_USER_ACTION_MOUSE_CLICK;
174 case OB_MOUSE_ACTION_DOUBLE_CLICK:
175 return OB_USER_ACTION_MOUSE_DOUBLE_CLICK;
176 case OB_MOUSE_ACTION_MOTION: return OB_USER_ACTION_MOUSE_MOTION;
177 default:
178 g_assert_not_reached();
179 }
180 }
181
182 static gboolean fire_binding(ObMouseAction a, ObFrameContext context,
183 ObClient *c, guint state,
184 guint button, gint x, gint y)
185 {
186 GSList *it;
187 ObMouseBinding *b;
188
189 for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
190 b = it->data;
191 if (b->state == state && b->button == button)
192 break;
193 }
194 /* if not bound, then nothing to do! */
195 if (it == NULL) return FALSE;
196
197 actions_run_acts(b->actions[a], mouse_action_to_user_action(a),
198 state, x, y, button, context, c);
199 return TRUE;
200 }
201
202 void mouse_event(ObClient *client, XEvent *e)
203 {
204 static Time ltime;
205 static guint button = 0, state = 0, lbutton = 0;
206 static Window lwindow = None;
207 static gint px, py, pwx = -1, pwy = -1;
208
209 ObFrameContext context;
210 gboolean click = FALSE;
211 gboolean dclick = FALSE;
212
213 switch (e->type) {
214 case ButtonPress:
215 context = frame_context(client, e->xbutton.window,
216 e->xbutton.x, e->xbutton.y);
217 context = mouse_button_frame_context(context, e->xbutton.button,
218 e->xbutton.state);
219
220 px = e->xbutton.x_root;
221 py = e->xbutton.y_root;
222 if (!button) pwx = e->xbutton.x;
223 if (!button) pwy = e->xbutton.y;
224 button = e->xbutton.button;
225 state = e->xbutton.state;
226
227 /* if the binding was in a client context, then we need to call
228 XAllowEvents with ReplayPointer at some point, to send the event
229 through to the client. when this happens though depends. if
230 windows are going to be moved on screen, then the click will end
231 up going somewhere wrong, so have the action system perform the
232 ReplayPointer for us if that is the case. */
233 if (CLIENT_CONTEXT(context, client))
234 actions_set_need_pointer_replay_before_move(TRUE);
235 else
236 actions_set_need_pointer_replay_before_move(FALSE);
237
238 fire_binding(OB_MOUSE_ACTION_PRESS, context,
239 client, e->xbutton.state,
240 e->xbutton.button,
241 e->xbutton.x_root, e->xbutton.y_root);
242
243 /* if the bindings grab the pointer, there won't be a ButtonRelease
244 event for us */
245 if (grab_on_pointer())
246 button = 0;
247
248 /* replay the pointer event if it hasn't been replayed yet (i.e. no
249 windows were moved) */
250 if (actions_get_need_pointer_replay_before_move())
251 XAllowEvents(obt_display, ReplayPointer, event_curtime);
252
253 /* in the client context, we won't get a button release because of the
254 way it is grabbed, so just fake one */
255 if (!CLIENT_CONTEXT(context, client))
256 break;
257
258 case ButtonRelease:
259 /* use where the press occured in the window */
260 context = frame_context(client, e->xbutton.window, pwx, pwy);
261 context = mouse_button_frame_context(context, e->xbutton.button,
262 e->xbutton.state);
263
264 if (e->xbutton.button == button)
265 pwx = pwy = -1;
266
267 if (e->xbutton.button == button) {
268 /* clicks are only valid if its released over the window */
269 gint junk1, junk2;
270 Window wjunk;
271 guint ujunk, b, w, h;
272 /* this can cause errors to occur when the window closes */
273 obt_display_ignore_errors(TRUE);
274 junk1 = XGetGeometry(obt_display, e->xbutton.window,
275 &wjunk, &junk1, &junk2, &w, &h, &b, &ujunk);
276 obt_display_ignore_errors(FALSE);
277 if (junk1) {
278 if (e->xbutton.x >= (signed)-b &&
279 e->xbutton.y >= (signed)-b &&
280 e->xbutton.x < (signed)(w+b) &&
281 e->xbutton.y < (signed)(h+b)) {
282 click = TRUE;
283 /* double clicks happen if there were 2 in a row! */
284 if (lbutton == button &&
285 lwindow == e->xbutton.window &&
286 e->xbutton.time - config_mouse_dclicktime <=
287 ltime) {
288 dclick = TRUE;
289 lbutton = 0;
290 } else {
291 lbutton = button;
292 lwindow = e->xbutton.window;
293 }
294 } else {
295 lbutton = 0;
296 lwindow = None;
297 }
298 }
299
300 button = 0;
301 state = 0;
302 ltime = e->xbutton.time;
303 }
304 fire_binding(OB_MOUSE_ACTION_RELEASE, context,
305 client, e->xbutton.state,
306 e->xbutton.button,
307 e->xbutton.x_root,
308 e->xbutton.y_root);
309 if (click)
310 fire_binding(OB_MOUSE_ACTION_CLICK, context,
311 client, e->xbutton.state,
312 e->xbutton.button,
313 e->xbutton.x_root,
314 e->xbutton.y_root);
315 if (dclick)
316 fire_binding(OB_MOUSE_ACTION_DOUBLE_CLICK, context,
317 client, e->xbutton.state,
318 e->xbutton.button,
319 e->xbutton.x_root,
320 e->xbutton.y_root);
321 break;
322
323 case MotionNotify:
324 if (button) {
325 context = frame_context(client, e->xmotion.window, pwx, pwy);
326 context = mouse_button_frame_context(context, button, state);
327
328 if (ABS(e->xmotion.x_root - px) >= config_mouse_threshold ||
329 ABS(e->xmotion.y_root - py) >= config_mouse_threshold) {
330
331 /* You can't drag on buttons */
332 if (context == OB_FRAME_CONTEXT_MAXIMIZE ||
333 context == OB_FRAME_CONTEXT_ALLDESKTOPS ||
334 context == OB_FRAME_CONTEXT_SHADE ||
335 context == OB_FRAME_CONTEXT_ICONIFY ||
336 context == OB_FRAME_CONTEXT_ICON ||
337 context == OB_FRAME_CONTEXT_CLOSE)
338 break;
339
340 fire_binding(OB_MOUSE_ACTION_MOTION, context,
341 client, state, button, px, py);
342 button = 0;
343 state = 0;
344 }
345 }
346 break;
347
348 default:
349 g_assert_not_reached();
350 }
351 }
352
353 gboolean mouse_bind(const gchar *buttonstr, const gchar *contextstr,
354 ObMouseAction mact, ObActionsAct *action)
355 {
356 guint state, button;
357 ObFrameContext context;
358 ObMouseBinding *b;
359 GSList *it;
360
361 if (!translate_button(buttonstr, &state, &button)) {
362 g_message(_("Invalid button '%s' in mouse binding"), buttonstr);
363 return FALSE;
364 }
365
366 context = frame_context_from_string(contextstr);
367 if (!context) {
368 g_message(_("Invalid context '%s' in mouse binding"), contextstr);
369 return FALSE;
370 }
371
372 for (it = bound_contexts[context]; it; it = g_slist_next(it)) {
373 b = it->data;
374 if (b->state == state && b->button == button) {
375 b->actions[mact] = g_slist_append(b->actions[mact], action);
376 return TRUE;
377 }
378 }
379
380 /* add the binding */
381 b = g_new0(ObMouseBinding, 1);
382 b->state = state;
383 b->button = button;
384 b->actions[mact] = g_slist_append(NULL, action);
385 bound_contexts[context] = g_slist_append(bound_contexts[context], b);
386
387 return TRUE;
388 }
389
390 void mouse_startup(gboolean reconfig)
391 {
392 grab_all_clients(TRUE);
393 }
394
395 void mouse_shutdown(gboolean reconfig)
396 {
397 grab_all_clients(FALSE);
398 mouse_unbind_all();
399 }
This page took 0.057365 seconds and 5 git commands to generate.