4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
11 #include <libtu/objp.h>
12 #include <libextl/extl.h>
13 #include <libmainloop/defer.h>
27 static void waitrelease(WRegion
*reg
);
28 static void submapgrab(WRegion
*reg
);
31 static void insstr(WWindow
*wwin
, XKeyEvent
*ev
)
33 static XComposeStatus cs
={NULL
, 0};
40 if(XFilterEvent((XEvent
*)ev
, ev
->window
))
42 n
=XmbLookupString(wwin
->xic
, ev
, buf
, 16, &ksym
, &stat
);
43 if(stat
!=XLookupChars
&& stat
!=XLookupBoth
)
46 n
=XLookupString(ev
, buf
, 32, &ksym
, &cs
);
52 /* Won't catch bad strings, but should filter out most crap. */
54 if(!iswprint(str_wchar_at(buf
, 32)))
61 window_insstr(wwin
, buf
, n
);
65 static void send_key(XEvent
*ev
, WClientWin
*cwin
)
69 ev
->xkey
.subwindow
=None
;
70 XSendEvent(ioncore_g
.dpy
, win
, False
, KeyPressMask
, ev
);
74 static bool quote_next_handler(WRegion
*reg
, XEvent
*xev
)
76 XKeyEvent
*ev
=&xev
->xkey
;
77 if(ev
->type
!=KeyPress
)
79 if(ioncore_ismod(ev
->keycode
))
81 assert(OBJ_IS(reg
, WClientWin
));
82 send_key(xev
, (WClientWin
*)reg
);
83 return TRUE
; /* remove the grab */
88 * Send next key press directly to \var{cwin}.
91 void clientwin_quote_next(WClientWin
*cwin
)
93 ioncore_grab_establish((WRegion
*)cwin
, quote_next_handler
, NULL
, 0);
94 ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY
);
98 static bool waitrelease_handler(WRegion
*reg
, XEvent
*ev
)
100 return (ioncore_unmod(ev
->xkey
.state
, ev
->xkey
.keycode
)==0);
104 static void waitrelease(WRegion
*reg
)
106 if(ioncore_modstate()==0)
109 /* We need to grab on the root window as <reg> might have been
110 * ioncore_defer_destroy:ed by the binding handler (the most common case
111 * for using this kpress_wait!). In such a case the grab may
112 * be removed before the modifiers are released.
114 ioncore_grab_establish((WRegion
*)region_rootwin_of(reg
),
117 ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY
);
121 static void free_sub(WSubmapState
*p
)
123 /*extl_unref_fn(p->leave);
124 watch_reset(&p->leave_reg);
131 void region_free_submapstat(WRegion
*reg
)
133 while(reg
->submapstat
!=NULL
){
134 WSubmapState
*p
=reg
->submapstat
;
135 reg
->submapstat
=p
->next
;
141 WHook
*ioncore_submap_ungrab_hook
=NULL
;
144 static void call_submap_ungrab_hook()
146 hook_call_v(ioncore_submap_ungrab_hook
);
150 static void clear_subs(WRegion
*reg
)
152 region_free_submapstat(reg
);
153 mainloop_defer_action(NULL
, (WDeferredAction
*)call_submap_ungrab_hook
);
155 while(reg!=NULL && reg->submapstat!=NULL){
156 WSubmapState *p=reg->submapstat;
157 reg->submapstat=p->next;
159 if(p->leave!=extl_fn_none() && p->leave_reg.obj!=NULL){
160 Watch regw=WATCH_INIT;
162 watch_setup(®w, (Obj*)reg, NULL);
164 extl_call(p->leave, "o", NULL, p->leave_reg.obj);
166 reg=(WRegion*)regw.obj;
177 static WSubmapState
*add_sub(WRegion
*reg
, uint key
, uint state
)
182 if(reg
->submapstat
==NULL
){
183 p
=&(reg
->submapstat
);
191 s
=ALLOC(WSubmapState
);
198 /*s->leave=extl_fn_none();
199 watch_init(&s->leave_reg);*/
208 static XKeyEvent
*current_key_event
=NULL
;
209 static uint current_kcb
, current_state
;
210 static bool current_submap
;
212 /* Note: state set to AnyModifier for submaps */
213 bool ioncore_current_key(uint
*kcb
, uint
*state
, bool *sub
)
219 *state
=current_state
;
226 enum{GRAB_NONE
, GRAB_NONE_SUBMAP
, GRAB_SUBMAP
, GRAB_WAITRELEASE
};
229 static WBinding
*lookup_binding_(WRegion
*reg
,
230 int act
, uint state
, uint kcb
,
232 WRegion
**binding_owner
, WRegion
**subreg
)
239 binding
=region_lookup_keybinding(reg
, act
, state
, kcb
, st
,
245 if(OBJ_IS(reg
, WRootWin
))
249 reg
=REGION_PARENT_REG(reg
);
255 static WBinding
*lookup_binding(WRegion
*oreg
,
256 int act
, uint state
, uint kcb
,
257 WRegion
**binding_owner
, WRegion
**subreg
)
261 /* Find the deepest nested active window grabbing this key. */
262 while(reg
->active_sub
!=NULL
)
265 return lookup_binding_(reg
, act
, state
, kcb
, oreg
->submapstat
,
266 binding_owner
, subreg
);
270 static void do_call_binding(WBinding
*binding
, WRegion
*reg
, WRegion
*subreg
)
272 WRegion
*mgd
=region_managed_within(reg
, subreg
);
274 /* TODO: having to pass both mgd and subreg for some handlers
275 * to work is ugly and complex.
277 extl_call(binding
->func
, "ooo", NULL
, reg
, mgd
, subreg
);
281 static int do_key(WRegion
*oreg
, XKeyEvent
*ev
)
283 WBinding
*binding
=NULL
;
284 WRegion
*binding_owner
=NULL
, *subreg
=NULL
;
285 bool grabbed
=(oreg
->flags
®ION_BINDINGS_ARE_GRABBED
);
289 binding
=lookup_binding(oreg
, BINDING_KEYPRESS
, ev
->state
, ev
->keycode
,
290 &binding_owner
, &subreg
);
292 binding
=region_lookup_keybinding(oreg
, BINDING_KEYPRESS
,
293 ev
->state
, ev
->keycode
,
299 bool subs
=(oreg
->submapstat
!=NULL
);
302 if(binding
->submap
!=NULL
){
303 WSubmapState
*s
=add_sub(oreg
, ev
->keycode
, ev
->state
);
305 /*WRegion *own2, *subreg2;
307 call=lookup_binding(binding_owner, BINDING_SUBMAP_LEAVE, 0, 0,
308 oreg->submapstat, &own2, &subreg2);
311 s->leave=extl_ref_fn(call->func);
312 watch_setup(&s->leave_reg, (Obj*)own2, NULL);
315 call
=lookup_binding_(binding_owner
, BINDING_SUBMAP_ENTER
, 0, 0,
317 &binding_owner
, &subreg
);
319 ret
=(grabbed
? GRAB_SUBMAP
: GRAB_NONE_SUBMAP
);
325 XUngrabKeyboard(ioncore_g
.dpy
, CurrentTime
);
327 if(ev
->state
!=0 && !subs
&& binding
->wait
)
328 ret
=GRAB_WAITRELEASE
;
332 current_kcb
=ev
->keycode
;
333 current_state
=ev
->state
;
336 do_call_binding(call
, binding_owner
, subreg
);
340 }else if(oreg
->submapstat
==NULL
&& OBJ_IS(oreg
, WWindow
)){
341 insstr((WWindow
*)oreg
, ev
);
348 static bool submapgrab_handler(WRegion
* reg
, XEvent
*xev
)
350 XKeyEvent
*ev
=&xev
->xkey
;
351 if(ev
->type
!=KeyPress
){
352 if(ioncore_unmod(ev
->state
, ev
->keycode
)==0){
354 WRegion
*binding_owner
, *subreg
;
356 binding
=lookup_binding(reg
,
357 BINDING_SUBMAP_RELEASEMOD
, 0, 0,
358 &binding_owner
, &subreg
);
361 do_call_binding(binding
, binding_owner
, subreg
);
366 if(ioncore_ismod(ev
->keycode
))
368 if(do_key(reg
, ev
)!=GRAB_SUBMAP
){
379 static void submapgrab(WRegion
*reg
)
381 ioncore_grab_establish(reg
, submapgrab_handler
, clear_subs
, 0);
382 ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY
);
386 void ioncore_do_handle_keypress(XKeyEvent
*ev
)
388 WRegion
*reg
=(WRegion
*)XWINDOW_REGION_OF(ev
->window
);
394 /* reg might be destroyed by binding handlers */
395 watch_setup(&w
, (Obj
*)reg
, NULL
);
397 grab
=do_key(reg
, ev
);
402 if(grab
==GRAB_SUBMAP
)
404 else if(grab
==GRAB_WAITRELEASE
)
406 else if(grab
==GRAB_NONE_SUBMAP
)
408 else if(grab
==GRAB_NONE
&& reg
->submapstat
!=NULL
)