replace the focus_backup with the screen_support_win, we dont need 2 offscreen windows.
[dana/openbox.git] / plugins / keyboard / keyboard.c
1 #include "kernel/focus.h"
2 #include "kernel/screen.h"
3 #include "kernel/frame.h"
4 #include "kernel/dispatch.h"
5 #include "kernel/openbox.h"
6 #include "kernel/event.h"
7 #include "kernel/grab.h"
8 #include "kernel/client.h"
9 #include "kernel/action.h"
10 #include "kernel/prop.h"
11 #include "kernel/timer.h"
12 #include "parser/parse.h"
13 #include "tree.h"
14 #include "keyboard.h"
15 #include "translate.h"
16 #include <glib.h>
17
18 /*
19
20 <keybind key="C-x">
21   <action name="ChangeDesktop">
22     <desktop>3</desktop>
23   </action>
24 </keybind>
25
26 */
27
28 static void parse_key(xmlDocPtr doc, xmlNodePtr node, GList *keylist)
29 {
30     char *key;
31     Action *action;
32     xmlNodePtr n, nact;
33     GList *it;
34
35     n = parse_find_node("keybind", node);
36     while (n) {
37         if (parse_attr_string("key", n, &key)) {
38             keylist = g_list_append(keylist, key);
39
40             parse_key(doc, n->xmlChildrenNode, keylist);
41
42             it = g_list_last(keylist);
43             g_free(it->data);
44             keylist = g_list_delete_link(keylist, it);
45         }
46         n = parse_find_node("keybind", n->next);
47     }
48     if (keylist) {
49         nact = parse_find_node("action", node);
50         while (nact) {
51             if ((action = action_parse(doc, nact))) {
52                 /* validate that its okay for a key binding */
53                 if (action->func == action_moveresize &&
54                     action->data.moveresize.corner !=
55                     prop_atoms.net_wm_moveresize_move_keyboard &&
56                     action->data.moveresize.corner !=
57                     prop_atoms.net_wm_moveresize_size_keyboard) {
58                     action_free(action);
59                     action = NULL;
60                 }
61
62                 if (action)
63                     kbind(keylist, action);
64             }
65             nact = parse_find_node("action", nact->next);
66         }
67     }
68 }
69
70 static void parse_xml(xmlDocPtr doc, xmlNodePtr node, void *d)
71 {
72     parse_key(doc, node, NULL);
73 }
74
75 void plugin_setup_config()
76 {
77     parse_register("keyboard", parse_xml, NULL);
78 }
79
80 KeyBindingTree *firstnode = NULL;
81
82 static KeyBindingTree *curpos;
83 static guint reset_key, reset_state;
84 static ObTimer *chain_timer;
85
86 static void grab_for_window(Window win, gboolean grab)
87 {
88     KeyBindingTree *p;
89
90     ungrab_all_keys(win);
91
92     if (grab) {
93         p = curpos ? curpos->first_child : firstnode;
94         while (p) {
95             grab_key(p->key, p->state, win, GrabModeAsync);
96             p = p->next_sibling;
97         }
98         if (curpos)
99             grab_key(reset_key, reset_state, win, GrabModeAsync);
100     }
101 }
102
103 static void grab_keys(gboolean grab)
104 {
105     GList *it;
106
107     grab_for_window(screen_support_win, grab);
108     for (it = client_list; it; it = g_list_next(it))
109         grab_for_window(((ObClient*)it->data)->frame->window, grab);
110 }
111
112 static void reset_chains()
113 {
114     if (chain_timer) {
115         timer_stop(chain_timer);
116         chain_timer = NULL;
117     }
118     if (curpos) {
119         curpos = NULL;
120         grab_keys(TRUE);
121     }
122 }
123
124 static void chain_timeout(void *data)
125 {
126     reset_chains();
127 }
128
129 gboolean kbind(GList *keylist, Action *action)
130 {
131     KeyBindingTree *tree, *t;
132     gboolean conflict;
133
134     g_assert(keylist != NULL);
135     g_assert(action != NULL);
136
137     if (!(tree = tree_build(keylist)))
138         return FALSE;
139
140     if ((t = tree_find(tree, &conflict)) != NULL) {
141         /* already bound to something, use the existing tree */
142         tree_destroy(tree);
143         tree = NULL;
144     } else
145         t = tree;
146     while (t->first_child) t = t->first_child;
147
148     if (conflict) {
149         g_message("conflict with binding");
150         tree_destroy(tree);
151         return FALSE;
152     }
153
154     /* set the action */
155     t->actions = g_slist_append(t->actions, action);
156     /* assimilate this built tree into the main tree. assimilation
157        destroys/uses the tree */
158     if (tree) tree_assimilate(tree);
159
160     return TRUE;
161 }
162
163 static void event(ObEvent *e, void *foo)
164 {
165     static KeyBindingTree *grabbed_key = NULL;
166
167     if (e->type == Event_Client_Mapped) {
168         grab_for_window(e->data.c.client->window, TRUE);
169         return;
170     } else if (e->type == Event_Client_Destroy) {
171         grab_for_window(e->data.c.client->window, FALSE);
172         return;
173     }
174
175     if (grabbed_key) {
176         gboolean done = FALSE;
177
178         if ((e->type == Event_X_KeyRelease && 
179              !(grabbed_key->state & e->data.x.e->xkey.state)))
180             done = TRUE;
181         else if (e->type == Event_X_KeyPress) {
182             if (e->data.x.e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
183                 done = TRUE;
184             else if (e->data.x.e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
185                 GSList *it;
186                 for (it = grabbed_key->actions; it; it = it->next) {
187                     Action *act = it->data;
188                     act->data.cycle.cancel = TRUE;
189                 }
190                 done = TRUE;
191             }
192         }
193         if (done) { 
194             GSList *it;
195             for (it = grabbed_key->actions; it; it = it->next) {
196                 Action *act = it->data;
197                 act->data.cycle.final = TRUE;
198                 act->func(&act->data);
199             }
200             grabbed_key = NULL;
201             grab_keyboard(FALSE);
202             reset_chains();
203             return;
204         }
205     }
206     if (e->type == Event_X_KeyRelease)
207         return;
208
209     g_assert(e->type == Event_X_KeyPress);
210
211     if (e->data.x.e->xkey.keycode == reset_key &&
212         e->data.x.e->xkey.state == reset_state) {
213         reset_chains();
214     } else {
215         KeyBindingTree *p;
216         if (curpos == NULL)
217             p = firstnode;
218         else
219             p = curpos->first_child;
220         while (p) {
221             if (p->key == e->data.x.e->xkey.keycode &&
222                 p->state == e->data.x.e->xkey.state) {
223                 if (p->first_child != NULL) { /* part of a chain */
224                     if (chain_timer) timer_stop(chain_timer);
225                     /* 5 second timeout for chains */
226                     chain_timer = timer_start(5000*1000, chain_timeout,
227                                               NULL);
228                     curpos = p;
229                     grab_keys(TRUE);
230                 } else {
231                     GSList *it;
232                     for (it = p->actions; it; it = it->next) {
233                         Action *act = it->data;
234                         if (act->func != NULL) {
235                             act->data.any.c = focus_client;
236
237                             if (act->func == action_cycle_windows) {
238                                 act->data.cycle.final = FALSE;
239                                 act->data.cycle.cancel = FALSE;
240                             }
241
242                             if (act->func == action_cycle_windows &&
243                                 !grabbed_key && grab_keyboard(TRUE)) {
244                                 grabbed_key = p;
245                             }
246
247                             act->data.any.c = focus_client;
248                             act->func(&act->data);
249                         }
250                     }
251
252                     reset_chains();
253                 }
254                 break;
255             }
256             p = p->next_sibling;
257         }
258     }
259 }
260
261 void plugin_startup()
262 {
263     curpos = NULL;
264     chain_timer = NULL;
265
266     dispatch_register(Event_Client_Mapped | Event_Client_Destroy |
267                       Event_X_KeyPress | Event_X_KeyRelease,
268                       (EventHandler)event, NULL);
269
270     translate_key("C-g", &reset_state, &reset_key);
271
272     grab_keys(TRUE);
273 }
274
275 void plugin_shutdown()
276 {
277     dispatch_register(0, (EventHandler)event, NULL);
278
279     tree_destroy(firstnode);
280     firstnode = NULL;
281     grab_keys(FALSE);
282 }
283