2 * ion/ioncore/pointer.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
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;
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)
47 /*{{{ Handler setup */
50 bool ioncore_set_drag_handlers(WRegion *reg,
51 WMotionHandler *begin,
52 WMotionHandler *motion,
54 GrabHandler *keypress,
55 GrabKilledHandler *killed)
57 if(ioncore_pointer_grab_region()==NULL || p_motion)
60 /* A motion handler set at this point may not set a begin handler */
61 if(p_grabstate!=ST_HELD && begin!=NULL)
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;
86 static bool time_in_threshold(Time 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)
117 /*{{{ Call handlers */
120 static XEvent *p_curr_event=NULL;
123 XEvent *ioncore_current_pointer_event()
129 static void call_button(WBinding *binding, XButtonEvent *ev)
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));
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);
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);
163 static void call_motion_begin(WBinding *binding, XMotionEvent *ev,
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;
187 /*{{{ ioncore_handle_button_press/release/motion */
190 static void finish_pointer()
193 window_release((WWindow*)p_reg);
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)){
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);
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;
227 for(i=0; i<n_acts; i++){
228 if(region_lookup_binding(reg, acts[i], state, button, area))
236 static bool ioncore_dodo_handle_buttonpress(XButtonEvent *ev, bool sub)
238 WBinding *pressbind=NULL;
240 WRegion *subreg=NULL;
248 reg=(WRegion*)XWINDOW_REGION_OF_T(ev->window, WWindow);
253 dblclick=(p_clickcnt==1 && time_in_threshold(ev->time) &&
254 p_button==button && p_state==state);
256 if(dblclick && p_reg!=reg){
262 subreg=region_current(reg);
263 area=window_press((WWindow*)reg, ev, &subreg);
266 pressbind=region_lookup_binding(reg, BINDING_BUTTONDBLCLICK, state,
271 pressbind=region_lookup_binding(reg, BINDING_BUTTONPRESS, state,
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))
285 p_motion_begin_handler=NULL;
286 p_motion_handler=NULL;
287 p_motion_end_handler=NULL;
289 p_killed_handler=NULL;
293 p_orig_x=p_x=ev->x_root;
294 p_orig_y=p_y=ev->y_root;
299 watch_setup(&p_regwatch, (Obj*)reg, NULL);
301 watch_setup(&p_subregwatch, (Obj*)subreg, NULL);
303 ioncore_grab_establish(reg, handle_key, pointer_grab_killed, 0);
307 call_button(pressbind, ev);
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;
323 if(XTranslateCoordinates(ioncore_g.dpy, ev->window, ev2.window,
324 ev->x, ev->y, &(ev2.x), &(ev2.y),
326 if(ioncore_dodo_handle_buttonpress(&ev2, 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)
345 binding=region_lookup_binding(p_reg, BINDING_BUTTONCLICK,
346 p_state, p_button, p_area);
348 call_button(binding, ev);
355 ioncore_grab_remove(handle_key);
362 void ioncore_do_handle_motionnotify(XMotionEvent *ev)
365 WBinding *binding=NULL;
371 if(motion_in_threshold(ev->x_root, ev->y_root))
373 binding=region_lookup_binding(p_reg, BINDING_BUTTONMOTION,
374 p_state, p_button, p_area);
384 call_motion_begin(binding, ev, dx, dy);
387 call_motion(ev, dx, dy);