new swig build system. much better. yay.
[dana/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 "otk/display.hh"
13
14 extern "C" {
15 #include <X11/Xlib.h>
16
17 #include "gettext.h"
18 #define _(str) gettext(str)
19 }
20
21 #include <algorithm>
22
23 namespace ob {
24
25 static bool buttonvalue(const std::string &button, unsigned int *val)
26 {
27   if (button == "Left" || button == "1" || button == "Button1") {
28     *val |= Button1;
29   } else if (button == "Middle" || button == "2" || button == "Button2") {
30     *val |= Button2;
31   } else if (button == "Right" || button == "3" || button == "Button3") {
32     *val |= Button3;
33   } else if (button == "Up" || button == "4" || button == "Button4") {
34     *val |= Button4;
35   } else if (button == "Down" || button == "5" || button == "Button5") {
36     *val |= Button5;
37   } else
38     return false;
39   return true;
40 }
41
42 static bool modvalue(const std::string &mod, unsigned int *val)
43 {
44   if (mod == "C") {           // control
45     *val |= ControlMask;
46   } else if (mod == "S") {    // shift
47     *val |= ShiftMask;
48   } else if (mod == "A" ||    // alt/mod1
49              mod == "M" ||
50              mod == "Mod1" ||
51              mod == "M1") {
52     *val |= Mod1Mask;
53   } else if (mod == "Mod2" ||   // mod2
54              mod == "M2") {
55     *val |= Mod2Mask;
56   } else if (mod == "Mod3" ||   // mod3
57              mod == "M3") {
58     *val |= Mod3Mask;
59   } else if (mod == "W" ||    // windows/mod4
60              mod == "Mod4" ||
61              mod == "M4") {
62     *val |= Mod4Mask;
63   } else if (mod == "Mod5" ||   // mod5
64              mod == "M5") {
65     *val |= Mod5Mask;
66   } else {                    // invalid
67     return false;
68   }
69   return true;
70 }
71
72 bool Bindings::translate(const std::string &str, Binding &b,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::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     return buttonvalue(key, &b.key);
107   }
108 }
109
110 static void destroytree(KeyBindingTree *tree)
111 {
112   while (tree) {
113     KeyBindingTree *c = tree->first_child;
114     delete tree;
115     tree = c;
116   }
117 }
118
119 KeyBindingTree *Bindings::buildtree(const StringVect &keylist,
120                                     KeyCallback callback, void *data) const
121 {
122   if (keylist.empty()) return 0; // nothing in the list.. return 0
123
124   KeyBindingTree *ret = 0, *p;
125
126   StringVect::const_reverse_iterator it, end = keylist.rend();
127   for (it = keylist.rbegin(); it != end; ++it) {
128     p = ret;
129     ret = new KeyBindingTree();
130     if (!p) {
131       // this is the first built node, the bottom node of the tree
132       ret->chain = false;
133       ret->callbacks.push_back(KeyCallbackData(callback, data));
134     }
135     ret->first_child = p;
136     if (!translate(*it, ret->binding)) {
137       destroytree(ret);
138       ret = 0;
139       break;
140     }
141   }
142   return ret;
143 }
144
145
146 Bindings::Bindings()
147   : _curpos(&_keytree),
148     _resetkey(0,0),
149     _timer((otk::Timer *) 0),
150     _keybgrab_callback(0, 0)
151 {
152 //  setResetKey("C-g"); // set the default reset key
153 }
154
155
156 Bindings::~Bindings()
157 {
158   if (_timer)
159     delete _timer;
160
161   removeAllKeys();
162   //removeAllButtons(); // this is done by each client as they are unmanaged
163   removeAllEvents();
164 }
165
166
167 void Bindings::assimilate(KeyBindingTree *node)
168 {
169   KeyBindingTree *a, *b, *tmp, *last;
170
171   if (!_keytree.first_child) {
172     // there are no nodes at this level yet
173     _keytree.first_child = node;
174   } else {
175     a = _keytree.first_child;
176     last = a;
177     b = node;
178     while (a) {
179       last = a;
180       if (a->binding != b->binding) {
181         a = a->next_sibling;
182       } else {
183         tmp = b;
184         b = b->first_child;
185         delete tmp;
186         a = a->first_child;
187       }
188     }
189     if (last->binding != b->binding)
190       last->next_sibling = b;
191     else {
192       last->first_child = b->first_child;
193       delete b;
194     }
195   }
196 }
197
198
199 KeyBindingTree *Bindings::find(KeyBindingTree *search,
200                                  bool *conflict) const {
201   *conflict = false;
202   KeyBindingTree *a, *b;
203   a = _keytree.first_child;
204   b = search;
205   while (a && b) {
206     if (a->binding != b->binding) {
207       a = a->next_sibling;
208     } else {
209       if (a->chain == b->chain) {
210         if (!a->chain) {
211           // found it! (return the actual id, not the search's)
212           return a;
213         }
214       } else {
215         *conflict = true;
216         return 0; // the chain status' don't match (conflict!)
217       }
218       b = b->first_child;
219       a = a->first_child;
220     }
221   }
222   return 0; // it just isn't in here
223 }
224
225
226 bool Bindings::addKey(const StringVect &keylist, KeyCallback callback,
227                       void *data)
228 {
229   KeyBindingTree *tree, *t;
230   bool conflict;
231
232   if (!(tree = buildtree(keylist, callback, data)))
233     return false; // invalid binding requested
234
235   t = find(tree, &conflict);
236   if (conflict) {
237     // conflicts with another binding
238     destroytree(tree);
239     return false;
240   }
241
242   if (t) {
243     // already bound to something
244     t->callbacks.push_back(KeyCallbackData(callback, data));
245     destroytree(tree);
246   } else {
247     // grab the server here to make sure no key pressed go missed
248     otk::display->grab();
249     grabKeys(false);
250
251     // assimilate this built tree into the main tree
252     assimilate(tree); // assimilation destroys/uses the tree
253
254     grabKeys(true); 
255     otk::display->ungrab();
256   }
257  
258   return true;
259 }
260
261 /*
262 bool Bindings::removeKey(const StringVect &keylist, KeyCallback callback, void *data)
263 {
264   assert(false); // XXX: function not implemented yet
265
266   KeyBindingTree *tree;
267   bool conflict;
268
269   if (!(tree = buildtree(keylist, 0)))
270     return false; // invalid binding requested
271
272   KeyBindingTree *t = find(tree, &conflict);
273   if (t) {
274     KeyCallbackList::iterator it = std::find(t->callbacks.begin(),
275                                              t->callbacks.end(),
276                                              callback);
277     if (it != t->callbacks.end()) {
278       // grab the server here to make sure no key pressed go missed
279       otk::display->grab();
280       grabKeys(false);
281       
282       _curpos = &_keytree;
283       
284       // XXX do shit here ...
285       Py_XDECREF(*it);
286       
287       grabKeys(true);
288       otk::display->ungrab();
289       
290       return true;
291     }
292   }
293   return false;
294 }
295 */
296
297 void Bindings::setResetKey(const std::string &key)
298 {
299   Binding b(0, 0);
300   if (translate(key, b)) {
301     // grab the server here to make sure no key pressed go missed
302     otk::display->grab();
303     grabKeys(false);
304     _resetkey.key = b.key;
305     _resetkey.modifiers = b.modifiers;
306     grabKeys(true);
307     otk::display->ungrab();
308   }
309 }
310
311
312 static void remove_branch(KeyBindingTree *first)
313 {
314   KeyBindingTree *p = first;
315
316   while (p) {
317     if (p->first_child)
318       remove_branch(p->first_child);
319     KeyBindingTree *s = p->next_sibling;
320     while(!p->callbacks.empty()) {
321       p->callbacks.pop_front();
322     }
323     delete p;
324     p = s;
325   }
326 }
327
328
329 void Bindings::removeAllKeys()
330 {
331   grabKeys(false);
332   if (_keytree.first_child) {
333     remove_branch(_keytree.first_child);
334     _keytree.first_child = 0;
335   }
336   grabKeys(true);
337 }
338
339
340 void Bindings::grabKeys(bool grab)
341 {
342   for (int i = 0; i < ScreenCount(**otk::display); ++i) {
343     Screen *sc = openbox->screen(i);
344     if (!sc) continue; // not a managed screen
345     Window root = otk::display->screenInfo(i)->rootWindow();
346
347     KeyBindingTree *p = _curpos->first_child;
348     while (p) {
349       if (grab) {
350         otk::display->grabKey(p->binding.key, p->binding.modifiers,
351                                 root, false, GrabModeAsync, GrabModeAsync,
352                                 false);
353       }
354       else
355         otk::display->ungrabKey(p->binding.key, p->binding.modifiers,
356                                   root);
357       p = p->next_sibling;
358     }
359
360     if (_resetkey.key)
361       if (grab)
362         otk::display->grabKey(_resetkey.key, _resetkey.modifiers,
363                                 root, false, GrabModeAsync, GrabModeAsync,
364                                 false);
365       else
366         otk::display->ungrabKey(_resetkey.key, _resetkey.modifiers,
367                                   root);
368   }
369 }
370
371
372 bool Bindings::grabKeyboard(int screen, KeyCallback callback, void *data)
373 {
374   assert(callback);
375   if (_keybgrab_callback.callback) return false; // already grabbed
376
377   if (!openbox->screen(screen))
378     return false; // the screen is not managed
379   
380   Window root = otk::display->screenInfo(screen)->rootWindow();
381   if (XGrabKeyboard(**otk::display, root, false, GrabModeAsync,
382                     GrabModeAsync, CurrentTime))
383     return false;
384   _keybgrab_callback.callback = callback;
385   _keybgrab_callback.data = data;
386   return true;
387 }
388
389
390 void Bindings::ungrabKeyboard()
391 {
392   if (!_keybgrab_callback.callback) return; // not grabbed
393
394   _keybgrab_callback = KeyCallbackData(0, 0);
395   XUngrabKeyboard(**otk::display, CurrentTime);
396   XUngrabPointer(**otk::display, CurrentTime);
397 }
398
399
400 bool Bindings::grabPointer(int screen)
401 {
402   if (!openbox->screen(screen))
403     return false; // the screen is not managed
404   
405   Window root = otk::display->screenInfo(screen)->rootWindow();
406   XGrabPointer(**otk::display, root, false, 0, GrabModeAsync,
407                GrabModeAsync, None, None, CurrentTime);
408   return true;
409 }
410
411
412 void Bindings::ungrabPointer()
413 {
414   XUngrabPointer(**otk::display, CurrentTime);
415 }
416
417
418 void Bindings::fireKey(int screen, unsigned int modifiers, unsigned int key,
419                        Time time, KeyAction::KA action)
420 {
421   if (_keybgrab_callback.callback) {
422     Client *c = openbox->focusedClient();
423     KeyData data(screen, c, time, modifiers, key, action);
424     _keybgrab_callback.fire(&data);
425   }
426   
427   // KeyRelease events only occur during keyboard grabs
428   if (action == KeyAction::Release) return;
429     
430   if (key == _resetkey.key && modifiers == _resetkey.modifiers) {
431     resetChains(this);
432   } else {
433     KeyBindingTree *p = _curpos->first_child;
434     while (p) {
435       if (p->binding.key == key && p->binding.modifiers == modifiers) {
436         if (p->chain) {
437           if (_timer)
438             delete _timer;
439           _timer = new otk::Timer(5000, // 5 second timeout
440                                   (otk::Timer::TimeoutHandler)resetChains,
441                                   this);
442           // grab the server here to make sure no key presses get missed
443           otk::display->grab();
444           grabKeys(false);
445           _curpos = p;
446           grabKeys(true);
447           otk::display->ungrab();
448         } else {
449           Client *c = openbox->focusedClient();
450           KeyData data(screen, c, time, modifiers, key, action);
451           KeyCallbackList::iterator it, end = p->callbacks.end();
452           for (it = p->callbacks.begin(); it != end; ++it)
453             it->fire(&data);
454           resetChains(this);
455         }
456         break;
457       }
458       p = p->next_sibling;
459     }
460   }
461 }
462
463 void Bindings::resetChains(Bindings *self)
464 {
465   if (self->_timer) {
466     delete self->_timer;
467     self->_timer = (otk::Timer *) 0;
468   }
469   // grab the server here to make sure no key pressed go missed
470   otk::display->grab();
471   self->grabKeys(false);
472   self->_curpos = &self->_keytree;
473   self->grabKeys(true);
474   otk::display->ungrab();
475 }
476
477
478 bool Bindings::addButton(const std::string &but, MouseContext::MC context,
479                          MouseAction::MA action, MouseCallback callback,
480                          void *data)
481 {
482   assert(context >= 0 && context < MouseContext::NUM_MOUSE_CONTEXT);
483   assert(action >= 0 && action < MouseAction::NUM_MOUSE_ACTION);
484   
485   Binding b(0,0);
486   if (!translate(but, b, false))
487     return false;
488
489   ButtonBindingList::iterator it, end = _buttons[context].end();
490
491   // look for a duplicate binding
492   for (it = _buttons[context].begin(); it != end; ++it)
493     if ((*it)->binding.key == b.key &&
494         (*it)->binding.modifiers == b.modifiers) {
495       break;
496     }
497
498   ButtonBinding *bind;
499   
500   // the binding didnt exist yet, add it
501   if (it == end) {
502     bind = new ButtonBinding();
503     bind->binding.key = b.key;
504     bind->binding.modifiers = b.modifiers;
505     _buttons[context].push_back(bind);
506     // grab the button on all clients
507     for (int sn = 0; sn < ScreenCount(**otk::display); ++sn) {
508       Screen *s = openbox->screen(sn);
509       if (!s) continue; // not managed
510       Client::List::iterator c_it, c_end = s->clients.end();
511       for (c_it = s->clients.begin(); c_it != c_end; ++c_it) {
512         grabButton(true, bind->binding, context, *c_it);
513       }
514     }
515   } else
516     bind = *it;
517   bind->callbacks[action].push_back(MouseCallbackData(callback, data));
518   return true;
519 }
520
521 void Bindings::removeAllButtons()
522 {
523   for (int i = 0; i < MouseContext::NUM_MOUSE_CONTEXT; ++i) {
524     ButtonBindingList::iterator it, end = _buttons[i].end();
525     for (it = _buttons[i].begin(); it != end; ++it) {
526       for (int a = 0; a < MouseAction::NUM_MOUSE_ACTION; ++a) {
527         while (!(*it)->callbacks[a].empty()) {
528           (*it)->callbacks[a].pop_front();
529         }
530       }
531       // ungrab the button on all clients
532       for (int sn = 0; sn < ScreenCount(**otk::display); ++sn) {
533         Screen *s = openbox->screen(sn);
534         if (!s) continue; // not managed
535         Client::List::iterator c_it, c_end = s->clients.end();
536         for (c_it = s->clients.begin(); c_it != c_end; ++c_it) {
537           grabButton(false, (*it)->binding, (MouseContext::MC)i, *c_it);
538         }
539       }
540     }
541   }
542 }
543
544 void Bindings::grabButton(bool grab, const Binding &b,
545                           MouseContext::MC context, Client *client)
546 {
547   Window win;
548   int mode = GrabModeAsync;
549   unsigned int mask;
550   switch(context) {
551   case MouseContext::Frame:
552     win = client->frame->window();
553     mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
554     break;
555   case MouseContext::Window:
556     win = client->frame->plate();
557     mode = GrabModeSync; // this is handled in fireButton
558     mask = ButtonPressMask; // can't catch more than this with Sync mode
559                             // the release event is manufactured by the
560                             // master buttonPressHandler
561     break;
562   default:
563     // any other elements already get button events, don't grab on them
564     return;
565   }
566   if (grab)
567     otk::display->grabButton(b.key, b.modifiers, win, false, mask, mode,
568                              GrabModeAsync, None, None, false);
569   else
570     otk::display->ungrabButton(b.key, b.modifiers, win);
571 }
572
573 void Bindings::grabButtons(bool grab, Client *client)
574 {
575   for (int i = 0; i < MouseContext::NUM_MOUSE_CONTEXT; ++i) {
576     ButtonBindingList::iterator it, end = _buttons[i].end();
577     for (it = _buttons[i].begin(); it != end; ++it)
578       grabButton(grab, (*it)->binding, (MouseContext::MC)i, client);
579   }
580 }
581
582 void Bindings::fireButton(MouseData *data)
583 {
584   if (data->context == MouseContext::Window) {
585     // Replay the event, so it goes to the client
586     XAllowEvents(**otk::display, ReplayPointer, data->time);
587   }
588   
589   ButtonBindingList::iterator it, end = _buttons[data->context].end();
590   for (it = _buttons[data->context].begin(); it != end; ++it)
591     if ((*it)->binding.key == data->button &&
592         (*it)->binding.modifiers == data->state) {
593       MouseCallbackList::iterator c_it,
594         c_end = (*it)->callbacks[data->action].end();
595       for (c_it = (*it)->callbacks[data->action].begin();
596            c_it != c_end; ++c_it)
597         c_it->fire(data);
598     }
599 }
600
601
602 bool Bindings::addEvent(EventAction::EA action, EventCallback callback,
603                         void *data)
604 {
605   if (action < 0 || action >= EventAction::NUM_EVENT_ACTION) {
606     return false;
607   }
608 #ifdef    XKB
609   if (action == EventAction::Bell && _eventlist[action].empty())
610     XkbSelectEvents(**otk::display, XkbUseCoreKbd,
611                     XkbBellNotifyMask, XkbBellNotifyMask);
612 #endif // XKB
613   _eventlist[action].push_back(EventCallbackData(callback, data));
614   return true;
615 }
616
617 bool Bindings::removeEvent(EventAction::EA action, EventCallback callback,
618                            void *data)
619 {
620   if (action < 0 || action >= EventAction::NUM_EVENT_ACTION) {
621     return false;
622   }
623   
624   EventCallbackList::iterator it = std::find(_eventlist[action].begin(),
625                                              _eventlist[action].end(),
626                                              EventCallbackData(callback,
627                                                                data));
628   if (it != _eventlist[action].end()) {
629     _eventlist[action].erase(it);
630 #ifdef    XKB
631     if (action == EventAction::Bell && _eventlist[action].empty())
632       XkbSelectEvents(**otk::display, XkbUseCoreKbd,
633                       XkbBellNotifyMask, 0);
634 #endif // XKB
635     return true;
636   }
637   return false;
638 }
639
640 void Bindings::removeAllEvents()
641 {
642   for (int i = 0; i < EventAction::NUM_EVENT_ACTION; ++i) {
643     while (!_eventlist[i].empty()) {
644       _eventlist[i].pop_front();
645     }
646   }
647 }
648
649 void Bindings::fireEvent(EventData *data)
650 {
651   EventCallbackList::iterator c_it, c_end = _eventlist[data->action].end();
652   for (c_it = _eventlist[data->action].begin(); c_it != c_end; ++c_it)
653     c_it->fire(data);
654 }
655
656 }