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