]> git.decadent.org.uk Git - ion3.git/blob - ioncore/key.c
Update cfg_kludge_flash for Flash 10
[ion3.git] / ioncore / key.c
1 /*
2  * ion/ioncore/key.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <ctype.h>
10
11 #include <libtu/objp.h>
12 #include <libextl/extl.h>
13 #include <libmainloop/defer.h>
14
15 #include "common.h"
16 #include "key.h"
17 #include "binding.h"
18 #include "global.h"
19 #include "event.h"
20 #include "cursor.h"
21 #include "grab.h"
22 #include "regbind.h"
23 #include "strings.h"
24 #include "xwindow.h"
25
26
27 static void waitrelease(WRegion *reg);
28 static void submapgrab(WRegion *reg);
29
30
31 static void insstr(WWindow *wwin, XKeyEvent *ev)
32 {
33     static XComposeStatus cs={NULL, 0};
34     char buf[32]={0,};
35     Status stat;
36     int n, i;
37     KeySym ksym;
38     
39     if(wwin->xic!=NULL){
40         if(XFilterEvent((XEvent*)ev, ev->window))
41            return;
42         n=XmbLookupString(wwin->xic, ev, buf, 16, &ksym, &stat);
43         if(stat!=XLookupChars && stat!=XLookupBoth)
44             return;
45     }else{
46         n=XLookupString(ev, buf, 32, &ksym, &cs);
47     }
48     
49     if(n<=0)
50         return;
51     
52     /* Won't catch bad strings, but should filter out most crap. */
53     if(ioncore_g.use_mb){
54         if(!iswprint(str_wchar_at(buf, 32)))
55             return;
56     }else{
57         if(iscntrl(*buf))
58             return;
59     }
60     
61     window_insstr(wwin, buf, n);
62 }
63
64
65 static void send_key(XEvent *ev, WClientWin *cwin)
66 {
67     Window win=cwin->win;
68     ev->xkey.window=win;
69     ev->xkey.subwindow=None;
70     XSendEvent(ioncore_g.dpy, win, False, KeyPressMask, ev);
71 }
72
73
74 static bool quote_next_handler(WRegion *reg, XEvent *xev)
75 {
76     XKeyEvent *ev=&xev->xkey;
77     if(ev->type!=KeyPress)
78         return FALSE;
79     if(ioncore_ismod(ev->keycode))
80         return FALSE;
81     assert(OBJ_IS(reg, WClientWin));
82     send_key(xev, (WClientWin*)reg);
83     return TRUE; /* remove the grab */
84 }
85
86
87 /*EXTL_DOC
88  * Send next key press directly to \var{cwin}.
89  */
90 EXTL_EXPORT_MEMBER
91 void clientwin_quote_next(WClientWin *cwin)
92 {
93     ioncore_grab_establish((WRegion*)cwin, quote_next_handler, NULL, 0);
94     ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
95 }
96
97
98 static bool waitrelease_handler(WRegion *reg, XEvent *ev)
99 {
100     return (ioncore_unmod(ev->xkey.state, ev->xkey.keycode)==0);
101 }
102
103
104 static void waitrelease(WRegion *reg)
105 {
106     if(ioncore_modstate()==0)
107         return;
108         
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.
113      */
114     ioncore_grab_establish((WRegion*)region_rootwin_of(reg), 
115                            waitrelease_handler, 
116                            NULL, 0);
117     ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
118 }
119
120
121 static void free_sub(WSubmapState *p)
122 {
123     /*extl_unref_fn(p->leave);
124     watch_reset(&p->leave_reg);
125     */
126     
127     free(p);
128 }
129
130
131 void region_free_submapstat(WRegion *reg)
132 {
133     while(reg->submapstat!=NULL){
134         WSubmapState *p=reg->submapstat;
135         reg->submapstat=p->next;
136         free_sub(p);
137     }
138 }
139
140
141 WHook *ioncore_submap_ungrab_hook=NULL;
142
143
144 static void call_submap_ungrab_hook()
145 {
146     hook_call_v(ioncore_submap_ungrab_hook); 
147 }
148
149
150 static void clear_subs(WRegion *reg)
151 {
152     region_free_submapstat(reg);
153     mainloop_defer_action(NULL, (WDeferredAction*)call_submap_ungrab_hook);
154 /*
155     while(reg!=NULL && reg->submapstat!=NULL){
156         WSubmapState *p=reg->submapstat;
157         reg->submapstat=p->next;
158         
159         if(p->leave!=extl_fn_none() && p->leave_reg.obj!=NULL){
160             Watch regw=WATCH_INIT;
161                 
162             watch_setup(&regw, (Obj*)reg, NULL);
163                 
164             extl_call(p->leave, "o", NULL, p->leave_reg.obj);
165             
166             reg=(WRegion*)regw.obj;
167             
168             watch_reset(&regw);
169         }
170         
171         free_sub(p);
172     }
173 */
174 }
175
176
177 static WSubmapState *add_sub(WRegion *reg, uint key, uint state)
178 {
179     WSubmapState **p;
180     WSubmapState *s;
181     
182     if(reg->submapstat==NULL){
183         p=&(reg->submapstat);
184     }else{
185         s=reg->submapstat;
186         while(s->next!=NULL)
187             s=s->next;
188         p=&(s->next);
189     }
190     
191     s=ALLOC(WSubmapState);
192     
193     if(s==NULL)
194         return NULL;
195     
196     s->key=key;
197     s->state=state;
198     /*s->leave=extl_fn_none();
199     watch_init(&s->leave_reg);*/
200     
201     *p=s;
202     
203     return s;
204
205 }
206
207
208 static XKeyEvent *current_key_event=NULL;
209 static uint current_kcb, current_state;
210 static bool current_submap;
211
212 /* Note: state set to AnyModifier for submaps */
213 bool ioncore_current_key(uint *kcb, uint *state, bool *sub)
214 {
215     if(current_kcb==0)
216         return FALSE;
217         
218     *kcb=current_kcb;
219     *state=current_state;
220     *sub=current_submap;
221     
222     return TRUE;
223 }
224
225
226 enum{GRAB_NONE, GRAB_NONE_SUBMAP, GRAB_SUBMAP, GRAB_WAITRELEASE};
227
228
229 static WBinding *lookup_binding_(WRegion *reg, 
230                                  int act, uint state, uint kcb, 
231                                  WSubmapState *st,
232                                  WRegion **binding_owner, WRegion **subreg)
233 {
234     WBinding *binding;
235     
236     *subreg=NULL;
237     
238     do{
239         binding=region_lookup_keybinding(reg, act, state, kcb, st,
240                                          binding_owner);
241         
242         if(binding!=NULL)
243             break;
244             
245         if(OBJ_IS(reg, WRootWin))
246             break;
247         
248         *subreg=reg;
249         reg=REGION_PARENT_REG(reg);
250     }while(reg!=NULL);
251     
252     return binding;
253 }
254
255 static WBinding *lookup_binding(WRegion *oreg, 
256                                 int act, uint state, uint kcb, 
257                                 WRegion **binding_owner, WRegion **subreg)
258 {
259     WRegion *reg=oreg;
260         
261     /* Find the deepest nested active window grabbing this key. */
262     while(reg->active_sub!=NULL)
263         reg=reg->active_sub;
264         
265     return lookup_binding_(reg, act, state, kcb, oreg->submapstat, 
266                            binding_owner, subreg);
267 }
268
269
270 static void do_call_binding(WBinding *binding, WRegion *reg, WRegion *subreg)
271 {
272     WRegion *mgd=region_managed_within(reg, subreg);
273
274     /* TODO: having to pass both mgd and subreg for some handlers
275      * to work is ugly and complex.
276      */
277     extl_call(binding->func, "ooo", NULL, reg, mgd, subreg);
278 }
279
280
281 static int do_key(WRegion *oreg, XKeyEvent *ev)
282 {
283     WBinding *binding=NULL;
284     WRegion *binding_owner=NULL, *subreg=NULL;
285     bool grabbed=(oreg->flags&REGION_BINDINGS_ARE_GRABBED);
286     int ret=GRAB_NONE;
287     
288     if(grabbed){
289         binding=lookup_binding(oreg, BINDING_KEYPRESS, ev->state, ev->keycode,
290                                &binding_owner, &subreg);
291     }else{
292         binding=region_lookup_keybinding(oreg, BINDING_KEYPRESS, 
293                                          ev->state, ev->keycode, 
294                                          oreg->submapstat, 
295                                          &binding_owner);
296     }
297     
298     if(binding!=NULL){
299         bool subs=(oreg->submapstat!=NULL);
300         WBinding *call=NULL;
301         
302         if(binding->submap!=NULL){
303             WSubmapState *s=add_sub(oreg, ev->keycode, ev->state);
304             if(s!=NULL){
305                 /*WRegion *own2, *subreg2;
306                 
307                 call=lookup_binding(binding_owner, BINDING_SUBMAP_LEAVE, 0, 0,
308                                     oreg->submapstat, &own2, &subreg2);
309                                     
310                 if(call!=NULL){
311                     s->leave=extl_ref_fn(call->func);
312                     watch_setup(&s->leave_reg, (Obj*)own2, NULL);
313                 }*/
314                 
315                 call=lookup_binding_(binding_owner, BINDING_SUBMAP_ENTER, 0, 0,
316                                      oreg->submapstat, 
317                                      &binding_owner, &subreg);
318                 
319                 ret=(grabbed ? GRAB_SUBMAP : GRAB_NONE_SUBMAP);
320             }
321         }else{
322             call=binding;
323             
324             if(grabbed)
325                 XUngrabKeyboard(ioncore_g.dpy, CurrentTime);
326             
327             if(ev->state!=0 && !subs && binding->wait)
328                 ret=GRAB_WAITRELEASE;
329         }
330         
331         if(call!=NULL){
332             current_kcb=ev->keycode;
333             current_state=ev->state;
334             current_submap=subs;
335             
336             do_call_binding(call, binding_owner, subreg);
337             
338             current_kcb=0;
339         }
340     }else if(oreg->submapstat==NULL && OBJ_IS(oreg, WWindow)){
341         insstr((WWindow*)oreg, ev);
342     }
343     
344     return ret;
345 }
346
347
348 static bool submapgrab_handler(WRegion* reg, XEvent *xev)
349 {
350     XKeyEvent *ev=&xev->xkey;
351     if(ev->type!=KeyPress){
352         if(ioncore_unmod(ev->state, ev->keycode)==0){
353             WBinding *binding;
354             WRegion *binding_owner, *subreg;
355             
356             binding=lookup_binding(reg, 
357                                    BINDING_SUBMAP_RELEASEMOD, 0, 0,
358                                    &binding_owner, &subreg);
359             
360             if(binding!=NULL)
361                 do_call_binding(binding, binding_owner, subreg);
362         }
363         return FALSE;
364     }
365
366     if(ioncore_ismod(ev->keycode))
367         return FALSE;
368     if(do_key(reg, ev)!=GRAB_SUBMAP){
369         clear_subs(reg);
370         return TRUE;
371     }else{
372         return FALSE;
373     }
374 }
375
376
377
378
379 static void submapgrab(WRegion *reg)
380 {
381     ioncore_grab_establish(reg, submapgrab_handler, clear_subs, 0);
382     ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
383 }
384
385
386 void ioncore_do_handle_keypress(XKeyEvent *ev)
387 {
388     WRegion *reg=(WRegion*)XWINDOW_REGION_OF(ev->window);
389     
390     if(reg!=NULL){
391         Watch w=WATCH_INIT;
392         int grab;
393         
394         /* reg might be destroyed by binding handlers */
395         watch_setup(&w, (Obj*)reg, NULL);
396         
397         grab=do_key(reg, ev);
398         
399         reg=(WRegion*)w.obj;
400         
401         if(reg!=NULL){
402             if(grab==GRAB_SUBMAP)
403                 submapgrab(reg);
404             else if(grab==GRAB_WAITRELEASE)
405                 waitrelease(reg);
406             else if(grab==GRAB_NONE_SUBMAP)
407                 /* nothing */;
408             else if(grab==GRAB_NONE && reg->submapstat!=NULL)
409                 clear_subs(reg);
410         }
411         
412         watch_reset(&w);
413     }
414 }