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