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