move the keyboard and mouse plugins into the kernel for mucho sexiness.
[mikachu/openbox.git] / openbox / keyboard.c
1 #include "focus.h"
2 #include "screen.h"
3 #include "frame.h"
4 #include "openbox.h"
5 #include "event.h"
6 #include "grab.h"
7 #include "client.h"
8 #include "action.h"
9 #include "prop.h"
10 #include "timer.h"
11 #include "keytree.h"
12 #include "keyboard.h"
13 #include "translate.h"
14
15 #include <glib.h>
16
17 KeyBindingTree *keyboard_firstnode;
18
19 static KeyBindingTree *curpos;
20 static ObTimer *chain_timer;
21 static gboolean interactive_grab;
22 static guint grabbed_state;
23 static ObClient *grabbed_client;
24 static ObAction *grabbed_action;
25 static ObFrameContext grabbed_context;
26
27 static void grab_for_window(Window win, gboolean grab)
28 {
29     KeyBindingTree *p;
30
31     ungrab_all_keys(win);
32
33     if (grab) {
34         p = curpos ? curpos->first_child : keyboard_firstnode;
35         while (p) {
36             grab_key(p->key, p->state, win, GrabModeAsync);
37             p = p->next_sibling;
38         }
39     }
40 }
41
42 void keyboard_grab_for_client(ObClient *c, gboolean grab)
43 {
44     grab_for_window(c->window, grab);
45 }
46
47 static void grab_keys(gboolean grab)
48 {
49     GList *it;
50
51     grab_for_window(screen_support_win, grab);
52     for (it = client_list; it; it = g_list_next(it))
53         grab_for_window(((ObClient*)it->data)->frame->window, grab);
54 }
55
56 void keyboard_reset_chains()
57 {
58     if (chain_timer) {
59         timer_stop(chain_timer);
60         chain_timer = NULL;
61     }
62     if (curpos) {
63         curpos = NULL;
64         grab_keys(TRUE);
65     }
66 }
67
68 static void chain_timeout(ObTimer *t, void *data)
69 {
70     keyboard_reset_chains();
71 }
72
73 gboolean keyboard_bind(GList *keylist, ObAction *action)
74 {
75     KeyBindingTree *tree, *t;
76     gboolean conflict;
77
78     g_assert(keylist != NULL);
79     g_assert(action != NULL);
80
81     if (!(tree = tree_build(keylist)))
82         return FALSE;
83
84     if ((t = tree_find(tree, &conflict)) != NULL) {
85         /* already bound to something, use the existing tree */
86         tree_destroy(tree);
87         tree = NULL;
88     } else
89         t = tree;
90     while (t->first_child) t = t->first_child;
91
92     if (conflict) {
93         g_warning("conflict with binding");
94         tree_destroy(tree);
95         return FALSE;
96     }
97
98     /* set the action */
99     t->actions = g_slist_append(t->actions, action);
100     /* assimilate this built tree into the main tree. assimilation
101        destroys/uses the tree */
102     if (tree) tree_assimilate(tree);
103
104     return TRUE;
105 }
106
107 void keyboard_interactive_grab(guint state, ObClient *client,
108                                ObFrameContext context, ObAction *action)
109 {
110     if (!interactive_grab && grab_keyboard(TRUE)) {
111         interactive_grab = TRUE;
112         grabbed_state = state;
113         grabbed_client = client;
114         grabbed_action = action;
115         grabbed_context = context;
116         grab_pointer(TRUE, None);
117     }
118 }
119
120 gboolean keyboard_process_interactive_grab(const XEvent *e,
121                                            ObClient **client,
122                                            ObFrameContext *context)
123 {
124     gboolean handled = FALSE;
125     gboolean done = FALSE;
126
127     if (interactive_grab) {
128         *client = grabbed_client;
129         *context = grabbed_context;
130     }
131
132     if ((e->type == KeyRelease && 
133          !(grabbed_state & e->xkey.state)))
134         done = TRUE;
135     else if (e->type == KeyPress) {
136         if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN))
137             done = TRUE;
138         else if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) {
139             if (grabbed_action->func == action_cycle_windows) {
140                 grabbed_action->data.cycle.cancel = TRUE;
141             }
142             if (grabbed_action->func == action_desktop_dir) {
143                 grabbed_action->data.desktopdir.cancel = TRUE;
144             }
145             if (grabbed_action->func == action_send_to_desktop_dir)
146             {
147                 grabbed_action->data.sendtodir.cancel = TRUE;
148             }
149             done = TRUE;
150         }
151     }
152     if (done) { 
153         if (grabbed_action->func == action_cycle_windows) {
154             grabbed_action->data.cycle.final = TRUE;
155         }
156         if (grabbed_action->func == action_desktop_dir) {
157             grabbed_action->data.desktopdir.final = TRUE;
158         }
159         if (grabbed_action->func == action_send_to_desktop_dir) {
160             grabbed_action->data.sendtodir.final = TRUE;
161         }
162
163         grabbed_action->func(&grabbed_action->data);
164
165         interactive_grab = FALSE;
166         grab_keyboard(FALSE);
167         grab_pointer(FALSE, None);
168         keyboard_reset_chains();
169
170         handled = TRUE;
171     }
172
173     return handled;
174 }
175
176 void keyboard_event(ObClient *client, const XEvent *e)
177 {
178     KeyBindingTree *p;
179
180     g_assert(e->type == KeyPress);
181
182     if (curpos == NULL)
183         p = keyboard_firstnode;
184     else
185         p = curpos->first_child;
186     while (p) {
187         if (p->key == e->xkey.keycode &&
188             p->state == e->xkey.state) {
189             if (p->first_child != NULL) { /* part of a chain */
190                 if (chain_timer) timer_stop(chain_timer);
191                 /* 5 second timeout for chains */
192                 chain_timer = timer_start(5000*1000, chain_timeout,
193                                           NULL);
194                 curpos = p;
195                 grab_keys(TRUE);
196             } else {
197                 GSList *it;
198                 for (it = p->actions; it; it = it->next) {
199                     ObAction *act = it->data;
200                     if (act->func != NULL) {
201                         act->data.any.c = client;
202
203                         if (act->func == action_cycle_windows)
204                         {
205                             act->data.cycle.final = FALSE;
206                             act->data.cycle.cancel = FALSE;
207                         }
208                         if (act->func == action_desktop_dir)
209                         {
210                             act->data.desktopdir.final = FALSE;
211                             act->data.desktopdir.cancel = FALSE;
212                         }
213                         if (act->func == action_send_to_desktop_dir)
214                         {
215                             act->data.sendtodir.final = FALSE;
216                             act->data.sendtodir.cancel = FALSE;
217                         }
218
219                         if (act->func == action_moveresize)
220                         {
221                             screen_pointer_pos(&act->data.moveresize.x,
222                                                &act->data.moveresize.y);
223                         }
224
225                         if ((act->func == action_cycle_windows ||
226                              act->func == action_desktop_dir ||
227                              act->func == action_send_to_desktop_dir))
228                         {
229                             keyboard_interactive_grab(e->xkey.state, client,
230                                                       0, act);
231                         }
232
233                         if (act->func == action_showmenu)
234                         {
235                             act->data.showmenu.x =
236                                 e->xkey.x_root;
237                             act->data.showmenu.y =
238                                 e->xkey.y_root;
239                         }
240
241                         act->data.any.c = client;
242                         act->func(&act->data);
243                     }
244                 }
245
246                 keyboard_reset_chains();
247             }
248             break;
249         }
250         p = p->next_sibling;
251     }
252 }
253
254 void keyboard_startup()
255 {
256     grab_keys(TRUE);
257 }
258
259 void keyboard_shutdown()
260 {
261     tree_destroy(keyboard_firstnode);
262     keyboard_firstnode = NULL;
263     grab_keys(FALSE);
264 }
265