add an xlock binding
[dana/openbox.git] / plugins / keyboard / keyboard.c
1 #include "../../kernel/focus.h"
2 #include "../../kernel/dispatch.h"
3 #include "../../kernel/openbox.h"
4 #include "../../kernel/grab.h"
5 #include "../../kernel/action.h"
6 #include "tree.h"
7 #include "keyboard.h"
8 #include <glib.h>
9
10 KeyBindingTree *firstnode;
11
12 static KeyBindingTree *curpos;
13 static guint reset_key, reset_state;
14 static gboolean grabbed;
15
16 static void grab_keys(gboolean grab)
17 {
18     if (!grab) {
19         ungrab_all_keys();
20     } else {
21         KeyBindingTree *p = firstnode;
22         while (p) {
23             grab_key(p->key, p->state, GrabModeSync);
24             p = p->next_sibling;
25         }
26     }
27 }
28
29 static void reset_chains()
30 {
31     /* XXX kill timer */
32     curpos = NULL;
33     if (grabbed) {
34         grabbed = FALSE;
35         grab_keyboard(FALSE);
36     }
37 }
38
39 static gboolean kbind(GList *keylist, Action *action)
40 {
41     KeyBindingTree *tree, *t;
42     gboolean conflict;
43
44     g_assert(keylist != NULL);
45     g_assert(action != NULL);
46
47     if (!(tree = tree_build(keylist))) {
48         g_warning("invalid binding");
49         return FALSE;
50     }
51     if ((t = tree_find(tree, &conflict)) != NULL) {
52         /* already bound to something */
53         g_warning("keychain is already bound");
54         tree_destroy(tree);
55         return FALSE;
56     }
57     if (conflict) {
58         g_warning("conflict with binding");
59         tree_destroy(tree);
60         return FALSE;
61     }
62
63     /* grab the server here to make sure no key presses go missed */
64     grab_server(TRUE);
65     grab_keys(FALSE);
66
67     /* set the action */
68     t = tree;
69     while (t->first_child) t = t->first_child;
70     t->action = action;
71     /* assimilate this built tree into the main tree. assimilation
72        destroys/uses the tree */
73     tree_assimilate(tree);
74
75     grab_keys(TRUE); 
76     grab_server(FALSE);
77
78     return TRUE;
79 }
80
81 static void press(ObEvent *e, void *foo)
82 {
83     if (e->data.x.e->xkey.keycode == reset_key &&
84         e->data.x.e->xkey.state == reset_state) {
85         reset_chains();
86         XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
87     } else {
88         KeyBindingTree *p;
89         if (curpos == NULL)
90             p = firstnode;
91         else
92             p = curpos->first_child;
93         while (p) {
94             if (p->key == e->data.x.e->xkey.keycode &&
95                 p->state == e->data.x.e->xkey.state) {
96                 if (p->first_child != NULL) { /* part of a chain */
97                     /* XXX TIMER */
98                     if (!grabbed) {
99                         grab_keyboard(TRUE);
100                         grabbed = TRUE;
101                         XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
102                     }
103                     curpos = p;
104                 } else {
105                     if (p->action->func != NULL) {
106                         p->action->data.any.c = focus_client;
107
108                         g_assert(!(p->action->func == action_move ||
109                                    p->action->func == action_resize));
110
111                         p->action->func(&p->action->data);
112                     }
113
114                     XAllowEvents(ob_display, AsyncKeyboard, CurrentTime);
115                     reset_chains();
116                 }
117                 break;
118             }
119             p = p->next_sibling;
120         }
121     }
122 }
123
124 static void binddef()
125 {
126     GList *list = g_list_append(NULL, NULL);
127     Action *a;
128
129     /* When creating an Action struct, all of the data elements in the
130        appropriate struct need to be set, except the Client*, which will be set
131        at call-time when then action function is used.
132     */
133
134     list->data = "A-Right";
135     a = action_new(action_next_desktop);
136     a->data.nextprevdesktop.wrap = TRUE;
137     kbind(list, a);
138
139     list->data = "A-Left";
140     a = action_new(action_previous_desktop);
141     a->data.nextprevdesktop.wrap = TRUE;
142     kbind(list, a);
143
144     list->data = "A-1";
145     a = action_new(action_desktop);
146     a->data.desktop.desk = 0;
147     kbind(list, a);
148
149     list->data = "A-2"; 
150     a = action_new(action_desktop);
151     a->data.desktop.desk = 1;
152     kbind(list, a);
153
154     list->data = "A-3";
155     a = action_new(action_desktop);
156     a->data.desktop.desk = 2;
157     kbind(list, a);
158
159     list->data = "A-4";
160     a = action_new(action_desktop);
161     a->data.desktop.desk = 3;
162     kbind(list, a);
163
164     list->data = "A-space";
165     a = action_new(action_execute);
166     a->data.execute.path = g_strdup("xterm");
167     kbind(list, a);
168
169     list->data = "C-A-Escape";
170     a = action_new(action_execute);
171     a->data.execute.path = g_strdup("xlock -nolock -mode puzzle");
172     kbind(list, a);
173 }
174
175 void plugin_startup()
176 {
177     dispatch_register(Event_X_KeyPress, (EventHandler)press, NULL);
178
179     /* XXX parse config file! */
180     binddef();
181 }
182
183 void plugin_shutdown()
184 {
185     dispatch_register(0, (EventHandler)press, NULL);
186
187     grab_keys(FALSE);
188     tree_destroy(firstnode);
189     firstnode = NULL;
190     grab_keys(TRUE);
191 }
192