]> git.decadent.org.uk Git - ion3.git/blob - ioncore/key.c
57ada801f313f85d8f3515f6611e638876e62a2c
[ion3.git] / ioncore / key.c
1 /*
2  * ion/ioncore/key.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 <ctype.h>
13 #include <libtu/objp.h>
14 #include "common.h"
15 #include "key.h"
16 #include "binding.h"
17 #include "global.h"
18 #include "event.h"
19 #include "cursor.h"
20 #include "grab.h"
21 #include "regbind.h"
22 #include <libextl/extl.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     if(!ioncore_unmod(ev->xkey.state, ev->xkey.keycode))
101         return TRUE;
102     return FALSE;
103 }
104
105
106 static void waitrelease(WRegion *reg)
107 {
108     if(ioncore_modstate()==0)
109         return;
110         
111     /* We need to grab on the root window as <reg> might have been
112      * ioncore_defer_destroy:ed by the binding handler (the most common case
113      * for using this kpress_wait!). In such a case the grab may
114      * be removed before the modifiers are released.
115      */
116     ioncore_grab_establish((WRegion*)region_rootwin_of(reg), 
117                            waitrelease_handler, 
118                            NULL, 0);
119     ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
120 }
121
122
123 static void free_subs(WSubmapState *p)
124 {
125     WSubmapState *next;
126     
127     while(p!=NULL){
128         next=p->next;
129         free(p);
130         p=next;
131     }
132 }
133
134
135 static void clear_subs(WRegion *reg)
136 {
137     while(reg->submapstat!=NULL){
138         WSubmapState *tmp=reg->submapstat;
139         reg->submapstat=tmp->next;
140         free(tmp);
141     }
142 }
143
144
145 static bool add_sub(WRegion *reg, uint key, uint state)
146 {
147     WSubmapState **p;
148     WSubmapState *s;
149     
150     if(reg->submapstat==NULL){
151         p=&(reg->submapstat);
152     }else{
153         s=reg->submapstat;
154         while(s->next!=NULL)
155             s=s->next;
156         p=&(s->next);
157     }
158     
159     s=ALLOC(WSubmapState);
160     
161     if(s==NULL)
162         return FALSE;
163     
164     s->key=key;
165     s->state=state;
166     
167     *p=s;
168     
169     return TRUE;
170
171 }
172
173
174 static XKeyEvent *current_key_event=NULL;
175 static uint current_kcb, current_state;
176 static bool current_submap;
177
178 /* Note: state set to AnyModifier for submaps */
179 bool ioncore_current_key(uint *kcb, uint *state, bool *sub)
180 {
181     if(current_kcb==0)
182         return FALSE;
183         
184     *kcb=current_kcb;
185     *state=current_state;
186     *sub=current_submap;
187     
188     return TRUE;
189 }
190
191
192 /* Return value TRUE = grab needed */
193 static bool do_key(WRegion *reg, XKeyEvent *ev)
194 {
195     WBinding *binding=NULL;
196     WRegion *oreg=NULL, *binding_owner=NULL, *subreg=NULL;
197     bool grabbed;
198     
199     oreg=reg;
200     grabbed=(oreg->flags&REGION_BINDINGS_ARE_GRABBED);
201     
202     if(grabbed){
203         /* Find the deepest nested active window grabbing this key. */
204         while(reg->active_sub!=NULL)
205             reg=reg->active_sub;
206         
207         do{
208             binding=region_lookup_keybinding(reg, ev, oreg->submapstat, 
209                                              &binding_owner);
210             
211             if(binding!=NULL)
212                 break;
213             if(OBJ_IS(reg, WRootWin))
214                 break;
215             
216             subreg=reg;
217             reg=REGION_PARENT_REG(reg);
218         }while(reg!=NULL);
219     }else{
220         binding=region_lookup_keybinding(oreg, ev, oreg->submapstat, 
221                                          &binding_owner);
222     }
223     
224     if(binding!=NULL){
225         if(binding->submap!=NULL){
226             if(add_sub(oreg, ev->keycode, ev->state))
227                 return grabbed;
228             else
229                 clear_subs(oreg);
230         }else if(binding_owner!=NULL){
231             WRegion *mgd=region_managed_within(binding_owner, subreg);
232             bool subs=(oreg->submapstat!=NULL);
233             
234             clear_subs(oreg);
235             
236             if(grabbed)
237                 XUngrabKeyboard(ioncore_g.dpy, CurrentTime);
238             
239             current_kcb=ev->keycode;
240             current_state=ev->state;
241             current_submap=subs;
242             
243             /* TODO: having to pass both mgd and subreg for some handlers
244              * to work is ugly and complex.
245              */
246             extl_call(binding->func, "ooo", NULL, binding_owner, mgd, subreg);
247             
248             current_kcb=0;
249             
250             if(ev->state!=0 && !subs && binding->wait)
251                 waitrelease(oreg);
252         }
253     }else if(oreg->submapstat!=NULL){
254         clear_subs(oreg);
255     }else if(OBJ_IS(oreg, WWindow)){
256         insstr((WWindow*)oreg, ev);
257     }
258     
259     return FALSE;
260 }
261
262
263 static bool submapgrab_handler(WRegion* reg, XEvent *xev)
264 {
265     XKeyEvent *ev=&xev->xkey;
266     if(ev->type!=KeyPress)
267         return FALSE;
268     if(ioncore_ismod(ev->keycode))
269         return FALSE;
270     return !do_key(reg, ev);
271 }
272
273
274 static void submapgrab(WRegion *reg)
275 {
276     ioncore_grab_establish(reg, submapgrab_handler, clear_subs, 0);
277     ioncore_change_grab_cursor(IONCORE_CURSOR_WAITKEY);
278 }
279
280
281 void ioncore_do_handle_keypress(XKeyEvent *ev)
282 {
283     WRegion *reg=(WRegion*)XWINDOW_REGION_OF(ev->window);
284     
285     if(reg!=NULL){
286         if(do_key(reg, ev))
287             submapgrab(reg);
288     }
289 }