]> git.decadent.org.uk Git - ion3.git/blob - ioncore/pointer.c
671af84835cce79f6b4bed427ec111b4df3e25e3
[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, "oo", NULL, p_reg, p_subreg);
141     p_curr_event=NULL;
142 }
143
144
145 static void call_motion(XMotionEvent *ev, int dx, int dy)
146 {
147     if(p_motion_handler!=NULL && p_reg!=NULL){
148         p_curr_event=(XEvent*)ev;
149         p_motion_handler(p_reg, ev, dx, dy);
150         p_curr_event=NULL;
151     }
152 }
153
154
155 static void call_motion_end(XButtonEvent *ev)
156 {
157     if(p_motion_end_handler!=NULL && p_reg!=NULL){
158         p_curr_event=(XEvent*)ev;
159         p_motion_end_handler(p_reg, ev);
160         p_curr_event=NULL;
161     }
162 }
163
164
165 static void call_motion_begin(WBinding *binding, XMotionEvent *ev,
166                               int dx, int dy)
167 {
168     WMotionHandler *fn;
169     
170     if(binding==NULL)
171         return;
172
173     p_curr_event=(XEvent*)ev;
174     
175     extl_call(binding->func, "oo", NULL, p_reg, p_subreg);
176     
177     if(p_motion_begin_handler!=NULL && p_reg!=NULL)
178         p_motion_begin_handler(p_reg, ev, dx, dy);
179     
180     p_motion_begin_handler=NULL;
181     
182     p_curr_event=NULL;
183 }
184
185
186 /*}}}*/
187
188
189 /*{{{ ioncore_handle_button_press/release/motion */
190
191
192 static void finish_pointer()
193 {
194     if(p_reg!=NULL)
195         window_release((WWindow*)p_reg);
196     p_grabstate=ST_NO;
197     watch_reset(&p_subregwatch);
198 }
199
200
201 static bool handle_key(WRegion *reg, XEvent *ev)
202 {
203     if(p_key_handler!=NULL){
204         if(p_key_handler(reg, ev)){
205             finish_pointer();
206             return TRUE;
207         }
208     }
209     return FALSE;
210 }
211
212
213 static void pointer_grab_killed(WRegion *unused)
214 {
215     if(p_reg!=NULL && p_killed_handler!=NULL)
216         p_killed_handler(p_reg);
217     watch_reset(&p_regwatch);
218     finish_pointer();
219 }
220
221
222 bool ioncore_do_handle_buttonpress(XButtonEvent *ev)
223 {
224     WBinding *pressbind=NULL;
225     WRegion *reg=NULL;
226     WRegion *subreg=NULL;
227     uint button, state;
228     bool dblclick;
229     
230     state=ev->state;
231     button=ev->button;
232     
233     reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow);
234     
235     if(reg==NULL)
236         return FALSE;
237
238     if(ev->subwindow!=None){
239         XButtonEvent ev2=*ev;
240         ev2.window=ev->subwindow;
241         if(XTranslateCoordinates(ioncore_g.dpy, ev->window, ev2.window,
242                                  ev->x, ev->y, &(ev2.x), &(ev2.y),
243                                  &(ev2.subwindow))){
244             if(ioncore_do_handle_buttonpress(&ev2))
245                 return TRUE;
246         }
247     }
248
249     dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) && 
250               p_button==button && p_state==state && reg==p_reg);
251     
252     p_motion=FALSE;
253     p_motion_begin_handler=NULL;
254     p_motion_handler=NULL;
255     p_motion_end_handler=NULL;
256     p_key_handler=NULL;
257     p_killed_handler=NULL;
258     p_grabstate=ST_INIT;
259     p_button=button;
260     p_state=state;
261     p_orig_x=p_x=ev->x_root;
262     p_orig_y=p_y=ev->y_root;
263     p_time=ev->time;
264     p_clickcnt=0;
265
266     watch_setup(&p_regwatch, (Obj*)reg, NULL);
267     
268     subreg=region_current(reg);
269     p_area=window_press((WWindow*)reg, ev, &subreg);
270     if(subreg!=NULL)
271         watch_setup(&p_subregwatch, (Obj*)subreg, NULL);
272
273     if(dblclick){
274         pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state,
275                                              button, p_area);
276     }
277     
278     if(pressbind==NULL){
279         pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state,
280                                              button, p_area);
281     }
282     
283     ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0);
284     p_grabstate=ST_HELD;
285     
286     call_button(pressbind, ev);
287     
288     return TRUE;
289 }
290
291
292 bool ioncore_do_handle_buttonrelease(XButtonEvent *ev)
293 {
294     WBinding *binding=NULL;
295     
296     if(p_button!=ev->button)
297            return FALSE;
298
299     if(p_reg!=NULL){
300         if(p_motion==FALSE){
301             p_clickcnt=1;
302             binding=region_lookup_binding(p_reg, BINDING_BUTTONCLICK,
303                                                p_state, p_button, p_area);
304             call_button(binding, ev);
305         }else{
306             call_motion_end(ev);
307         }
308         
309     }
310     
311     ioncore_grab_remove(handle_key);
312     finish_pointer();
313     
314     return TRUE;
315 }
316
317
318 void ioncore_do_handle_motionnotify(XMotionEvent *ev)
319 {
320     int dx, dy;
321     WBinding *binding=NULL;
322     
323     if(p_reg==NULL)
324         return;
325     
326     if(!p_motion){
327         if(motion_in_threshold(ev->x_root, ev->y_root))
328             return;
329         binding=region_lookup_binding(p_reg, BINDING_BUTTONMOTION,
330                                            p_state, p_button, p_area);
331     }
332     
333     p_time=ev->time;
334     dx=ev->x_root-p_x;
335     dy=ev->y_root-p_y;
336     p_x=ev->x_root;
337     p_y=ev->y_root;    
338     
339     if(!p_motion){
340         call_motion_begin(binding, ev, dx, dy);
341         p_motion=TRUE;
342     }else{
343         call_motion(ev, dx, dy);
344     }
345 }
346
347
348 /*}}}*/
349