Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

/src/bindings.cc

Go to the documentation of this file.
00001 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 # include "../config.h"
00005 #endif
00006 
00007 #include "bindings.hh"
00008 #include "screen.hh"
00009 #include "openbox.hh"
00010 #include "client.hh"
00011 #include "frame.hh"
00012 #include "python.hh"
00013 #include "otk/display.hh"
00014 
00015 extern "C" {
00016 #include <X11/Xlib.h>
00017 
00018 #include "gettext.h"
00019 #define _(str) gettext(str)
00020 }
00021 
00022 #include <algorithm>
00023 
00024 namespace ob {
00025 
00026 static bool buttonvalue(const std::string &button, unsigned int *val)
00027 {
00028   if (button == "Left" || button == "1" || button == "Button1") {
00029     *val |= Button1;
00030   } else if (button == "Middle" || button == "2" || button == "Button2") {
00031     *val |= Button2;
00032   } else if (button == "Right" || button == "3" || button == "Button3") {
00033     *val |= Button3;
00034   } else if (button == "Up" || button == "4" || button == "Button4") {
00035     *val |= Button4;
00036   } else if (button == "Down" || button == "5" || button == "Button5") {
00037     *val |= Button5;
00038   } else
00039     return false;
00040   return true;
00041 }
00042 
00043 static bool modvalue(const std::string &mod, unsigned int *val)
00044 {
00045   if (mod == "C") {           // control
00046     *val |= ControlMask;
00047   } else if (mod == "S") {    // shift
00048     *val |= ShiftMask;
00049   } else if (mod == "A" ||    // alt/mod1
00050              mod == "M" ||
00051              mod == "Mod1" ||
00052              mod == "M1") {
00053     *val |= Mod1Mask;
00054   } else if (mod == "Mod2" ||   // mod2
00055              mod == "M2") {
00056     *val |= Mod2Mask;
00057   } else if (mod == "Mod3" ||   // mod3
00058              mod == "M3") {
00059     *val |= Mod3Mask;
00060   } else if (mod == "W" ||    // windows/mod4
00061              mod == "Mod4" ||
00062              mod == "M4") {
00063     *val |= Mod4Mask;
00064   } else if (mod == "Mod5" ||   // mod5
00065              mod == "M5") {
00066     *val |= Mod5Mask;
00067   } else {                    // invalid
00068     return false;
00069   }
00070   return true;
00071 }
00072 
00073 bool Bindings::translate(const std::string &str, Binding &b,bool askey) const
00074 {
00075   // parse out the base key name
00076   std::string::size_type keybegin = str.find_last_of('-');
00077   keybegin = (keybegin == std::string::npos) ? 0 : keybegin + 1;
00078   std::string key(str, keybegin);
00079 
00080   // parse out the requested modifier keys
00081   unsigned int modval = 0;
00082   std::string::size_type begin = 0, end;
00083   while (begin != keybegin) {
00084     end = str.find_first_of('-', begin);
00085 
00086     std::string mod(str, begin, end-begin);
00087     if (!modvalue(mod, &modval)) {
00088       printf(_("Invalid modifier element in key binding: %s\n"), mod.c_str());
00089       return false;
00090     }
00091     
00092     begin = end + 1;
00093   }
00094 
00095   // set the binding
00096   b.modifiers = modval;
00097   if (askey) {
00098     KeySym sym = XStringToKeysym(const_cast<char *>(key.c_str()));
00099     if (sym == NoSymbol) {
00100       printf(_("Invalid Key name in key binding: %s\n"), key.c_str());
00101       return false;
00102     }
00103     if (!(b.key = XKeysymToKeycode(**otk::display, sym)))
00104       printf(_("No valid keycode for Key in key binding: %s\n"), key.c_str());
00105     return b.key != 0;
00106   } else {
00107     return buttonvalue(key, &b.key);
00108   }
00109 }
00110 
00111 static void destroytree(KeyBindingTree *tree)
00112 {
00113   while (tree) {
00114     KeyBindingTree *c = tree->first_child;
00115     delete tree;
00116     tree = c;
00117   }
00118 }
00119 
00120 KeyBindingTree *Bindings::buildtree(const StringVect &keylist,
00121                                       PyObject *callback) const
00122 {
00123   if (keylist.empty()) return 0; // nothing in the list.. return 0
00124 
00125   KeyBindingTree *ret = 0, *p;
00126 
00127   StringVect::const_reverse_iterator it, end = keylist.rend();
00128   for (it = keylist.rbegin(); it != end; ++it) {
00129     p = ret;
00130     ret = new KeyBindingTree();
00131     if (!p) {
00132       // this is the first built node, the bottom node of the tree
00133       ret->chain = false;
00134       ret->callbacks.push_back(callback);
00135     }
00136     ret->first_child = p;
00137     if (!translate(*it, ret->binding)) {
00138       destroytree(ret);
00139       ret = 0;
00140       break;
00141     }
00142   }
00143   return ret;
00144 }
00145 
00146 
00147 Bindings::Bindings()
00148   : _curpos(&_keytree),
00149     _resetkey(0,0),
00150     _timer((otk::Timer *) 0),
00151     _keybgrab_callback(0)
00152 {
00153 //  setResetKey("C-g"); // set the default reset key
00154 }
00155 
00156 
00157 Bindings::~Bindings()
00158 {
00159   if (_timer)
00160     delete _timer;
00161 
00162   removeAllKeys();
00163   //removeAllButtons(); // this is done by each client as they are unmanaged
00164   removeAllEvents();
00165 }
00166 
00167 
00168 void Bindings::assimilate(KeyBindingTree *node)
00169 {
00170   KeyBindingTree *a, *b, *tmp, *last;
00171 
00172   if (!_keytree.first_child) {
00173     // there are no nodes at this level yet
00174     _keytree.first_child = node;
00175   } else {
00176     a = _keytree.first_child;
00177     last = a;
00178     b = node;
00179     while (a) {
00180       last = a;
00181       if (a->binding != b->binding) {
00182         a = a->next_sibling;
00183       } else {
00184         tmp = b;
00185         b = b->first_child;
00186         delete tmp;
00187         a = a->first_child;
00188       }
00189     }
00190     if (last->binding != b->binding)
00191       last->next_sibling = b;
00192     else {
00193       last->first_child = b->first_child;
00194       delete b;
00195     }
00196   }
00197 }
00198 
00199 
00200 KeyBindingTree *Bindings::find(KeyBindingTree *search,
00201                                  bool *conflict) const {
00202   *conflict = false;
00203   KeyBindingTree *a, *b;
00204   a = _keytree.first_child;
00205   b = search;
00206   while (a && b) {
00207     if (a->binding != b->binding) {
00208       a = a->next_sibling;
00209     } else {
00210       if (a->chain == b->chain) {
00211   if (!a->chain) {
00212           // found it! (return the actual id, not the search's)
00213     return a;
00214         }
00215       } else {
00216         *conflict = true;
00217         return 0; // the chain status' don't match (conflict!)
00218       }
00219       b = b->first_child;
00220       a = a->first_child;
00221     }
00222   }
00223   return 0; // it just isn't in here
00224 }
00225 
00226 
00227 bool Bindings::addKey(const StringVect &keylist, PyObject *callback)
00228 {
00229   KeyBindingTree *tree, *t;
00230   bool conflict;
00231 
00232   if (!(tree = buildtree(keylist, callback)))
00233     return false; // invalid binding requested
00234 
00235   t = find(tree, &conflict);
00236   if (conflict) {
00237     // conflicts with another binding
00238     destroytree(tree);
00239     return false;
00240   }
00241 
00242   if (t) {
00243     // already bound to something
00244     t->callbacks.push_back(callback);
00245     destroytree(tree);
00246   } else {
00247     // grab the server here to make sure no key pressed go missed
00248     otk::display->grab();
00249     grabKeys(false);
00250 
00251     // assimilate this built tree into the main tree
00252     assimilate(tree); // assimilation destroys/uses the tree
00253 
00254     grabKeys(true); 
00255     otk::display->ungrab();
00256   }
00257  
00258   Py_INCREF(callback);
00259 
00260   return true;
00261 }
00262 
00263 /*
00264 bool Bindings::removeKey(const StringVect &keylist, PyObject *callback)
00265 {
00266   assert(false); // XXX: function not implemented yet
00267 
00268   KeyBindingTree *tree;
00269   bool conflict;
00270 
00271   if (!(tree = buildtree(keylist, 0)))
00272     return false; // invalid binding requested
00273 
00274   KeyBindingTree *t = find(tree, &conflict);
00275   if (t) {
00276     CallbackList::iterator it = std::find(t->callbacks.begin(),
00277                                           t->callbacks.end(),
00278                                           callback);
00279     if (it != t->callbacks.end()) {
00280       // grab the server here to make sure no key pressed go missed
00281       otk::display->grab();
00282       grabKeys(false);
00283       
00284       _curpos = &_keytree;
00285       
00286       // XXX do shit here ...
00287       Py_XDECREF(*it);
00288       
00289       grabKeys(true);
00290       otk::display->ungrab();
00291       
00292       return true;
00293     }
00294   }
00295   return false;
00296 }
00297 */
00298 
00299 void Bindings::setResetKey(const std::string &key)
00300 {
00301   Binding b(0, 0);
00302   if (translate(key, b)) {
00303     // grab the server here to make sure no key pressed go missed
00304     otk::display->grab();
00305     grabKeys(false);
00306     _resetkey.key = b.key;
00307     _resetkey.modifiers = b.modifiers;
00308     grabKeys(true);
00309     otk::display->ungrab();
00310   }
00311 }
00312 
00313 
00314 static void remove_branch(KeyBindingTree *first)
00315 {
00316   KeyBindingTree *p = first;
00317 
00318   while (p) {
00319     if (p->first_child)
00320       remove_branch(p->first_child);
00321     KeyBindingTree *s = p->next_sibling;
00322     while(!p->callbacks.empty()) {
00323       Py_XDECREF(p->callbacks.front());
00324       p->callbacks.pop_front();
00325     }
00326     delete p;
00327     p = s;
00328   }
00329 }
00330 
00331 
00332 void Bindings::removeAllKeys()
00333 {
00334   grabKeys(false);
00335   if (_keytree.first_child) {
00336     remove_branch(_keytree.first_child);
00337     _keytree.first_child = 0;
00338   }
00339   grabKeys(true);
00340 }
00341 
00342 
00343 void Bindings::grabKeys(bool grab)
00344 {
00345   for (int i = 0; i < ScreenCount(**otk::display); ++i) {
00346     Screen *sc = openbox->screen(i);
00347     if (!sc) continue; // not a managed screen
00348     Window root = otk::display->screenInfo(i)->rootWindow();
00349 
00350     KeyBindingTree *p = _curpos->first_child;
00351     while (p) {
00352       if (grab) {
00353         otk::display->grabKey(p->binding.key, p->binding.modifiers,
00354                                 root, false, GrabModeAsync, GrabModeAsync,
00355                                 false);
00356       }
00357       else
00358         otk::display->ungrabKey(p->binding.key, p->binding.modifiers,
00359                                   root);
00360       p = p->next_sibling;
00361     }
00362 
00363     if (_resetkey.key)
00364       if (grab)
00365         otk::display->grabKey(_resetkey.key, _resetkey.modifiers,
00366                                 root, false, GrabModeAsync, GrabModeAsync,
00367                                 false);
00368       else
00369         otk::display->ungrabKey(_resetkey.key, _resetkey.modifiers,
00370                                   root);
00371   }
00372 }
00373 
00374 
00375 bool Bindings::grabKeyboard(int screen, PyObject *callback)
00376 {
00377   assert(callback);
00378   if (_keybgrab_callback) return false; // already grabbed
00379 
00380   if (!openbox->screen(screen))
00381     return false; // the screen is not managed
00382   
00383   Window root = otk::display->screenInfo(screen)->rootWindow();
00384   if (XGrabKeyboard(**otk::display, root, false, GrabModeAsync,
00385                     GrabModeAsync, CurrentTime))
00386     return false;
00387   _keybgrab_callback = callback;
00388   return true;
00389 }
00390 
00391 
00392 void Bindings::ungrabKeyboard()
00393 {
00394   if (!_keybgrab_callback) return; // not grabbed
00395 
00396   _keybgrab_callback = 0;
00397   XUngrabKeyboard(**otk::display, CurrentTime);
00398   XUngrabPointer(**otk::display, CurrentTime);
00399 }
00400 
00401 
00402 bool Bindings::grabPointer(int screen)
00403 {
00404   if (!openbox->screen(screen))
00405     return false; // the screen is not managed
00406   
00407   Window root = otk::display->screenInfo(screen)->rootWindow();
00408   XGrabPointer(**otk::display, root, false, 0, GrabModeAsync,
00409                GrabModeAsync, None, None, CurrentTime);
00410   return true;
00411 }
00412 
00413 
00414 void Bindings::ungrabPointer()
00415 {
00416   XUngrabPointer(**otk::display, CurrentTime);
00417 }
00418 
00419 
00420 void Bindings::fireKey(int screen, unsigned int modifiers, unsigned int key,
00421                        Time time, KeyAction::KA action)
00422 {
00423   if (_keybgrab_callback) {
00424     Client *c = openbox->focusedClient();
00425     KeyData data(screen, c, time, modifiers, key, action);
00426     python_callback(_keybgrab_callback, &data);
00427   }
00428   
00429   // KeyRelease events only occur during keyboard grabs
00430   if (action == KeyAction::Release) return;
00431     
00432   if (key == _resetkey.key && modifiers == _resetkey.modifiers) {
00433     resetChains(this);
00434   } else {
00435     KeyBindingTree *p = _curpos->first_child;
00436     while (p) {
00437       if (p->binding.key == key && p->binding.modifiers == modifiers) {
00438         if (p->chain) {
00439           if (_timer)
00440             delete _timer;
00441           _timer = new otk::Timer(5000, // 5 second timeout
00442                                   (otk::Timer::TimeoutHandler)resetChains,
00443                                   this);
00444           // grab the server here to make sure no key presses get missed
00445           otk::display->grab();
00446           grabKeys(false);
00447           _curpos = p;
00448           grabKeys(true);
00449           otk::display->ungrab();
00450         } else {
00451           Client *c = openbox->focusedClient();
00452           KeyData data(screen, c, time, modifiers, key, action);
00453           CallbackList::iterator it, end = p->callbacks.end();
00454           for (it = p->callbacks.begin(); it != end; ++it)
00455             python_callback(*it, &data);
00456           resetChains(this);
00457         }
00458         break;
00459       }
00460       p = p->next_sibling;
00461     }
00462   }
00463 }
00464 
00465 void Bindings::resetChains(Bindings *self)
00466 {
00467   if (self->_timer) {
00468     delete self->_timer;
00469     self->_timer = (otk::Timer *) 0;
00470   }
00471   // grab the server here to make sure no key pressed go missed
00472   otk::display->grab();
00473   self->grabKeys(false);
00474   self->_curpos = &self->_keytree;
00475   self->grabKeys(true);
00476   otk::display->ungrab();
00477 }
00478 
00479 
00480 bool Bindings::addButton(const std::string &but, MouseContext::MC context,
00481                            MouseAction::MA action, PyObject *callback)
00482 {
00483   assert(context >= 0 && context < MouseContext::NUM_MOUSE_CONTEXT);
00484   assert(action >= 0 && action < MouseAction::NUM_MOUSE_ACTION);
00485   
00486   Binding b(0,0);
00487   if (!translate(but, b, false))
00488     return false;
00489 
00490   ButtonBindingList::iterator it, end = _buttons[context].end();
00491 
00492   // look for a duplicate binding
00493   for (it = _buttons[context].begin(); it != end; ++it)
00494     if ((*it)->binding.key == b.key &&
00495         (*it)->binding.modifiers == b.modifiers) {
00496       break;
00497     }
00498 
00499   ButtonBinding *bind;
00500   
00501   // the binding didnt exist yet, add it
00502   if (it == end) {
00503     bind = new ButtonBinding();
00504     bind->binding.key = b.key;
00505     bind->binding.modifiers = b.modifiers;
00506     _buttons[context].push_back(bind);
00507     // grab the button on all clients
00508     for (int sn = 0; sn < ScreenCount(**otk::display); ++sn) {
00509       Screen *s = openbox->screen(sn);
00510       if (!s) continue; // not managed
00511       Client::List::iterator c_it, c_end = s->clients.end();
00512       for (c_it = s->clients.begin(); c_it != c_end; ++c_it) {
00513         grabButton(true, bind->binding, context, *c_it);
00514       }
00515     }
00516   } else
00517     bind = *it;
00518   bind->callbacks[action].push_back(callback);
00519   Py_INCREF(callback);
00520   return true;
00521 }
00522 
00523 void Bindings::removeAllButtons()
00524 {
00525   for (int i = 0; i < MouseContext::NUM_MOUSE_CONTEXT; ++i) {
00526     ButtonBindingList::iterator it, end = _buttons[i].end();
00527     for (it = _buttons[i].begin(); it != end; ++it) {
00528       for (int a = 0; a < MouseAction::NUM_MOUSE_ACTION; ++a) {
00529         while (!(*it)->callbacks[a].empty()) {
00530           Py_XDECREF((*it)->callbacks[a].front());
00531           (*it)->callbacks[a].pop_front();
00532         }
00533       }
00534       // ungrab the button on all clients
00535       for (int sn = 0; sn < ScreenCount(**otk::display); ++sn) {
00536         Screen *s = openbox->screen(sn);
00537         if (!s) continue; // not managed
00538         Client::List::iterator c_it, c_end = s->clients.end();
00539         for (c_it = s->clients.begin(); c_it != c_end; ++c_it) {
00540           grabButton(false, (*it)->binding, (MouseContext::MC)i, *c_it);
00541         }
00542       }
00543     }
00544   }
00545 }
00546 
00547 void Bindings::grabButton(bool grab, const Binding &b,
00548                           MouseContext::MC context, Client *client)
00549 {
00550   Window win;
00551   int mode = GrabModeAsync;
00552   unsigned int mask;
00553   switch(context) {
00554   case MouseContext::Frame:
00555     win = client->frame->window();
00556     mask = ButtonPressMask | ButtonMotionMask | ButtonReleaseMask;
00557     break;
00558   case MouseContext::Window:
00559     win = client->frame->plate();
00560     mode = GrabModeSync; // this is handled in fireButton
00561     mask = ButtonPressMask; // can't catch more than this with Sync mode
00562                             // the release event is manufactured by the
00563                             // master buttonPressHandler
00564     break;
00565   default:
00566     // any other elements already get button events, don't grab on them
00567     return;
00568   }
00569   if (grab)
00570     otk::display->grabButton(b.key, b.modifiers, win, false, mask, mode,
00571                              GrabModeAsync, None, None, false);
00572   else
00573     otk::display->ungrabButton(b.key, b.modifiers, win);
00574 }
00575 
00576 void Bindings::grabButtons(bool grab, Client *client)
00577 {
00578   for (int i = 0; i < MouseContext::NUM_MOUSE_CONTEXT; ++i) {
00579     ButtonBindingList::iterator it, end = _buttons[i].end();
00580     for (it = _buttons[i].begin(); it != end; ++it)
00581       grabButton(grab, (*it)->binding, (MouseContext::MC)i, client);
00582   }
00583 }
00584 
00585 void Bindings::fireButton(MouseData *data)
00586 {
00587   if (data->context == MouseContext::Window) {
00588     // Replay the event, so it goes to the client
00589     XAllowEvents(**otk::display, ReplayPointer, data->time);
00590   }
00591   
00592   ButtonBindingList::iterator it, end = _buttons[data->context].end();
00593   for (it = _buttons[data->context].begin(); it != end; ++it)
00594     if ((*it)->binding.key == data->button &&
00595         (*it)->binding.modifiers == data->state) {
00596       CallbackList::iterator c_it,c_end = (*it)->callbacks[data->action].end();
00597       for (c_it = (*it)->callbacks[data->action].begin();
00598            c_it != c_end; ++c_it)
00599         python_callback(*c_it, data);
00600     }
00601 }
00602 
00603 
00604 bool Bindings::addEvent(EventAction::EA action, PyObject *callback)
00605 {
00606   if (action < 0 || action >= EventAction::NUM_EVENTS) {
00607     return false;
00608   }
00609 #ifdef    XKB
00610   if (action == EventAction::Bell && _eventlist[action].empty())
00611     XkbSelectEvents(**otk::display, XkbUseCoreKbd,
00612                     XkbBellNotifyMask, XkbBellNotifyMask);
00613 #endif // XKB
00614   _eventlist[action].push_back(callback);
00615   Py_INCREF(callback);
00616   return true;
00617 }
00618 
00619 bool Bindings::removeEvent(EventAction::EA action, PyObject *callback)
00620 {
00621   if (action < 0 || action >= EventAction::NUM_EVENTS) {
00622     return false;
00623   }
00624   
00625   CallbackList::iterator it = std::find(_eventlist[action].begin(),
00626                                         _eventlist[action].end(),
00627                                         callback);
00628   if (it != _eventlist[action].end()) {
00629     Py_XDECREF(*it);
00630     _eventlist[action].erase(it);
00631 #ifdef    XKB
00632     if (action == EventAction::Bell && _eventlist[action].empty())
00633       XkbSelectEvents(**otk::display, XkbUseCoreKbd,
00634                       XkbBellNotifyMask, 0);
00635 #endif // XKB
00636     return true;
00637   }
00638   return false;
00639 }
00640 
00641 void Bindings::removeAllEvents()
00642 {
00643   for (int i = 0; i < EventAction::NUM_EVENTS; ++i) {
00644     while (!_eventlist[i].empty()) {
00645       Py_XDECREF(_eventlist[i].front());
00646       _eventlist[i].pop_front();
00647     }
00648   }
00649 }
00650 
00651 void Bindings::fireEvent(EventData *data)
00652 {
00653   CallbackList::iterator c_it, c_end = _eventlist[data->action].end();
00654   for (c_it = _eventlist[data->action].begin(); c_it != c_end; ++c_it)
00655     python_callback(*c_it, data);
00656 }
00657 
00658 }

Generated on Tue Feb 4 22:58:57 2003 for Openbox by doxygen1.3-rc2