so close to keybindings. wont link for now.
[mikachu/openbox.git] / src / bindings.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "bindings.hh"
8 #include "screen.hh"
9 #include "openbox.hh"
10 #include "client.hh"
11 #include "frame.hh"
12 #include "python.hh"
13 #include "otk/display.hh"
14
15 extern "C" {
16 #include <X11/Xlib.h>
17
18 #include "gettext.h"
19 #define _(str) gettext(str)
20 }
21
22 namespace ob {
23
24 #include <stdio.h>
25 static void print_branch(const BindingTree *first, std::string str)
26 {
27   const BindingTree *p = first;
28   
29   while (p) {
30     if (p->first_child)
31       print_branch(p->first_child, str + " " + p->text);
32     if (!p->chain)
33       printf("%d%s\n", p->id, (str + " " + p->text).c_str());
34     p = p->next_sibling;
35   }
36 }
37
38
39 void OBBindings::display()
40 {
41   if (_keytree.first_child) {
42     printf("Key Tree:\n");
43     print_branch(_keytree.first_child, "");
44   }
45   if (_mousetree) {
46     printf("Mouse Tree:\n");
47     BindingTree *p = _mousetree;
48     while (p) {
49       printf("%d %s\n", p->id, p->text.c_str());
50       p = p->next_sibling;
51     }
52   }
53 }
54
55
56 static bool buttonvalue(const std::string &button, unsigned int *val)
57 {
58   if (button == "1" || button == "Button1") {
59     *val |= Button1;
60   } else if (button == "2" || button == "Button2") {
61     *val |= Button2;
62   } else if (button == "3" || button == "Button3") {
63     *val |= Button3;
64   } else if (button == "4" || button == "Button4") {
65     *val |= Button4;
66   } else if (button == "5" || button == "Button5") {
67     *val |= Button5;
68   } else
69     return false;
70   return true;
71 }
72
73 static bool modvalue(const std::string &mod, unsigned int *val)
74 {
75   if (mod == "C") {           // control
76     *val |= ControlMask;
77   } else if (mod == "S") {    // shift
78     *val |= ShiftMask;
79   } else if (mod == "A" ||    // alt/mod1
80              mod == "M" ||
81              mod == "Mod1" ||
82              mod == "M1") {
83     *val |= Mod1Mask;
84   } else if (mod == "Mod2" ||   // mod2
85              mod == "M2") {
86     *val |= Mod2Mask;
87   } else if (mod == "Mod3" ||   // mod3
88              mod == "M3") {
89     *val |= Mod3Mask;
90   } else if (mod == "W" ||    // windows/mod4
91              mod == "Mod4" ||
92              mod == "M4") {
93     *val |= Mod4Mask;
94   } else if (mod == "Mod5" ||   // mod5
95              mod == "M5") {
96     *val |= Mod5Mask;
97   } else {                    // invalid
98     return false;
99   }
100   return true;
101 }
102
103 bool OBBindings::translate(const std::string &str, Binding &b,
104                            bool askey) const
105 {
106   // parse out the base key name
107   std::string::size_type keybegin = str.find_last_of('-');
108   keybegin = (keybegin == std::string::npos) ? 0 : keybegin + 1;
109   std::string key(str, keybegin);
110
111   // parse out the requested modifier keys
112   unsigned int modval = 0;
113   std::string::size_type begin = 0, end;
114   while (begin != keybegin) {
115     end = str.find_first_of('-', begin);
116
117     std::string mod(str, begin, end-begin);
118     if (!modvalue(mod, &modval)) {
119 //      printf(_("Invalid modifier element in key binding: %s\n"), mod.c_str());
120       return false;
121     }
122     
123     begin = end + 1;
124   }
125
126   // set the binding
127   b.modifiers = modval;
128   if (askey) {
129     KeySym sym = XStringToKeysym(const_cast<char *>(key.c_str()));
130     if (sym == NoSymbol) return false;
131     b.key = XKeysymToKeycode(otk::OBDisplay::display, sym);
132     return b.key != 0;
133   } else {
134     return buttonvalue(key, &b.key);
135   }
136 }
137
138 static void destroytree(BindingTree *tree)
139 {
140   while (tree) {
141     BindingTree *c = tree->first_child;
142     delete tree;
143     tree = c;
144   }
145 }
146
147 BindingTree *OBBindings::buildtree(const StringVect &keylist, int id) const
148 {
149   if (keylist.empty()) return 0; // nothing in the list.. return 0
150
151   BindingTree *ret = 0, *p;
152
153   StringVect::const_reverse_iterator it, end = keylist.rend();
154   for (it = keylist.rbegin(); it != end; ++it) {
155     p = ret;
156     ret = new BindingTree(id);
157     if (!p) ret->chain = false; // only the first built node
158     ret->first_child = p;
159     if (!translate(*it, ret->binding, true)) {
160       destroytree(ret);
161       ret = 0;
162       break;
163     }
164     ret->text = *it; // XXX: rm me
165   }
166   return ret;
167 }
168
169
170 OBBindings::OBBindings()
171   : _curpos(&_keytree), _mousetree(0)
172 {
173 }
174
175
176 OBBindings::~OBBindings()
177 {
178   grabMouseOnAll(false); // ungrab everything
179   grabKeys(false);
180   remove_all();
181 }
182
183
184 bool OBBindings::add_mouse(const std::string &button, int id)
185 {
186   BindingTree n;
187
188   if (!translate(button, n.binding, false))
189     return false;
190
191   BindingTree *p = _mousetree, **newp = &_mousetree;
192   while (p) {
193     if (p->binding == n.binding)
194       return false; // conflict
195     p = p->next_sibling;
196     newp = &p->next_sibling;
197   }
198
199   grabMouseOnAll(false); // ungrab everything
200   
201   *newp = new BindingTree(id);
202   (*newp)->text = button;
203   (*newp)->chain = false;
204   (*newp)->binding.key = n.binding.key;
205   (*newp)->binding.modifiers = n.binding.modifiers;
206
207   grabMouseOnAll(true);
208   
209   return true;
210 }
211
212
213 int OBBindings::remove_mouse(const std::string &button)
214 {
215   (void)button;
216   assert(false); // XXX: function not implemented yet
217
218   grabMouseOnAll(false); // ungrab everything
219
220   // do shit...
221   
222   grabMouseOnAll(true);
223 }
224
225
226 void OBBindings::assimilate(BindingTree *node)
227 {
228   BindingTree *a, *b, *tmp, *last;
229
230   if (!_keytree.first_child) {
231     // there are no nodes at this level yet
232     _keytree.first_child = node;
233   } else {
234     a = _keytree.first_child;
235     last = a;
236     b = node;
237     while (a) {
238       last = a;
239       if (a->binding != b->binding) {
240         a = a->next_sibling;
241       } else {
242         tmp = b;
243         b = b->first_child;
244         delete tmp;
245         a = a->first_child;
246       }
247     }
248     if (last->binding != b->binding)
249       last->next_sibling = b;
250     else {
251       last->first_child = b->first_child;
252       delete b;
253     }
254   }
255 }
256
257
258 int OBBindings::find_key(BindingTree *search) const {
259   BindingTree *a, *b;
260   a = _keytree.first_child;
261   b = search;
262   while (a && b) {
263     if (a->binding != b->binding) {
264       a = a->next_sibling;
265     } else {
266       if (a->chain == b->chain) {
267         if (!a->chain) {
268           return a->id; // found it! (return the actual id, not the search's)
269         }
270       } else {
271         return -2; // the chain status' don't match (conflict!)
272       }
273       b = b->first_child;
274       a = a->first_child;
275     }
276   }
277   return -1; // it just isn't in here
278 }
279
280
281 bool OBBindings::add_key(const StringVect &keylist, int id)
282 {
283   BindingTree *tree;
284
285   if (!(tree = buildtree(keylist, id)))
286     return false; // invalid binding requested
287
288   if (find_key(tree) != -1) {
289     // conflicts with another binding
290     destroytree(tree);
291     return false;
292   }
293
294   grabKeys(false);
295   
296   // assimilate this built tree into the main tree
297   assimilate(tree); // assimilation destroys/uses the tree
298
299   grabKeys(true); 
300  
301   return true;
302 }
303
304
305 int OBBindings::find_key(const StringVect &keylist)
306 {
307   BindingTree *tree;
308   bool ret;
309
310   if (!(tree = buildtree(keylist, 0)))
311     return false; // invalid binding requested
312
313   ret = find_key(tree) >= 0;
314
315   destroytree(tree);
316
317   return ret;
318 }
319
320
321 int OBBindings::remove_key(const StringVect &keylist)
322 {
323   (void)keylist;
324   assert(false); // XXX: function not implemented yet
325
326   grabKeys(false);
327   _curpos = &_keytree;
328
329   // do shit here...
330   
331   grabKeys(true);
332
333 }
334
335
336 static void remove_branch(BindingTree *first)
337 {
338   BindingTree *p = first;
339
340   while (p) {
341     if (p->first_child)
342       remove_branch(p->first_child);
343     BindingTree *s = p->next_sibling;
344     delete p;
345     p = s;
346   }
347 }
348
349
350 void OBBindings::remove_all()
351 {
352   if (_keytree.first_child) {
353     remove_branch(_keytree.first_child);
354     _keytree.first_child = 0;
355   }
356   BindingTree *p = _mousetree;
357   while (p) {
358     BindingTree *n = p->next_sibling;
359     delete p;
360     p = n;
361   }
362   _mousetree = 0;
363 }
364
365
366 void OBBindings::process(unsigned int modifiers, unsigned int key)
367 {
368   BindingTree *c = _curpos->first_child;
369
370   while (c) {
371     if (c->binding.key == key && c->binding.modifiers == modifiers) {
372       _curpos = c;
373       break;
374     }
375   }
376   if (c) {
377     if (!_curpos->chain) {
378       // XXX execute command for _curpos->id
379       _curpos = &_keytree; // back to the start
380     }
381   }
382 }
383
384
385 void OBBindings::grabMouse(bool grab, const OBClient *client)
386 {
387   BindingTree *p = _mousetree;
388   while (p) {
389     if (grab)
390       otk::OBDisplay::grabButton(p->binding.key, p->binding.modifiers,
391                                  client->frame->window(), false,
392                                  ButtonMotionMask | ButtonPressMask |
393                                  ButtonReleaseMask, GrabModeAsync,
394                                  GrabModeAsync, None, None, false);
395     else
396       otk::OBDisplay::ungrabButton(p->binding.key, p->binding.modifiers,
397                                    client->frame->window());
398     p = p->next_sibling;
399   }
400 }
401
402
403 void OBBindings::grabMouseOnAll(bool grab)
404 {
405   for (int i = 0; i < Openbox::instance->screenCount(); ++i) {
406     OBScreen *s = Openbox::instance->screen(i);
407     assert(s);
408     OBScreen::ClientList::iterator it, end = s->clients.end();
409     for (it = s->clients.begin(); it != end; ++it)
410       grabMouse(grab, *it);
411   }
412 }
413
414
415 void OBBindings::grabKeys(bool grab)
416 {
417   for (int i = 0; i < Openbox::instance->screenCount(); ++i) {
418     Window root = otk::OBDisplay::screenInfo(i)->rootWindow();
419
420     BindingTree *p = _curpos->first_child;
421     while (p) {
422       if (grab)
423         otk::OBDisplay::grabKey(p->binding.key, p->binding.modifiers,
424                                 root, false, GrabModeAsync, GrabModeAsync,
425                                 false);
426       else
427         otk::OBDisplay::ungrabKey(p->binding.key, p->binding.modifiers,
428                                   root);
429       p = p->next_sibling;
430     }
431   }
432 }
433
434
435 void OBBindings::fire(OBActions::ActionType type, Window window,
436                       unsigned int modifiers, unsigned int key, Time time)
437 {
438   if (type == OBActions::Action_KeyPress) {
439     BindingTree *p = _curpos->first_child;
440     while (p) {
441       if (p->binding.key == key && p->binding.modifiers == modifiers) {
442         if (p->chain) {
443           grabKeys(false);
444           _curpos = p;
445           grabKeys(true);
446         } else {
447           python_callback_binding(p->id, type, window, modifiers, key, time);
448           _curpos = &_keytree;
449         }
450         break;
451       }
452       p = p->next_sibling;
453     }
454     
455     assert(false);
456   } else {
457     BindingTree *p = _mousetree;
458     while (p) {
459       if (p->binding.key == key && p->binding.modifiers == modifiers) {
460         python_callback_binding(p->id, type, window, modifiers, key, time);
461         break;
462       }
463       p = p->next_sibling;
464     }
465   }
466 }
467
468
469 }