]> git.decadent.org.uk Git - ion3.git/blob - ioncore/pointer.c
f1dc9e763c8a4bd5b900568c4f42f6526b0b93f5
[ion3.git] / ioncore / pointer.c
1 /*
2  * ion/ioncore/pointer.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * Ion is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include "common.h"
13 #include "pointer.h"
14 #include "cursor.h"
15 #include "event.h"
16 #include "global.h"
17 #include "focus.h"
18 #include "regbind.h"
19 #include "grab.h"
20 #include "xwindow.h"
21
22
23 /*{{{ Variables */
24
25
26 static uint p_button=0, p_state=0;
27 static int p_x=-1, p_y=-1;
28 static int p_orig_x=-1, p_orig_y=-1;
29 static bool p_motion=FALSE;
30 static int p_clickcnt=0;
31 static Time p_time=0;
32 static int p_area=0;
33 static enum{ST_NO, ST_INIT, ST_HELD} p_grabstate=ST_NO;
34
35 static WButtonHandler *p_motion_end_handler=NULL;
36 static WMotionHandler *p_motion_handler=NULL;
37 static WMotionHandler *p_motion_begin_handler=NULL;
38 static GrabHandler *p_key_handler=NULL;
39 static GrabKilledHandler *p_killed_handler=NULL;
40
41 static Watch p_regwatch=WATCH_INIT, p_subregwatch=WATCH_INIT;
42
43 #define p_reg ((WRegion*)p_regwatch.obj)
44 #define p_subreg ((WRegion*)p_subregwatch.obj)
45
46
47 /*}}}*/
48
49
50 /*{{{ Handler setup */
51
52
53 bool ioncore_set_drag_handlers(WRegion *reg,
54                          WMotionHandler *begin,
55                          WMotionHandler *motion,
56                          WButtonHandler *end,
57                          GrabHandler *keypress,
58                          GrabKilledHandler *killed)
59 {
60     if(ioncore_pointer_grab_region()==NULL || p_motion)
61         return FALSE;
62     
63     /* A motion handler set at this point may not set a begin handler */
64     if(p_grabstate!=ST_HELD && begin!=NULL)
65         return FALSE;
66     
67     if(p_reg!=reg){
68         watch_setup(&p_regwatch, (Obj*)reg, NULL);
69         watch_reset(&p_subregwatch);
70     }
71     
72     p_motion_begin_handler=begin;
73     p_motion_handler=motion;
74     p_motion_end_handler=end;
75     p_key_handler=keypress;
76     p_killed_handler=killed;
77     p_motion=TRUE;
78     
79     return TRUE;
80 }
81
82
83 /*}}}*/
84
85
86 /*{{{ Misc. */
87
88
89 static bool time_in_threshold(Time time)
90 {
91     Time t;
92     
93     if(time<p_time)
94         t=p_time-time;
95     else
96         t=time-p_time;
97     
98     return t<ioncore_g.dblclick_delay;
99 }
100
101
102 static bool motion_in_threshold(int x, int y)
103 {
104     return (x>p_x-CF_DRAG_TRESHOLD && x<p_x+CF_DRAG_TRESHOLD &&
105             y>p_y-CF_DRAG_TRESHOLD && y<p_y+CF_DRAG_TRESHOLD);
106 }
107
108
109 WRegion *ioncore_pointer_grab_region()
110 {
111     if(p_grabstate==ST_NO)
112         return NULL;
113     return p_reg;
114 }
115
116
117 /*}}}*/
118
119
120 /*{{{ Call handlers */
121
122
123 static XEvent *p_curr_event=NULL;
124
125
126 XEvent *ioncore_current_pointer_event()
127 {
128     return p_curr_event;
129 }
130
131
132 static void call_button(WBinding *binding, XButtonEvent *ev)
133 {
134     WButtonHandler *fn;
135
136     if(binding==NULL)
137         return;
138
139     p_curr_event=(XEvent*)ev;
140     extl_call(binding->func, "ooo", NULL, p_reg, p_subreg, 
141               (p_reg!=NULL ? p_reg->active_sub : NULL));
142     p_curr_event=NULL;
143 }
144
145
146 static void call_motion(XMotionEvent *ev, int dx, int dy)
147 {
148     if(p_motion_handler!=NULL && p_reg!=NULL){
149         p_curr_event=(XEvent*)ev;
150         p_motion_handler(p_reg, ev, dx, dy);
151         p_curr_event=NULL;
152     }
153 }
154
155
156 static void call_motion_end(XButtonEvent *ev)
157 {
158     if(p_motion_end_handler!=NULL && p_reg!=NULL){
159         p_curr_event=(XEvent*)ev;
160         p_motion_end_handler(p_reg, ev);
161         p_curr_event=NULL;
162     }
163 }
164
165
166 static void call_motion_begin(WBinding *binding, XMotionEvent *ev,
167                               int dx, int dy)
168 {
169     WMotionHandler *fn;
170     
171     if(binding==NULL)
172         return;
173
174     p_curr_event=(XEvent*)ev;
175     
176     extl_call(binding->func, "oo", NULL, p_reg, p_subreg);
177     
178     if(p_motion_begin_handler!=NULL && p_reg!=NULL)
179         p_motion_begin_handler(p_reg, ev, dx, dy);
180     
181     p_motion_begin_handler=NULL;
182     
183     p_curr_event=NULL;
184 }
185
186
187 /*}}}*/
188
189
190 /*{{{ ioncore_handle_button_press/release/motion */
191
192
193 static void finish_pointer()
194 {
195     if(p_reg!=NULL)
196         window_release((WWindow*)p_reg);
197     p_grabstate=ST_NO;
198     watch_reset(&p_subregwatch);
199 }
200
201
202 static bool handle_key(WRegion *reg, XEvent *ev)
203 {
204     if(p_key_handler!=NULL){
205         if(p_key_handler(reg, ev)){
206             finish_pointer();
207             return TRUE;
208         }
209     }
210     return FALSE;
211 }
212
213
214 static void pointer_grab_killed(WRegion *unused)
215 {
216     if(p_reg!=NULL && p_killed_handler!=NULL)
217         p_killed_handler(p_reg);
218     watch_reset(&p_regwatch);
219     finish_pointer();
220 }
221
222
223 bool ioncore_do_handle_buttonpress(XButtonEvent *ev)
224 {
225     WBinding *pressbind=NULL;
226     WRegion *reg=NULL;
227     WRegion *subreg=NULL;
228     uint button, state;
229     bool dblclick;
230     
231     state=ev->state;
232     button=ev->button;
233     
234     reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow);
235     
236     if(reg==NULL)
237         return FALSE;
238
239     if(ev->subwindow!=None){
240         XButtonEvent ev2=*ev;
241         ev2.window=ev->subwindow;
242         if(XTranslateCoordinates(ioncore_g.dpy, ev->window, ev2.window,
243                                  ev->x, ev->y, &(ev2.x), &(ev2.y),
244                                  &(ev2.subwindow))){
245             if(ioncore_do_handle_buttonpress(&ev2))
246                 return TRUE;
247         }
248     }
249
250     dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) && 
251               p_button==button && p_state==state && reg==p_reg);
252     
253     p_motion=FALSE;
254     p_motion_begin_handler=NULL;
255     p_motion_handler=NULL;
256     p_motion_end_handler=NULL;
257     p_key_handler=NULL;
258     p_killed_handler=NULL;
259     p_grabstate=ST_INIT;
260     p_button=button;
261     p_state=state;
262     p_orig_x=p_x=ev->x_root;
263     p_orig_y=p_y=ev->y_root;
264     p_time=ev->time;
265     p_clickcnt=0;
266
267     watch_setup(&p_regwatch, (Obj*)reg, NULL);
268     
269     subreg=region_current(reg);
270     p_area=window_press((WWindow*)reg, ev, &subreg);
271     if(subreg!=NULL)
272         watch_setup(&p_subregwatch, (Obj*)subreg, NULL);
273
274     if(dblclick){
275         pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state,
276                                              button, p_area);
277     }
278     
279     if(pressbind==NULL){
280         pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state,
281                                              button, p_area);
282     }
283     
284     ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0);
285     p_grabstate=ST_HELD;
286     
287     call_button(pressbind, ev);
288     
289     return TRUE;
290 }
291
292
293 bool ioncore_do_handle_buttonrelease(XButtonEvent *ev)
294 {
295     WBinding *binding=NULL;
296     
297     if(p_button!=ev->button)
298            return FALSE;
299
300     if(p_reg!=NULL){
301         if(p_motion==FALSE){
302             p_clickcnt=1;
303             binding=region_lookup_binding(p_reg, BINDING_BUTTONCLICK,
304                                                p_state, p_button, p_area);
305             call_button(binding, ev);
306         }else{
307             call_motion_end(ev);
308         }
309         
310     }
311     
312     ioncore_grab_remove(handle_key);
313     finish_pointer();
314     
315     return TRUE;
316 }
317
318
319 void ioncore_do_handle_motionnotify(XMotionEvent *ev)
320 {
321     int dx, dy;
322     WBinding *binding=NULL;
323     
324     if(p_reg==NULL)
325         return;
326     
327     if(!p_motion){
328         if(motion_in_threshold(ev->x_root, ev->y_root))
329             return;
330         binding=region_lookup_binding(p_reg, BINDING_BUTTONMOTION,
331                                            p_state, p_button, p_area);
332     }
333     
334     p_time=ev->time;
335     dx=ev->x_root-p_x;
336     dy=ev->y_root-p_y;
337     p_x=ev->x_root;
338     p_y=ev->y_root;    
339     
340     if(!p_motion){
341         call_motion_begin(binding, ev, dx, dy);
342         p_motion=TRUE;
343     }else{
344         call_motion(ev, dx, dy);
345     }
346 }
347
348
349 /*}}}*/
350