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