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