"Fossies" - the Fresh Open Source Software Archive

Member "bbkeys-0.9.1/src/KeyClient.cpp" (22 Dec 2008, 14829 Bytes) of package /linux/privat/old/bbkeys-0.9.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "KeyClient.cpp" see the Fossies "Dox" file reference documentation.

    1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
    2 // -- KeyClient.cpp --
    3 // Copyright (c) 2001 - 2003 Jason 'vanRijn' Kasper <vR at movingparts dot net>
    4 //
    5 // Permission is hereby granted, free of charge, to any person obtaining a
    6 // copy of this software and associated documentation files (the "Software"),
    7 // to deal in the Software without restriction, including without limitation
    8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
    9 // and/or sell copies of the Software, and to permit persons to whom the
   10 // Software is furnished to do so, subject to the following conditions:
   11 //
   12 // The above copyright notice and this permission notice shall be included in
   13 // all copies or substantial portions of the Software.
   14 //
   15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   21 // DEALINGS IN THE SOFTWARE.
   22 
   23 // E_O_H_VR
   24 
   25 #include "../config.h"
   26 
   27 extern "C" {
   28 
   29 #ifdef    HAVE_SIGNAL_H
   30 #  include <signal.h>
   31 #endif // HAVE_SIGNAL_H
   32 
   33 #ifdef    HAVE_SYS_SIGNAL_H
   34 #  include <sys/signal.h>
   35 #endif // HAVE_SYS_SIGNAL_H
   36 
   37 #ifdef    HAVE_UNISTD_H
   38 # include <unistd.h>
   39 # include <sys/types.h>
   40 #endif // HAVE_UNISTD_H
   41 
   42 #ifdef    HAVE_SYS_STAT_H
   43 #  include <sys/types.h>
   44 #  include <sys/stat.h>
   45 #endif // HAVE_SYS_STAT_H
   46 
   47 #include <sys/types.h>
   48 #include <sys/wait.h>
   49 
   50 }
   51 
   52 #include "version.h"
   53 
   54 #include "KeyClient.h"
   55 #include "LocalUtil.h"
   56 #include "actions.hh"
   57 
   58 #include <iostream>
   59 #include <algorithm>
   60 #include <vector>
   61 #include <strings.h>
   62 
   63 //--------------------------------------------------------
   64 // Constructor/Destructor
   65 //--------------------------------------------------------
   66 KeyClient::KeyClient (int argc, char **argv,
   67                       Config & config, std::string display):
   68   bt::Application(BBTOOL, display.c_str(), true), _config(config),
   69   _keybindings(0), config_check_timer(0)
   70 {
   71 
   72   // save off what we're constructed with for reconfiguring later...
   73   _argc = argc;
   74   _argv = argv;
   75 
   76   // initialize our keyword map for the file tokenizer
   77   initKeywords(_keywordMap);
   78 
   79   // now connect to the X server
   80   _display = XDisplay();
   81   if (! _display ) {
   82     cerr << BBTOOL << ": " << "KeyClient: ERROR: Can't connect to X Server. Bummer! Exiting\n";
   83     exit(2);
   84   }
   85 
   86   // check to see if we've been handed another config-file to use
   87   _configFileName = bt::expandTilde(_config.getStringValue("config",
   88                                     "~/.bbkeysrc") );
   89 
   90   struct stat buf;
   91   if (0 != stat(_configFileName.c_str(), &buf) ||!S_ISREG(buf.st_mode)) {
   92     cerr << BBTOOL << ": " << "KeyClient: ERROR: Couldn't load rc-file: [" << _configFileName
   93          << "], falling back to default: [" << DEFAULTRC << "]\n";
   94     _configFileName = DEFAULTRC;
   95   } else {
   96     _last_time_config_changed = buf.st_mtime;
   97   }
   98 
   99   _debug = _config.getBoolValue("debug", false);
  100 
  101   // here's our friendly little general-purpose keygrabber
  102   _keyGrabber = new KeyGrabber(_display, numLockMask(), scrollLockMask() );
  103 
  104   _netclient = new Netclient(this->display());
  105   _active = _clients.end();
  106 
  107   initialize();
  108 }
  109 
  110 KeyClient::~KeyClient ()
  111 {
  112 
  113   // delete all screens
  114   for_each(screenList.begin(), screenList.end(), bt::PointerAssassin());
  115 
  116   if (_keybindings) delete _keybindings;
  117   if (_netclient) delete _netclient;
  118   if (_keyGrabber) delete _keyGrabber;
  119 }
  120 
  121 void KeyClient::initialize() {
  122 
  123   // now, read in our configuration file and set both program settings
  124   // and keybindings we're asked to handle
  125   handleConfigFile();
  126 
  127   // parse command options again to override what we read in from config file
  128   parseOptions( _argc, _argv, _config );
  129 
  130 
  131   // now create a screen handler for each screen that exists
  132   for (unsigned int i = 0; i < bt::Application::display().screenCount(); i++) {
  133     ScreenHandler *screen = new ScreenHandler(this, i);
  134     if (! screen->isManaged()) {
  135       delete screen;
  136       continue;
  137     }
  138 
  139     screen->initialize();
  140 
  141     // add this screen to our collection
  142     screenList.push_back(screen);
  143   }
  144 
  145   if (screenList.empty()) {
  146     cerr << BBTOOL << ": " << "KeyClient: initialize: no compatible window managers found, aborting.\n";
  147     ::exit(3);
  148   }
  149 
  150   _autoConfigCheckTimeout = (_config.getNumberValue("autoConfigCheckTimeout", 10)) * 1000;
  151   _autoConfig = _config.getBoolValue("autoConfig", true);
  152   if (_autoConfig) {
  153     if (!config_check_timer) {
  154       config_check_timer = new bt::Timer(this, this);
  155     }
  156     config_check_timer->setTimeout(_autoConfigCheckTimeout);
  157     config_check_timer->recurring(True);
  158     config_check_timer->start();
  159   }
  160 
  161 }
  162 
  163 //--------------------------------------------------------
  164 // reconfigure
  165 //--------------------------------------------------------
  166 void KeyClient::reconfigure ()
  167 {
  168   std::cout << BBTOOL << ": " << 
  169     "KeyClient: reconfigure: hey, goodie! I got a reconfigure request!!\n";
  170 
  171   // delete all screens
  172   for_each(screenList.begin(), screenList.end(), bt::PointerAssassin());
  173   screenList.clear();
  174 
  175   // initialize and/or clear our config
  176   _config.reset();
  177 
  178   // reset our timer
  179   if (config_check_timer) {
  180     config_check_timer->halt();
  181   }
  182 
  183   initialize();
  184 
  185 }
  186 
  187 
  188 void KeyClient::handleConfigFile() {
  189 
  190   FileTokenizer tokenizer(_keywordMap, _configFileName.c_str());
  191 
  192   // clear off any of our keybindings we have and get them ready to go
  193   if (_keybindings) {
  194     _keybindings->unloadBindings();
  195   } else {
  196     _keybindings = new keytree(_display);
  197   }
  198 
  199   _keybindings->reset();
  200 
  201   bool _doingConfig = false, _doingKeybindings = false;
  202 
  203   TokenBlock *block = 0;
  204   while ((block = tokenizer.next())) {
  205     switch (block->tag) {
  206     case ConfigOpts::begin: // um, we ignore these. =:)
  207       break;
  208     case ConfigOpts::end:
  209       if (_doingConfig) {
  210         _doingConfig = false;
  211       }
  212       break;
  213     case ConfigOpts::config:
  214       _doingConfig = true;
  215       break;
  216     case ConfigOpts::option:
  217       if (_debug)
  218         cout << BBTOOL << ": " << "got a config option!, setting key: [" << block->name
  219              << "] to value: [" << block->data << "]\n";
  220 
  221       _config.setOption(block->name, block->data);
  222       break;
  223     case ConfigOpts::keybindings:
  224       _doingKeybindings = true;
  225       setKeybindings(tokenizer);
  226 
  227       if (_debug)
  228         _keybindings->showTree();
  229 
  230       break;
  231     default:
  232       cerr << BBTOOL << ": " << "unknown tag found in ConfigOpts block: ["
  233            << block->tag << "], name: [" << block->name
  234            << "], data: [" << block->data << "]\n";
  235       break;
  236     }
  237     delete block;
  238   }
  239 
  240   if (_debug) {
  241     cout << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n";
  242     _config.showOptions();
  243   }
  244 
  245 }
  246 
  247 void KeyClient::setKeybindings(FileTokenizer & tokenizer) {
  248 
  249   // our modifier masks
  250   struct {
  251     const char *str;
  252     unsigned int mask;
  253   }
  254   modifiers[] = {
  255     { "mod1", Mod1Mask },
  256     { "mod2", Mod2Mask },
  257     { "mod3", Mod3Mask },
  258     { "mod4", Mod4Mask },
  259     { "mod5", Mod5Mask },
  260     { "control", ControlMask },
  261     { "shift", ShiftMask },
  262     { "", 0 }
  263   };
  264 
  265   // this tells us how many levels deep we're nested. this will tell us when to return
  266   int _iLevels =0;
  267 
  268   TokenBlock *block = 0;
  269   while ((block = tokenizer.next())) {
  270 
  271     // if we hit an end, return
  272     if (block->tag == ConfigOpts::end) {
  273       --_iLevels;
  274       _keybindings->retract();
  275       // 0 is our root level, so if we're below that, we need to bail out
  276       if (_iLevels <0) {
  277         if (block) delete block;
  278         return;
  279       }
  280     } else {
  281 
  282       string fullKey = block->name;
  283 
  284       if (fullKey.size() <=0) {
  285         cerr << BBTOOL << ": " << "ERROR: No key or modifier given. Ignoring this one, Jimmy.\n";
  286         if (block) delete block;
  287         continue;
  288       }
  289       // first, split our string containing our keys/modifiers and separate
  290       // the keys from the modifiers
  291       vector<string> results;
  292       int matches = LocalUtil::splitString(fullKey, "-", results);
  293 
  294       // here's our keyname. make sure it's a valid key
  295       string _key = results[results.size() -1];
  296 
  297       KeySym sym = XStringToKeysym(_key.c_str());
  298       if (sym == 0) {
  299         cerr << BBTOOL << ": " << "ERROR: Invalid key (" << _key << ")! This may cause odd behavior.\n";
  300       }
  301 
  302       // now iterate through our modifiers and try to match the given string
  303       // to a modifier mask. if we find it, xor it together with what we already have
  304       unsigned int _mask=0;
  305 
  306       for (int j=0; j < (matches -1); j++) {
  307 
  308         bool found=false;
  309         string mod = results[j];
  310 
  311         for (int i = 0; modifiers[i].str[0] != '\0'; ++i) {
  312           if ( strcasecmp(modifiers[i].str, mod.c_str()) == 0 ) {
  313             _mask |= modifiers[i].mask;
  314             found = true;
  315             break;
  316           }
  317         }
  318         if (!found) cerr << BBTOOL << ": " << "ERROR: Couldn't find modifier for mod: [" << mod << "]\n";
  319       }
  320 
  321       // now, if we have a chain, nest down a level and add the keybinding
  322       if (block->tag == Action::chain) {
  323         _iLevels++;
  324         _keybindings->advanceOnNewNode();
  325         _keybindings->setCurrentNodeProps(static_cast<Action::ActionType>(block->tag),
  326                                           _mask, _key, block->data);
  327       } else {
  328         _keybindings->addAction(static_cast<Action::ActionType>(block->tag),
  329                                 _mask, _key, block->data);
  330       }
  331     }
  332     if (block) delete block;
  333   }
  334 }
  335 
  336 void KeyClient::initKeywords(KeywordMap& keywords) {
  337 
  338   // load our map with our keybinding labels
  339   keywords.insert(KeywordMap::value_type("execute", Action::execute));
  340   keywords.insert(KeywordMap::value_type("iconify", Action::iconify));
  341   keywords.insert(KeywordMap::value_type("raise", Action::raise));
  342   keywords.insert(KeywordMap::value_type("lower", Action::lower));
  343   keywords.insert(KeywordMap::value_type("close", Action::close));
  344   keywords.insert(KeywordMap::value_type("toggleshade", Action::toggleShade));
  345   keywords.insert(KeywordMap::value_type("toggleomnipresent", Action::toggleOmnipresent));
  346   keywords.insert(KeywordMap::value_type("movewindowup", Action::moveWindowUp));
  347   keywords.insert(KeywordMap::value_type("movewindowdown", Action::moveWindowDown));
  348   keywords.insert(KeywordMap::value_type("movewindowleft", Action::moveWindowLeft));
  349   keywords.insert(KeywordMap::value_type("movewindowright", Action::moveWindowRight));
  350   keywords.insert(KeywordMap::value_type("resizewindowwidth", Action::resizeWindowWidth));
  351   keywords.insert(KeywordMap::value_type("resizewindowheight", Action::resizeWindowHeight));
  352 
  353   keywords.insert(KeywordMap::value_type("togglemaximizefull", Action::toggleMaximizeFull));
  354   keywords.insert(KeywordMap::value_type("togglemaximizevertical", Action::toggleMaximizeVertical));
  355   keywords.insert(KeywordMap::value_type("togglemaximizehorizontal", Action::toggleMaximizeHorizontal));
  356 
  357   keywords.insert(KeywordMap::value_type("sendtoworkspace", Action::sendToWorkspace));
  358   keywords.insert(KeywordMap::value_type("sendtonextworkspace", Action::sendToNextWorkspace));
  359   keywords.insert(KeywordMap::value_type("sendtoprevworkspace", Action::sendToPrevWorkspace));
  360 
  361   keywords.insert(KeywordMap::value_type("nextwindow", Action::nextWindow));
  362   keywords.insert(KeywordMap::value_type("prevwindow", Action::prevWindow));
  363   keywords.insert(KeywordMap::value_type("nextwindowonallworkspaces", Action::nextWindowOnAllWorkspaces));
  364   keywords.insert(KeywordMap::value_type("prevwindowonallworkspaces", Action::prevWindowOnAllWorkspaces));
  365 
  366   keywords.insert(KeywordMap::value_type("changeworkspace", Action::changeWorkspace));
  367   keywords.insert(KeywordMap::value_type("nextworkspace", Action::nextWorkspace));
  368   keywords.insert(KeywordMap::value_type("prevworkspace", Action::prevWorkspace));
  369 
  370   keywords.insert(KeywordMap::value_type("upworkspace", Action::upWorkspace));
  371   keywords.insert(KeywordMap::value_type("downworkspace", Action::downWorkspace));
  372   keywords.insert(KeywordMap::value_type("leftworkspace", Action::leftWorkspace));
  373   keywords.insert(KeywordMap::value_type("rightworkspace", Action::rightWorkspace));
  374 
  375   keywords.insert(KeywordMap::value_type("nextscreen", Action::nextScreen));
  376   keywords.insert(KeywordMap::value_type("prevscreen", Action::prevScreen));
  377 
  378   keywords.insert(KeywordMap::value_type("showrootmenu", Action::showRootMenu));
  379   keywords.insert(KeywordMap::value_type("showworkspacemenu", Action::showWorkspaceMenu));
  380   keywords.insert(KeywordMap::value_type("toggledecorations", Action::toggleDecorations));
  381 
  382   keywords.insert(KeywordMap::value_type("togglegrabs", Action::toggleGrabs));
  383   keywords.insert(KeywordMap::value_type("chain", Action::chain));
  384 
  385   // the words associated with our high-level file labels
  386   keywords.insert(KeywordMap::value_type("begin", ConfigOpts::begin));
  387   keywords.insert(KeywordMap::value_type("end", ConfigOpts::end));
  388   keywords.insert(KeywordMap::value_type("config", ConfigOpts::config));
  389   keywords.insert(KeywordMap::value_type("keybindings", ConfigOpts::keybindings));
  390   keywords.insert(KeywordMap::value_type("option", ConfigOpts::option));
  391 
  392 }
  393 
  394 bool KeyClient::process_signal(int sig) {
  395   switch (sig) {
  396   case SIGHUP:
  397     reconfigure();
  398     return true;
  399     break;
  400 
  401   default:
  402     return (bt::Application::process_signal(sig) );
  403   }
  404 
  405 }
  406 
  407 void KeyClient::process_event(XEvent *e) {
  408   // Send the event through the default EventHandlers.
  409   bt::Application::process_event(e);
  410 }
  411 
  412 void KeyClient::cycleScreen(int current, bool forward) const {
  413   unsigned int i;
  414   for (i = 0; i < screenList.size(); ++i)
  415     if (screenList[i]->getScreenNumber() == current) {
  416       current = i;
  417       break;
  418     }
  419   assert(i < screenList.size());  // current is for an unmanaged screen
  420 
  421   int dest = current + (forward ? 1 : -1);
  422 
  423   if (dest < 0) dest = (signed)screenList.size() - 1;
  424   else if (dest >= (signed)screenList.size()) dest = 0;
  425 
  426   const XWindow *target = screenList[dest]->lastActiveWindow();
  427   if (target) target->focus();
  428 }
  429 
  430 void KeyClient::timeout(bt::Timer *timer) {
  431   if (timer == config_check_timer) {
  432     checkConfigFile();
  433   }
  434 }
  435 
  436 void KeyClient::checkConfigFile() {
  437 
  438   struct stat file_status;
  439 
  440   if (stat(_configFileName.c_str(), &file_status) != 0) {
  441     if (_debug)
  442       std::cerr << BBTOOL << ": " << "Could not open config file: [" << _configFileName << "]";
  443   } else if (file_status.st_mtime != _last_time_config_changed) {
  444     if (_debug)
  445       std::cout << BBTOOL << ": " << "KeyClient: checkConfigFile: config file time changed..." << std::endl;
  446     _last_time_config_changed = file_status.st_mtime;
  447     reconfigure();
  448   }
  449 }
  450