]> Dogcows Code - chaz/openbox/blob - openbox/mouse.c
grab root mouse bindings on desktop windows also, since they are conceptually the...
[chaz/openbox] / openbox / mouse.c
1 #include "openbox.h"
2 #include "config.h"
3 #include "action.h"
4 #include "event.h"
5 #include "client.h"
6 #include "prop.h"
7 #include "grab.h"
8 #include "frame.h"
9 #include "translate.h"
10 #include "mouse.h"
11 #include "keyboard.h"
12 #include <glib.h>
13
14 typedef struct {
15 guint state;
16 guint button;
17 GSList *actions[OB_MOUSE_NUM_ACTIONS]; /* lists of Action pointers */
18 } ObMouseBinding;
19
20 #define CLIENT_CONTEXT(co, cl) (co == OB_FRAME_CONTEXT_CLIENT || \
21 (co == OB_FRAME_CONTEXT_ROOT && \
22 cl->type == OB_CLIENT_TYPE_DESKTOP))
23
24 /* Array of GSList*s of PointerBinding*s. */
25 static GSList *bound_contexts[OB_FRAME_NUM_CONTEXTS];
26
27 void mouse_grab_for_client(ObClient *client, gboolean grab)
28 {
29 int i;
30 GSList *it;
31
32 for (i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i)
33 for (it = bound_contexts[i]; it != NULL; it = it->next) {
34 /* grab/ungrab the button */
35 ObMouseBinding *b = it->data;
36 Window win;
37 int mode;
38 unsigned int mask;
39
40 if (i == OB_FRAME_CONTEXT_FRAME) {
41 win = client->frame->window;
42 mode = GrabModeAsync;
43 mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
44 } else if (CLIENT_CONTEXT(i, client)) {
45 win = client->frame->plate;
46 mode = GrabModeSync; /* this is handled in event */
47 mask = ButtonPressMask; /* can't catch more than this with Sync
48 mode the release event is
49 manufactured in event() */
50 } else continue;
51
52 if (grab)
53 grab_button_full(b->button, b->state, win, mask, mode, None);
54 else
55 ungrab_button(b->button, b->state, win);
56 }
57 }
58
59 static void grab_all_clients(gboolean grab)
60 {
61 GList *it;
62
63 for (it = client_list; it != NULL; it = it->next)
64 mouse_grab_for_client(it->data, grab);
65 }
66
67 static void clearall()
68 {
69 int i;
70 GSList *it;
71
72 for(i = 0; i < OB_FRAME_NUM_CONTEXTS; ++i) {
73 for (it = bound_contexts[i]; it != NULL; it = it->next) {
74 int j;
75
76 ObMouseBinding *b = it->data;
77 for (j = 0; j < OB_MOUSE_NUM_ACTIONS; ++j) {
78 GSList *it;
79 for (it = b->actions[j]; it; it = it->next) {
80 action_free(it->data);
81 }
82 g_slist_free(b->actions[j]);
83 }
84 g_free(b);
85 }
86 g_slist_free(bound_contexts[i]);
87 }
88 }
89
90 static gboolean fire_button(ObMouseAction a, ObFrameContext context,
91 ObClient *c, guint state,
92 guint button, int x, int y)
93 {
94 GSList *it;
95 ObMouseBinding *b;
96
97 for (it = bound_contexts[context]; it != NULL; it = it->next) {
98 b = it->data;
99 if (b->state == state && b->button == button)
100 break;
101 }
102 /* if not bound, then nothing to do! */
103 if (it == NULL) return FALSE;
104
105 for (it = b->actions[a]; it; it = it->next) {
106 ObAction *act = it->data;
107 if (act->func != NULL) {
108 act->data.any.c = c;
109
110 g_assert(act->func != action_moveresize);
111
112 if (act->func == action_showmenu) {
113 act->data.showmenu.x = x;
114 act->data.showmenu.y = y;
115 }
116
117 if (act->func == action_desktop_dir)
118 {
119 act->data.desktopdir.final = FALSE;
120 act->data.desktopdir.cancel = FALSE;
121 }
122 if (act->func == action_send_to_desktop_dir)
123 {
124 act->data.sendtodir.final = FALSE;
125 act->data.sendtodir.cancel = FALSE;
126 }
127
128 if (config_desktop_popup &&
129 (act->func == action_desktop_dir ||
130 act->func == action_send_to_desktop_dir))
131 {
132 keyboard_interactive_grab(state, c, context, act);
133 }
134
135 act->func(&act->data);
136 }
137 }
138 return TRUE;
139 }
140
141 static gboolean fire_motion(ObMouseAction a, ObFrameContext context,
142 ObClient *c, guint state, guint button,
143 int x_root, int y_root, guint32 corner)
144 {
145 GSList *it;
146 ObMouseBinding *b;
147
148 for (it = bound_contexts[context]; it != NULL; it = it->next) {
149 b = it->data;
150 if (b->state == state && b->button == button)
151 break;
152 }
153 /* if not bound, then nothing to do! */
154 if (it == NULL) return FALSE;
155
156 for (it = b->actions[a]; it; it = it->next) {
157 ObAction *act = it->data;
158 if (act->func != NULL) {
159 act->data.any.c = c;
160
161 if (act->func == action_moveresize) {
162 act->data.moveresize.x = x_root;
163 act->data.moveresize.y = y_root;
164 act->data.moveresize.button = button;
165 if (!(act->data.moveresize.corner ==
166 prop_atoms.net_wm_moveresize_move ||
167 act->data.moveresize.corner ==
168 prop_atoms.net_wm_moveresize_move_keyboard ||
169 act->data.moveresize.corner ==
170 prop_atoms.net_wm_moveresize_size_keyboard))
171 act->data.moveresize.corner = corner;
172 } else
173 g_assert_not_reached();
174
175 act->func(&act->data);
176 }
177 }
178 return TRUE;
179 }
180
181 static guint32 pick_corner(int x, int y, int cx, int cy, int cw, int ch)
182 {
183 if (x - cx < cw / 2) {
184 if (y - cy < ch / 2)
185 return prop_atoms.net_wm_moveresize_size_topleft;
186 else
187 return prop_atoms.net_wm_moveresize_size_bottomleft;
188 } else {
189 if (y - cy < ch / 2)
190 return prop_atoms.net_wm_moveresize_size_topright;
191 else
192 return prop_atoms.net_wm_moveresize_size_bottomright;
193 }
194 }
195
196 void mouse_event(ObClient *client, ObFrameContext context, XEvent *e)
197 {
198 static Time ltime;
199 static guint button = 0, state = 0, lbutton = 0;
200
201 static Window lwindow = None;
202 static int px, py;
203 gboolean click = FALSE;
204 gboolean dclick = FALSE;
205
206 switch (e->type) {
207 case ButtonPress:
208 px = e->xbutton.x_root;
209 py = e->xbutton.y_root;
210 button = e->xbutton.button;
211 state = e->xbutton.state;
212
213 fire_button(OB_MOUSE_ACTION_PRESS, context,
214 client, e->xbutton.state,
215 e->xbutton.button,
216 e->xbutton.x_root, e->xbutton.y_root);
217
218 if (CLIENT_CONTEXT(context, client)) {
219 /* Replay the event, so it goes to the client*/
220 XAllowEvents(ob_display, ReplayPointer, event_lasttime);
221 /* Fall through to the release case! */
222 } else
223 break;
224
225 case ButtonRelease:
226 if (e->xbutton.button == button) {
227 /* clicks are only valid if its released over the window */
228 int junk1, junk2;
229 Window wjunk;
230 guint ujunk, b, w, h;
231 XGetGeometry(ob_display, e->xbutton.window,
232 &wjunk, &junk1, &junk2, &w, &h, &b, &ujunk);
233 if (e->xbutton.x >= (signed)-b &&
234 e->xbutton.y >= (signed)-b &&
235 e->xbutton.x < (signed)(w+b) &&
236 e->xbutton.y < (signed)(h+b)) {
237 click = TRUE;
238 /* double clicks happen if there were 2 in a row! */
239 if (lbutton == button &&
240 lwindow == e->xbutton.window &&
241 e->xbutton.time - config_mouse_dclicktime <=
242 ltime) {
243 dclick = TRUE;
244 lbutton = 0;
245 } else {
246 lbutton = button;
247 lwindow = e->xbutton.window;
248 }
249 } else {
250 lbutton = 0;
251 lwindow = None;
252 }
253
254 button = 0;
255 state = 0;
256 ltime = e->xbutton.time;
257 }
258 fire_button(OB_MOUSE_ACTION_RELEASE, context,
259 client, e->xbutton.state,
260 e->xbutton.button,
261 e->xbutton.x_root, e->xbutton.y_root);
262 if (click)
263 fire_button(OB_MOUSE_ACTION_CLICK, context,
264 client, e->xbutton.state,
265 e->xbutton.button,
266 e->xbutton.x_root,
267 e->xbutton.y_root);
268 if (dclick)
269 fire_button(OB_MOUSE_ACTION_DOUBLE_CLICK, context,
270 client, e->xbutton.state,
271 e->xbutton.button,
272 e->xbutton.x_root,
273 e->xbutton.y_root);
274 break;
275
276 case MotionNotify:
277 if (button) {
278 if (ABS(e->xmotion.x_root - px) >=
279 config_mouse_threshold ||
280 ABS(e->xmotion.y_root - py) >=
281 config_mouse_threshold) {
282 guint32 corner;
283
284 /* You can't drag on buttons */
285 if (context == OB_FRAME_CONTEXT_MAXIMIZE ||
286 context == OB_FRAME_CONTEXT_ALLDESKTOPS ||
287 context == OB_FRAME_CONTEXT_SHADE ||
288 context == OB_FRAME_CONTEXT_ICONIFY ||
289 context == OB_FRAME_CONTEXT_ICON ||
290 context == OB_FRAME_CONTEXT_CLOSE)
291 break;
292
293 if (!client)
294 corner = prop_atoms.net_wm_moveresize_size_bottomright;
295 else
296 corner =
297 pick_corner(e->xmotion.x_root,
298 e->xmotion.y_root,
299 client->frame->area.x,
300 client->frame->area.y,
301 /* use the client size because the frame
302 can be differently sized (shaded
303 windows) and we want this based on the
304 clients size */
305 client->area.width +
306 client->frame->size.left +
307 client->frame->size.right,
308 client->area.height +
309 client->frame->size.top +
310 client->frame->size.bottom);
311 fire_motion(OB_MOUSE_ACTION_MOTION, context,
312 client, state, button, px, py, corner);
313 button = 0;
314 state = 0;
315 }
316 }
317 break;
318
319 default:
320 g_assert_not_reached();
321 }
322 }
323
324 gboolean mouse_bind(char *buttonstr, char *contextstr, ObMouseAction mact,
325 ObAction *action)
326 {
327 guint state, button;
328 ObFrameContext context;
329 ObMouseBinding *b;
330 GSList *it;
331
332 if (!translate_button(buttonstr, &state, &button)) {
333 g_warning("invalid button '%s'", buttonstr);
334 return FALSE;
335 }
336
337 contextstr = g_ascii_strdown(contextstr, -1);
338 context = frame_context_from_string(contextstr);
339 if (!context) {
340 g_warning("invalid context '%s'", contextstr);
341 g_free(contextstr);
342 return FALSE;
343 }
344 g_free(contextstr);
345
346 for (it = bound_contexts[context]; it != NULL; it = it->next){
347 b = it->data;
348 if (b->state == state && b->button == button) {
349 b->actions[mact] = g_slist_append(b->actions[mact], action);
350 return TRUE;
351 }
352 }
353
354 grab_all_clients(FALSE);
355
356 /* add the binding */
357 b = g_new0(ObMouseBinding, 1);
358 b->state = state;
359 b->button = button;
360 b->actions[mact] = g_slist_append(NULL, action);
361 bound_contexts[context] = g_slist_append(bound_contexts[context], b);
362
363 grab_all_clients(TRUE);
364
365 return TRUE;
366 }
367
368 void mouse_startup()
369 {
370 }
371
372 void mouse_shutdown()
373 {
374 grab_all_clients(FALSE);
375 clearall();
376 }
This page took 0.053593 seconds and 5 git commands to generate.