]> Dogcows Code - chaz/openbox/blob - plugins/mouse/mouse.c
0a9672058902c23728a69c65ffdec11359453e47
[chaz/openbox] / plugins / mouse / mouse.c
1 #include "../../kernel/openbox.h"
2 #include "../../kernel/dispatch.h"
3 #include "../../kernel/action.h"
4 #include "../../kernel/client.h"
5 #include "../../kernel/frame.h"
6 #include "../../kernel/grab.h"
7 #include "../../kernel/engine.h"
8 #include "translate.h"
9 #include "mouse.h"
10 #include "mouserc_parse.h"
11 #include <glib.h>
12
13 void plugin_setup_config()
14 {
15 }
16
17 static int drag_threshold = 3;
18
19 /* GData of GSList*s of PointerBinding*s. */
20 static GData *bound_contexts;
21
22 struct foreach_grab_temp {
23 Client *client;
24 gboolean grab;
25 };
26
27 static void foreach_grab(GQuark key, gpointer data, gpointer user_data)
28 {
29 struct foreach_grab_temp *d = user_data;
30 GSList *it;
31 for (it = data; it != NULL; it = it->next) {
32 /* grab/ungrab the button */
33 MouseBinding *b = it->data;
34 Window win;
35 int mode;
36 unsigned int mask;
37
38 if (key == g_quark_try_string("frame")) {
39 win = d->client->frame->window;
40 mode = GrabModeAsync;
41 mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
42 } else if (key == g_quark_try_string("client")) {
43 win = d->client->frame->plate;
44 mode = GrabModeSync; /* this is handled in event */
45 mask = ButtonPressMask; /* can't catch more than this with Sync
46 mode the release event is manufactured
47 in event */
48 } else return;
49
50 if (d->grab)
51 grab_button(b->button, b->state, win, mask, mode);
52 else
53 ungrab_button(b->button, b->state, win);
54 }
55 }
56
57 static void grab_for_client(Client *client, gboolean grab)
58 {
59 struct foreach_grab_temp bt;
60 bt.client = client;
61 bt.grab = grab;
62 g_datalist_foreach(&bound_contexts, foreach_grab, &bt);
63 }
64
65 static void grab_all_clients(gboolean grab)
66 {
67 GSList *it;
68
69 for (it = client_list; it != NULL; it = it->next)
70 grab_for_client(it->data, grab);
71 }
72
73 static void foreach_clear(GQuark key, gpointer data, gpointer user_data)
74 {
75 GSList *it;
76 user_data = user_data;
77 for (it = data; it != NULL; it = it->next) {
78 int i;
79
80 MouseBinding *b = it->data;
81 for (i = 0; i < NUM_MOUSEACTION; ++i)
82 if (b->action[i] != NULL)
83 action_free(b->action[i]);
84 g_free(b);
85 }
86 g_slist_free(data);
87 }
88
89 static void fire_button(MouseAction a, GQuark context, Client *c, guint state,
90 guint button)
91 {
92 GSList *it;
93 MouseBinding *b;
94
95 for (it = g_datalist_id_get_data(&bound_contexts, context);
96 it != NULL; it = it->next) {
97 b = it->data;
98 if (b->state == state && b->button == button)
99 break;
100 }
101 /* if not bound, then nothing to do! */
102 if (it == NULL) return;
103
104 if (b->action[a] != NULL && b->action[a]->func != NULL) {
105 b->action[a]->data.any.c = c;
106
107 g_assert(!(b->action[a]->func == action_move ||
108 b->action[a]->func == action_resize));
109
110 b->action[a]->func(&b->action[a]->data);
111 }
112 }
113
114 /* corner should be the opposite corner of the window in which the pointer
115 clicked, Corner_TopLeft if a good default if there is no client */
116 static void fire_motion(MouseAction a, GQuark context, Client *c, guint state,
117 guint button, int cx, int cy, int cw, int ch,
118 int dx, int dy, gboolean final, Corner corner)
119 {
120 GSList *it;
121 MouseBinding *b;
122
123 for (it = g_datalist_id_get_data(&bound_contexts, context);
124 it != NULL; it = it->next) {
125 b = it->data;
126 if (b->state == state && b->button == button)
127 break;
128 }
129 /* if not bound, then nothing to do! */
130 if (it == NULL) return;
131
132 if (b->action[a] != NULL && b->action[a]->func != NULL) {
133 b->action[a]->data.any.c = c;
134
135 if (b->action[a]->func == action_move) {
136 b->action[a]->data.move.x = cx + dx;
137 b->action[a]->data.move.y = cy + dy;
138 b->action[a]->data.move.final = final;
139 } else if (b->action[a]->func == action_resize) {
140 b->action[a]->data.resize.corner = corner;
141 switch (corner) {
142 case Corner_TopLeft:
143 b->action[a]->data.resize.x = cw + dx;
144 b->action[a]->data.resize.y = ch + dy;
145 break;
146 case Corner_TopRight:
147 b->action[a]->data.resize.x = cw - dx;
148 b->action[a]->data.resize.y = ch + dy;
149 break;
150 case Corner_BottomLeft:
151 b->action[a]->data.resize.x = cw + dx;
152 b->action[a]->data.resize.y = ch - dy;
153 break;
154 case Corner_BottomRight:
155 b->action[a]->data.resize.x = cw - dx;
156 b->action[a]->data.resize.y = ch - dy;
157 break;
158 }
159 b->action[a]->data.resize.final = final;
160 }
161 b->action[a]->func(&b->action[a]->data);
162 }
163 }
164
165 static Corner pick_corner(int x, int y, int cx, int cy, int cw, int ch)
166 {
167 if (x - cx < cw / 2) {
168 if (y - cy < ch / 2)
169 return Corner_BottomRight;
170 else
171 return Corner_TopRight;
172 } else {
173 if (y - cy < ch / 2)
174 return Corner_BottomLeft;
175 else
176 return Corner_TopLeft;
177 }
178 }
179
180 static void event(ObEvent *e, void *foo)
181 {
182 static Time ltime;
183 static int px, py, cx, cy, cw, ch, dx, dy;
184 static guint button = 0, lbutton = 0;
185 static gboolean drag = FALSE;
186 static Corner corner = Corner_TopLeft;
187 gboolean click = FALSE;
188 gboolean dclick = FALSE;
189 GQuark context;
190
191 switch (e->type) {
192 case Event_Client_Mapped:
193 grab_for_client(e->data.c.client, TRUE);
194 break;
195
196 case Event_Client_Destroy:
197 grab_for_client(e->data.c.client, FALSE);
198 break;
199
200 case Event_X_ButtonPress:
201 if (!button) {
202 if (e->data.x.client != NULL) {
203 cx = e->data.x.client->frame->area.x;
204 cy = e->data.x.client->frame->area.y;
205 cw = e->data.x.client->frame->area.width;
206 ch = e->data.x.client->frame->area.height;
207 px = e->data.x.e->xbutton.x_root;
208 py = e->data.x.e->xbutton.y_root;
209 corner = pick_corner(px, py, cx, cy, cw, ch);
210 }
211 button = e->data.x.e->xbutton.button;
212 }
213 context = engine_get_context(e->data.x.client,
214 e->data.x.e->xbutton.window);
215
216 fire_button(MouseAction_Press, context,
217 e->data.x.client, e->data.x.e->xbutton.state,
218 e->data.x.e->xbutton.button);
219
220 if (context == g_quark_try_string("client")) {
221 /* Replay the event, so it goes to the client*/
222 XAllowEvents(ob_display, ReplayPointer, CurrentTime);
223 /* Fall through to the release case! */
224 } else
225 break;
226
227 case Event_X_ButtonRelease:
228 context = engine_get_context(e->data.x.client,
229 e->data.x.e->xbutton.window);
230 if (e->data.x.e->xbutton.button == button) {
231 /* end drags */
232 if (drag) {
233 fire_motion(MouseAction_Motion, context,
234 e->data.x.client, e->data.x.e->xbutton.state,
235 e->data.x.e->xbutton.button,
236 cx, cy, cw, ch, dx, dy, TRUE, corner);
237 drag = FALSE;
238
239 lbutton = 0;
240 } else {
241 /* clicks are only valid if its released over the window */
242 if (e->data.x.e->xbutton.x >= 0 &&
243 e->data.x.e->xbutton.y >= 0) {
244 int junk;
245 Window wjunk;
246 guint ujunk, w, h;
247 XGetGeometry(ob_display, e->data.x.e->xbutton.window,
248 &wjunk, &junk, &junk, &w, &h, &ujunk, &ujunk);
249 if (e->data.x.e->xbutton.x < (signed)w &&
250 e->data.x.e->xbutton.y < (signed)h) {
251 click =TRUE;
252 /* double clicks happen if there were 2 in a row! */
253 if (lbutton == button &&
254 e->data.x.e->xbutton.time - 300 <= ltime)
255 dclick = TRUE;
256 }
257 lbutton = button;
258 } else
259 lbutton = 0;
260 }
261
262 button = 0;
263 ltime = e->data.x.e->xbutton.time;
264 }
265 fire_button(MouseAction_Press, context,
266 e->data.x.client, e->data.x.e->xbutton.state,
267 e->data.x.e->xbutton.button);
268 if (click)
269 fire_button(MouseAction_Click, context,
270 e->data.x.client, e->data.x.e->xbutton.state,
271 e->data.x.e->xbutton.button);
272 if (dclick)
273 fire_button(MouseAction_DClick, context,
274 e->data.x.client, e->data.x.e->xbutton.state,
275 e->data.x.e->xbutton.button);
276 break;
277
278 case Event_X_MotionNotify:
279 if (button) {
280 dx = e->data.x.e->xmotion.x_root - px;
281 dy = e->data.x.e->xmotion.y_root - py;
282 if (ABS(dx) >= drag_threshold || ABS(dy) >= drag_threshold)
283 drag = TRUE;
284 if (drag) {
285 context = engine_get_context(e->data.x.client,
286 e->data.x.e->xbutton.window);
287 fire_motion(MouseAction_Motion, context,
288 e->data.x.client, e->data.x.e->xmotion.state,
289 button, cx, cy, cw, ch, dx, dy, FALSE, corner);
290 }
291 }
292 break;
293
294 default:
295 g_assert_not_reached();
296 }
297 }
298
299 gboolean mbind(char *buttonstr, char *contextstr, MouseAction mact,
300 Action *action)
301 {
302 guint state, button;
303 GQuark context;
304 MouseBinding *b;
305 GSList *it;
306
307 if (!translate_button(buttonstr, &state, &button)) {
308 g_warning("invalid button '%s'", buttonstr);
309 return FALSE;
310 }
311
312 contextstr = g_ascii_strdown(contextstr, -1);
313 context = g_quark_try_string(contextstr);
314 if (!context) {
315 g_warning("invalid context '%s'", contextstr);
316 g_free(contextstr);
317 return FALSE;
318 }
319 g_free(contextstr);
320
321 for (it = g_datalist_id_get_data(&bound_contexts, context);
322 it != NULL; it = it->next){
323 b = it->data;
324 if (b->state == state && b->button == button) {
325 /* already bound */
326 if (b->action[mact] != NULL) {
327 g_warning("duplicate binding");
328 return FALSE;
329 }
330 b->action[mact] = action;
331 return TRUE;
332 }
333 }
334
335 grab_all_clients(FALSE);
336
337 /* add the binding */
338 b = g_new0(MouseBinding, 1);
339 b->state = state;
340 b->button = button;
341 b->action[mact] = action;
342 g_datalist_id_set_data(&bound_contexts, context,
343 g_slist_append(g_datalist_id_get_data(&bound_contexts, context), b));
344
345 grab_all_clients(TRUE);
346
347 return TRUE;
348 }
349
350 void plugin_startup()
351 {
352 dispatch_register(Event_Client_Mapped | Event_Client_Destroy |
353 Event_X_ButtonPress | Event_X_ButtonRelease |
354 Event_X_MotionNotify, (EventHandler)event, NULL);
355
356 mouserc_parse();
357 }
358
359 void plugin_shutdown()
360 {
361 dispatch_register(0, (EventHandler)event, NULL);
362
363 grab_all_clients(FALSE);
364 g_datalist_foreach(&bound_contexts, foreach_clear, NULL);
365 }
This page took 0.047384 seconds and 3 git commands to generate.