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