Released version 3-2014010505
[notion.git] / ioncore / pointer.c
blob8b57bcb90fabfe0dc1e8efedaec4fe4b15be6eac
1 /*
2 * ion/ioncore/pointer.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
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"
20 /*{{{ Variables */
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;
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;
38 static Watch p_regwatch=WATCH_INIT, p_subregwatch=WATCH_INIT;
40 #define p_reg ((WRegion*)p_regwatch.obj)
41 #define p_subreg ((WRegion*)p_subregwatch.obj)
44 /*}}}*/
47 /*{{{ Handler setup */
50 bool ioncore_set_drag_handlers(WRegion *reg,
51 WMotionHandler *begin,
52 WMotionHandler *motion,
53 WButtonHandler *end,
54 GrabHandler *keypress,
55 GrabKilledHandler *killed)
57 if(ioncore_pointer_grab_region()==NULL || p_motion)
58 return FALSE;
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;
64 if(p_reg!=reg){
65 watch_setup(&p_regwatch, (Obj*)reg, NULL);
66 watch_reset(&p_subregwatch);
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;
76 return TRUE;
80 /*}}}*/
83 /*{{{ Misc. */
86 static bool time_in_threshold(Time time)
88 Time t;
90 if(time<p_time)
91 t=p_time-time;
92 else
93 t=time-p_time;
95 return t<ioncore_g.dblclick_delay;
99 static bool motion_in_threshold(int x, int y)
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);
106 WRegion *ioncore_pointer_grab_region()
108 if(p_grabstate==ST_NO)
109 return NULL;
110 return p_reg;
114 /*}}}*/
117 /*{{{ Call handlers */
120 static XEvent *p_curr_event=NULL;
123 XEvent *ioncore_current_pointer_event()
125 return p_curr_event;
129 static void call_button(WBinding *binding, XButtonEvent *ev)
131 WButtonHandler *fn;
133 if(binding==NULL)
134 return;
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;
143 static void call_motion(XMotionEvent *ev, int dx, int dy)
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;
153 static void call_motion_end(XButtonEvent *ev)
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;
163 static void call_motion_begin(WBinding *binding, XMotionEvent *ev,
164 int dx, int dy)
166 WMotionHandler *fn;
168 if(binding==NULL)
169 return;
171 p_curr_event=(XEvent*)ev;
173 extl_call(binding->func, "oo", NULL, p_reg, p_subreg);
175 if(p_motion_begin_handler!=NULL && p_reg!=NULL)
176 p_motion_begin_handler(p_reg, ev, dx, dy);
178 p_motion_begin_handler=NULL;
180 p_curr_event=NULL;
184 /*}}}*/
187 /*{{{ ioncore_handle_button_press/release/motion */
190 static void finish_pointer()
192 if(p_reg!=NULL)
193 window_release((WWindow*)p_reg);
194 p_grabstate=ST_NO;
195 watch_reset(&p_subregwatch);
199 static bool handle_key(WRegion *reg, XEvent *ev)
201 if(p_key_handler!=NULL){
202 if(p_key_handler(reg, ev)){
203 finish_pointer();
204 return TRUE;
207 return FALSE;
211 static void pointer_grab_killed(WRegion *unused)
213 if(p_reg!=NULL && p_killed_handler!=NULL)
214 p_killed_handler(p_reg);
215 watch_reset(&p_regwatch);
216 finish_pointer();
220 static bool listens_to(WRegion *reg, uint state, uint button, int area)
222 static const int acts[]={BINDING_BUTTONMOTION, BINDING_BUTTONCLICK,
223 BINDING_BUTTONDBLCLICK};
224 static const int n_acts=3;
225 int i;
227 for(i=0; i<n_acts; i++){
228 if(region_lookup_binding(reg, acts[i], state, button, area))
229 return TRUE;
232 return FALSE;
236 static bool ioncore_dodo_handle_buttonpress(XButtonEvent *ev, bool sub)
238 WBinding *pressbind=NULL;
239 WRegion *reg=NULL;
240 WRegion *subreg=NULL;
241 uint button, state;
242 bool dblclick;
243 int area;
245 state=ev->state;
246 button=ev->button;
248 reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow);
250 if(reg==NULL)
251 return FALSE;
253 dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) &&
254 p_button==button && p_state==state);
256 if(dblclick && p_reg!=reg){
257 if(sub)
258 return FALSE;
259 dblclick=FALSE;
262 subreg=region_current(reg);
263 area=window_press((WWindow*)reg, ev, &subreg);
265 if(dblclick){
266 pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state,
267 button, area);
270 if(pressbind==NULL){
271 pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state,
272 button, area);
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.
280 if(!dblclick && !listens_to(reg, state, button, area))
281 return FALSE;
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;
299 watch_setup(&p_regwatch, (Obj*)reg, NULL);
300 if(subreg!=NULL)
301 watch_setup(&p_subregwatch, (Obj*)subreg, NULL);
303 ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0);
304 p_grabstate=ST_HELD;
306 if(pressbind!=NULL)
307 call_button(pressbind, ev);
309 return TRUE;
313 bool ioncore_do_handle_buttonpress(XButtonEvent *ev)
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.
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;
331 return ioncore_dodo_handle_buttonpress(ev, FALSE);
335 bool ioncore_do_handle_buttonrelease(XButtonEvent *ev)
337 WBinding *binding=NULL;
339 if(p_button!=ev->button)
340 return FALSE;
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);
355 ioncore_grab_remove(handle_key);
356 finish_pointer();
358 return TRUE;
362 void ioncore_do_handle_motionnotify(XMotionEvent *ev)
364 int dx, dy;
365 WBinding *binding=NULL;
367 if(p_reg==NULL)
368 return;
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);
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;
383 if(!p_motion){
384 call_motion_begin(binding, ev, dx, dy);
385 p_motion=TRUE;
386 }else{
387 call_motion(ev, dx, dy);
392 /*}}}*/