wmi

git clone git://oldgit.suckless.org/wmi/
Log | Files | Refs | LICENSE

binder.cpp (23472B)


      1 // Copyright (c) 2003 - 2009 Anselm R Garbe <anselm@garbe.us>
      2 // Copyright (c) 2003 - 2004 Marcel Manthe <schneegloeckchen at gmx.li>
      3 // See LICENSE for license details.
      4 
      5 extern "C" {
      6 #include <assert.h>
      7 }
      8 
      9 #include <sstream>
     10 #include <string>
     11 #include "wmi.h"
     12 
     13 #include "binder.h"
     14 
     15 #include "action.h"
     16 #include "box.h"
     17 #include "client.h"
     18 #include "container.h"
     19 #include "cursors.h"
     20 #include "expander.h"
     21 #include "frame.h"
     22 #include "inputbar.h"
     23 #include "kernel.h"
     24 #include "logger.h"
     25 #include "monitor.h"
     26 #include "shortcut.h"
     27 #include "thing.h"
     28 #include "util.h"
     29 #include "workspace.h"
     30 #include "xcore.h"
     31 
     32 
     33 Binder::Binder() {
     34     actionBindings_ = KERNEL->actionBindings();
     35     initLockModifiers();
     36 #ifdef POSIX_REGEX
     37     temp_pattern_="uninitalized";
     38     regex_prepare();
     39 #endif
     40 }
     41 
     42 Binder::~Binder() {
     43 }
     44 
     45 void Binder::grabShortcut(Shortcut *shortcut, Window window) {
     46 
     47     // update keysym definitions of the action
     48     if (shortcut->button()) {
     49         XCORE->grabButton(window, shortcut->modMask(),
     50                           shortcut->button());
     51 
     52         if (shortcut->modMask() == AnyModifier) {
     53             return;
     54         }
     55         if (numLockMask_) {
     56             XCORE->grabButton(window, shortcut->modMask() | numLockMask_,
     57                               shortcut->button());
     58             XCORE->grabButton(window, shortcut->modMask() | numLockMask_
     59                               | LockMask, shortcut->button());
     60         }
     61         if (scrollLockMask_) {
     62             XCORE->grabButton(window, shortcut->modMask() | scrollLockMask_,
     63                               shortcut->button());
     64             XCORE->grabButton(window, shortcut->modMask() | scrollLockMask_
     65                               | LockMask, shortcut->button());
     66         }
     67         if (numLockMask_ && scrollLockMask_) {
     68             XCORE->grabButton(window, shortcut->modMask() | numLockMask_
     69                     | scrollLockMask_, shortcut->button());
     70             XCORE->grabButton(window, shortcut->modMask() | numLockMask_
     71                     | scrollLockMask_ | LockMask, shortcut->button());
     72         }
     73     }
     74     else if (shortcut->keyCode()) {
     75         XCORE->grabKey(window, shortcut->modMask(),
     76                        shortcut->keyCode());
     77 
     78         if (shortcut->modMask() == AnyModifier) {
     79             return;
     80         }
     81         if (numLockMask_) {
     82             XCORE->grabKey(window, shortcut->modMask() | numLockMask_,
     83                               shortcut->keyCode());
     84             XCORE->grabKey(window, shortcut->modMask() | numLockMask_
     85                               | LockMask, shortcut->keyCode());
     86         }
     87         if (scrollLockMask_) {
     88             XCORE->grabKey(window, shortcut->modMask() | scrollLockMask_,
     89                               shortcut->keyCode());
     90             XCORE->grabKey(window, shortcut->modMask() | scrollLockMask_
     91                               | LockMask, shortcut->keyCode());
     92         }
     93         if (numLockMask_ && scrollLockMask_) {
     94             XCORE->grabKey(window, shortcut->modMask() | numLockMask_
     95                     | scrollLockMask_, shortcut->keyCode());
     96             XCORE->grabKey(window, shortcut->modMask() | numLockMask_
     97                     | scrollLockMask_ | LockMask, shortcut->keyCode());
     98         }
     99     }
    100 }
    101 
    102 void Binder::ungrabShortcut(Shortcut *shortcut, Window window) {
    103 
    104     if (shortcut->button()) {
    105         XCORE->ungrabButton(window, shortcut->modMask(), shortcut->button());
    106 
    107         if (shortcut->modMask() == AnyModifier) {
    108             return;
    109         }
    110         if (numLockMask_) {
    111             XCORE->ungrabButton(window, shortcut->modMask() | numLockMask_,
    112                               shortcut->button());
    113             XCORE->ungrabButton(window, shortcut->modMask() | numLockMask_
    114                               | LockMask, shortcut->button());
    115         }
    116         if (scrollLockMask_) {
    117             XCORE->ungrabButton(window, shortcut->modMask() | scrollLockMask_,
    118                               shortcut->button());
    119             XCORE->ungrabButton(window, shortcut->modMask() | scrollLockMask_
    120                               | LockMask, shortcut->button());
    121         }
    122         if (numLockMask_ && scrollLockMask_) {
    123             XCORE->ungrabButton(window, shortcut->modMask() | numLockMask_
    124                     | scrollLockMask_, shortcut->button());
    125             XCORE->ungrabButton(window, shortcut->modMask() | numLockMask_
    126                     | scrollLockMask_ | LockMask, shortcut->button());
    127         }
    128     }
    129     else {
    130         XCORE->ungrabKey(window, shortcut->modMask(),
    131                     shortcut->keyCode());
    132 
    133         if (shortcut->modMask() == AnyModifier) {
    134             return;
    135         }
    136         if (numLockMask_) {
    137             XCORE->ungrabKey(window, shortcut->modMask() | numLockMask_,
    138                               shortcut->keyCode());
    139             XCORE->ungrabKey(window, shortcut->modMask() | numLockMask_
    140                               | LockMask, shortcut->keyCode());
    141         }
    142         if (scrollLockMask_) {
    143             XCORE->ungrabKey(window, shortcut->modMask() | scrollLockMask_,
    144                               shortcut->keyCode());
    145             XCORE->ungrabKey(window, shortcut->modMask() | scrollLockMask_
    146                               | LockMask, shortcut->keyCode());
    147         }
    148         if (numLockMask_ && scrollLockMask_) {
    149             XCORE->ungrabKey(window, shortcut->modMask() | numLockMask_
    150                     | scrollLockMask_, shortcut->keyCode());
    151             XCORE->ungrabKey(window, shortcut->modMask() | numLockMask_
    152                     | scrollLockMask_ | LockMask, shortcut->keyCode());
    153         }
    154     }
    155 }
    156 
    157 void Binder::initKeys(Window window) {
    158 
    159     for (MBindings::iterator it = actionBindings_->begin();
    160          it != actionBindings_->end(); it++)
    161     {
    162         Action *action = (*it).second;
    163         LOGDEBUG("initKeys: " + action->id());
    164         if (action->listenOn()) {
    165             grabShortcut(action->listenOn(), window);
    166         }
    167     }
    168 }
    169 
    170 string Binder::prettyPrintKeyBindings() {
    171 
    172     ostringstream oss;
    173     for (MBindings::iterator it = actionBindings_->begin();
    174          it != actionBindings_->end(); it++)
    175     {
    176         Action *action = (*it).second;
    177         Shortcut *shortcut = action->shortcut();
    178         Shortcut *tmp = action->shortcut();
    179         if (shortcut) {
    180             oss.width(35);
    181             oss.fill(' ');
    182             oss << action->id() << " = ";
    183         }
    184         while (tmp) {
    185             oss.width(0);
    186             if (tmp->button()) {
    187                 oss << Util::stringForModMask(tmp->modMask())
    188                     << Util::stringForButton(tmp->button());
    189             }
    190             else {
    191                 oss << Util::stringForModMask(tmp->modMask())
    192                     << XCORE->keyCodeToString(tmp->keyCode());
    193             }
    194             tmp = tmp->next();
    195             if (tmp) {
    196                 oss << " :: ";
    197             }
    198         }
    199         if (shortcut) {
    200             oss << "\n";
    201         }
    202     }
    203 
    204     string result = oss.str();
    205     if (result == "") {
    206         result = "no key bindings defined\n";
    207     }
    208     return result;
    209 }
    210 
    211 void Binder::handleButton(Window window, XButtonEvent *event)
    212 {
    213     LOGDEBUG("handle button press event");
    214     handleShortcut(window, event->state, 0, event->button);
    215 }
    216 
    217 void Binder::handleShortcut(Window window, unsigned long modMask,
    218                             KeyCode keyCode, unsigned int button)
    219 {
    220     LOGDEBUG("handle shortcut event");
    221     LAction grabbedActions;
    222     // only gets in here for the first part of a shortcut, thus
    223     // create a list of all shortcuts which are listened for
    224     for (MBindings::iterator it = actionBindings_->begin();
    225             it != actionBindings_->end(); it++)
    226     {
    227         Action *action = (*it).second;
    228         Shortcut *shortcut = action->listenOn();
    229 
    230         if (shortcut &&
    231             (modMask == shortcut->modMask()) &&
    232             (keyCode == shortcut->keyCode()) &&
    233             (button == shortcut->button()))
    234         {
    235             if (shortcut->next()) {
    236                 action->setListenOn(shortcut->next());
    237                 grabbedActions.push_back(action);
    238             }
    239             else {
    240                 grabbedActions.clear();
    241                 LOGDEBUG("handle shortcut performs action '"
    242                          + action->id() + "'");
    243                 if (action->promptsCount() > 0) {
    244                     KERNEL->focusedMonitor()->inputBar()->runArgument(action);
    245                     // action will be performed by argument processing
    246                 }
    247                 else {
    248                     action->perform();
    249                 }
    250                 break;
    251             }
    252         }
    253     }
    254     if (grabbedActions.size()) {
    255         Shortcut shortcut(modMask, keyCode, 0, button);
    256         // now the magic occurs
    257         handleShortcutChains(window, &shortcut, &grabbedActions);
    258     }
    259     // reset listen on info
    260     for (MBindings::iterator it = actionBindings_->begin();
    261             it != actionBindings_->end(); it++)
    262     {
    263         Action *action = (*it).second;
    264         if (action->shortcut()) {
    265             // already grabbed
    266             action->setListenOn(action->shortcut());
    267         }
    268     }
    269 }
    270 
    271 void Binder::handleShortcutChains(Window window, Shortcut *prefix,
    272                                   LAction *grabbedActions)
    273 {
    274     string sequence = 
    275         " " + Util::stringForModMask(prefix->modMask())
    276             + XCORE->keyCodeToString(prefix->keyCode()) + " ::";
    277     Box *box = KERNEL->focusedMonitor()->box();
    278     box->setText(sequence);
    279     box->show();
    280     box->illuminate();
    281 
    282     XCORE->grabKeyboard(window);
    283 
    284     bool isDoubleShortcut = false;
    285     unsigned long modMask;
    286     KeyCode keyCode;
    287     Action *action = 0;
    288     while (grabbedActions->size()) {
    289         // fetch new key stroke
    290         nextKeystroke(&modMask, &keyCode);
    291 
    292         if ((prefix->modMask() == modMask) &&
    293             (prefix->keyCode() == keyCode))
    294         {
    295             isDoubleShortcut = true;
    296             break;
    297         }
    298         prefix->setModMask(modMask);
    299         prefix->setKeyCode(keyCode);
    300         sequence += " " + Util::stringForModMask(modMask)
    301                         + XCORE->keyCodeToString(keyCode) + " ::";
    302         box->setText(sequence);
    303         box->illuminate();
    304         for (LAction::iterator it = grabbedActions->begin();
    305                 it != grabbedActions->end(); )
    306         {
    307             action = *it;
    308             Shortcut *shortcut = action->listenOn();
    309             if (shortcut &&
    310                 (modMask == shortcut->modMask()) &&
    311                 (keyCode == shortcut->keyCode()))
    312             {
    313                 // iterates next
    314                 it++;
    315                 if (shortcut->next()) {
    316                     action->setListenOn(shortcut->next());
    317                 }
    318                 else {
    319                     grabbedActions->clear(); // escape condition
    320                     break;
    321                 }
    322             }
    323             else {
    324                 // iterates next
    325                 it = grabbedActions->erase(it);
    326             }
    327             action = 0;
    328         }
    329     }
    330     XCORE->ungrabKeyboard();
    331     box->hide();
    332     if (action) {
    333 
    334         LOGDEBUG("handle shortcut performs action '"
    335                 + action->id() + "'");
    336         if (action->promptsCount() > 0) {
    337             KERNEL->focusedMonitor()->inputBar()->runArgument(action);
    338             // action will be performed by argument processing
    339         }
    340         else {
    341             action->perform();
    342         }
    343     }
    344     else if (isDoubleShortcut) {
    345         emulateKeyPress(modMask, keyCode);
    346     }
    347 }
    348 
    349 void Binder::emulateKeyPress(unsigned long modMask, KeyCode keyCode) {
    350 
    351     Monitor *monitor = KERNEL->focusedMonitor();
    352     Client *client = monitor->focused()->topClient();
    353 
    354     if (client) {
    355         XEvent event;
    356         event.xkey.type = KeyPress;
    357         event.xkey.time = CurrentTime;
    358         event.xkey.window = client->clientWindow();
    359         event.xkey.display = KERNEL->display();
    360         event.xkey.state = modMask;
    361         event.xkey.keycode = keyCode;
    362         XCORE->sendEvent(client->clientWindow(), KeyPressMask, &event);
    363         event.xkey.type = KeyRelease;
    364         XCORE->sendEvent(client->clientWindow(), KeyReleaseMask, &event);
    365         XCORE->sync();
    366     }
    367 }
    368 
    369 void Binder::nextKeystroke(unsigned long *modMask, KeyCode *keyCode) {
    370     XEvent event;
    371     KeySym sym;
    372     *modMask = 0;
    373     do {
    374         XCORE->maskEvent(KeyPressMask, &event);
    375         *modMask |= event.xkey.state & validModMask_;
    376         *keyCode = event.xkey.keycode;
    377         sym = XCORE->keyCodeToKeySym(event.xkey.keycode);
    378     } while (IsModifierKey(sym));
    379 }
    380 
    381 void Binder::handleKey(Window window, XKeyEvent *event)
    382 {
    383     LOGDEBUG("handle key press event");
    384     handleShortcut(window, event->state, event->keycode, 0);
    385 }
    386 
    387 void Binder::grabButtons(Window window, unsigned long modMask)
    388 {
    389     XCORE->grabButton(window, modMask, AnyModifier);
    390 }
    391 
    392 void Binder::ungrabButtons(Window window) {
    393     XCORE->ungrabButton(window, AnyModifier, AnyButton);
    394 }
    395 
    396 #ifdef POSIX_REGEX
    397 bool Binder::regex_first_match(const string& pattern, const string& haystack, int& begin, int& end){
    398     regmatch_t temp_regmatch;
    399 
    400     begin = 0;
    401     end = -1 ;
    402     bool condition = false;
    403     if (pattern == temp_pattern_ && pattern!="") { condition = true; } else
    404     {
    405         condition = (regcomp(&temp_regex_, pattern.c_str(), REG_EXTENDED)==0);
    406         if (!condition) regex_prepare(); //reinitialize
    407     }
    408     if (condition){
    409         bool result = (regexec(&temp_regex_, haystack.c_str(), 1, &temp_regmatch, 0)==0);
    410         if (result){
    411             begin = temp_regmatch.rm_so;
    412             end = temp_regmatch.rm_eo;
    413         }
    414         temp_pattern_ = pattern;
    415         return result;
    416     }
    417     return false;
    418 }
    419 
    420 bool Binder::regex_match(const string& pattern, const string& haystack){
    421     int begin;
    422     int end;
    423     return(regex_first_match(pattern, haystack, begin, end));
    424 }
    425 #endif
    426 
    427 
    428 string Binder::absolutePattern(string pattern, Sstring *names,
    429                                    unsigned int offset)
    430 {
    431 
    432     string result = pattern;
    433 if (!(names->empty())) {
    434 
    435 #ifdef POSIX_REGEX
    436         if (doRegex_) {
    437 
    438             /* Check whether the search-pattern contains a modifier.
    439                If it does, simply return the pattern itself, otherwise,
    440                perform an infix-matching-algorithm. */
    441 
    442             string first = *names->begin();
    443             for (int direction = 0; direction <= 1; direction++){
    444                 bool matches_all = true;
    445                 do {
    446                     LOGDEBUG("Entering do-while-infix-matching-loop");
    447                     int begin;
    448                     int end;
    449 
    450                     if (regex_first_match (result, first, begin, end)){
    451                         LOGDEBUG("pattern-rule matched first member");
    452                         int count = end - begin + 1;
    453                         if (direction == 1) begin--;
    454                         if ((begin + count  <= (int)first.length()) && (begin >= 0) && (count>0)){
    455                             pattern=first.substr(begin, count);
    456                             LOGDEBUG("trying to add a char to pattern");
    457                             LOGDEBUG(pattern);
    458                         } else break;
    459                     }
    460 
    461                     for (Sstring::iterator it = names->begin(); it != names->end(); it++) 
    462                         if(!regex_match(pattern, (*it))) matches_all = false;
    463                     if (matches_all) result = pattern;
    464                 } while (matches_all);
    465             }
    466             return(result);
    467         }
    468 #endif
    469 
    470         string firstName = *names->begin();
    471         string lastName = *names->rbegin();
    472 
    473         unsigned int firstLength = firstName.length();
    474         unsigned int lastLength = lastName.length();
    475         for (unsigned int i = offset;
    476              (i < firstLength) && (i < lastLength); i++)
    477         {
    478             if (firstName[i] == lastName[i]) {
    479                 result += firstName[i];
    480             }
    481             else {
    482                 break;
    483             }
    484         }
    485     }
    486 
    487     return result; // return absolute pattern
    488 }
    489 
    490 void Binder::matchPattern(string digest, string pattern, Sstring *strings,
    491                           unsigned int patternLength)
    492 {
    493     bool condition;
    494     condition = (digest.substr(0, patternLength) == pattern);
    495 
    496 #ifdef POSIX_REGEX
    497     if (doRegex_) {
    498         condition = regex_match(pattern, digest);
    499     }
    500 #endif
    501 
    502     if (condition) {
    503         strings->insert(digest);
    504     }
    505 }
    506 
    507 void Binder::initRegex(string pattern){
    508 #ifdef POSIX_REGEX
    509     doRegex_ = Util::get(KERNEL->commonSettings(), "autocompletion.mode") == "regex";
    510 #endif
    511 }
    512 
    513 string Binder::queryFramesForPattern(string pattern, Sstring *sFrames)
    514 {
    515 
    516     Workspace *workspace = KERNEL->focusedMonitor()->focused();
    517     CFrame *frames = workspace->frames();
    518     unsigned int patternLength = pattern.length();
    519     string lName;
    520     initRegex(pattern);
    521     for (LFrame::iterator it = frames->begin(); it != frames->end(); it++) {
    522         Frame *frame = *it;
    523         matchPattern(frame->name(), pattern,
    524                      sFrames, patternLength);
    525     }
    526     return absolutePattern(pattern, sFrames, patternLength);
    527 }
    528 
    529 string Binder::queryClientIdsForPattern(string pattern, Sstring *clients)
    530 {
    531 
    532     Monitor *focusedMonitor = KERNEL->focusedMonitor();
    533     MClient *clientMap = focusedMonitor->clients();
    534     unsigned int patternLength = pattern.length();
    535     initRegex(pattern);
    536     for (MClient::iterator it = clientMap->begin();
    537          it != clientMap->end(); it++)
    538     {
    539         Client *client = (*it).second;
    540         if (client->attached()
    541 #ifdef SLOT_SUPPORT
    542                 || client->mode() == Client::SLOT
    543 #endif // SLOT_SUPPORT 
    544            )
    545         {
    546             matchPattern(client->id(), pattern, clients, patternLength);
    547         }
    548     }
    549     return absolutePattern(pattern, clients, pattern.length());
    550 }
    551 
    552 string Binder::queryClientsForPattern(string pattern, Sstring *clients)
    553 {
    554 
    555     Monitor *focusedMonitor = KERNEL->focusedMonitor();
    556     MClient *clientMap = focusedMonitor->clients();
    557     unsigned int patternLength = pattern.length();
    558     initRegex(pattern);
    559     for (MClient::iterator it = clientMap->begin();
    560          it != clientMap->end(); it++)
    561     {
    562         Client *client = (*it).second;
    563         if (client->attached()
    564 #ifdef SLOT_SUPPORT 
    565                 || client->mode() == Client::SLOT
    566 #endif
    567            )
    568         {
    569             matchPattern(client->name(), pattern, clients, patternLength);
    570         }
    571     }
    572     return absolutePattern(pattern, clients, pattern.length());
    573 }
    574 
    575 string Binder::queryDetachedClientsForPattern(string pattern, Sstring *clients)
    576 {
    577 
    578     Monitor *focusedMonitor = KERNEL->focusedMonitor();
    579     CClient *detachedClients = focusedMonitor->detachedClients();
    580     unsigned int patternLength = pattern.length();
    581     initRegex(pattern);
    582     for (LClient::iterator it = detachedClients->begin();
    583          it != detachedClients->end(); it++)
    584     {
    585         Client *client = *it;
    586         matchPattern(client->name(), pattern, clients, patternLength);
    587     }
    588     return absolutePattern(pattern, clients, pattern.length());
    589 }
    590 
    591 string Binder::queryMonitorsForPattern(string pattern, Sstring *monitors) {
    592 
    593     CMonitor *ms = KERNEL->monitors();
    594     unsigned int patternLength = pattern.length();
    595     initRegex(pattern);
    596     for (LMonitor::iterator it = ms->begin(); it != ms->end(); it++) {
    597         matchPattern((*it)->displayString(), pattern, monitors, patternLength);
    598     }
    599 
    600     return absolutePattern(pattern, monitors, patternLength);
    601 }
    602 
    603 string Binder::queryWorkspacesForPattern(string pattern,
    604         Sstring *workspaces)
    605 {
    606 
    607     Monitor *focusedMonitor = KERNEL->focusedMonitor();
    608     unsigned int patternLength = pattern.length();
    609     string wsName;
    610     initRegex(pattern);
    611     for (LWorkspace::iterator it = focusedMonitor->begin();
    612          it != focusedMonitor->end(); it++)
    613     {
    614         matchPattern((*it)->name(), pattern, workspaces, patternLength);
    615     }
    616 
    617     return absolutePattern(pattern, workspaces, patternLength);
    618 }
    619 
    620 string Binder::queryExternChainActionsForPattern(
    621         string pattern, Sstring *actionKeys)
    622 {
    623     unsigned int patternLength = pattern.length();
    624     string actionKey;
    625     Action *action;
    626     initRegex(pattern);
    627     for (MBindings::iterator it = actionBindings_->begin();
    628          it != actionBindings_->end(); it++)
    629     {
    630         actionKey = (*it).first;
    631         action = (*it).second;
    632         if (action->type() == Action::INTERN) {
    633             continue;
    634         }
    635         matchPattern(actionKey, pattern, actionKeys, patternLength);
    636     }
    637 
    638     return absolutePattern(pattern, actionKeys, patternLength);
    639 }
    640 
    641 string Binder::queryActionKeysWithoutValidationForPattern(
    642         string pattern, Sstring *actionKeys)
    643 {
    644     unsigned int patternLength = pattern.length();
    645     string actionKey;
    646     initRegex(pattern);
    647     for (MBindings::iterator it = actionBindings_->begin();
    648          it != actionBindings_->end(); it++)
    649     {
    650         actionKey = (*it).first;
    651         matchPattern(actionKey, pattern, actionKeys, patternLength);
    652     }
    653 
    654     return absolutePattern(pattern, actionKeys, patternLength);
    655 }
    656 
    657 string Binder::queryActionKeysForPattern(string pattern, Sstring *actionKeys) {
    658 
    659     unsigned int patternLength = pattern.length();
    660     string actionKey;
    661     initRegex(pattern);
    662     for (MBindings::iterator it = actionBindings_->begin();
    663          it != actionBindings_->end(); it++)
    664     {
    665         Action *action = (*it).second;
    666 
    667         if (action->isValid()) {
    668             actionKey = (*it).first;
    669             matchPattern(actionKey, pattern, actionKeys, patternLength);
    670         }
    671     }
    672 
    673     return absolutePattern(pattern, actionKeys, patternLength);
    674 }
    675 
    676 string Binder::queryCommandForPattern(string pattern, Sstring *expands) {
    677 
    678     Expander::instance()->queryString(pattern);
    679     Sstring exp = Expander::instance()->expands();
    680     initRegex(pattern);
    681 
    682 
    683     unsigned int offset  = pattern.find_last_of('/');
    684     if (offset == string::npos) offset=0; else offset++; 
    685 
    686 #ifdef POSIX_REGEX
    687     doRegex_ = doRegex_ && (offset == 0);
    688 #endif
    689 
    690     for (Sstring::iterator it = exp.begin();
    691         it != exp.end(); it++)
    692     {
    693     matchPattern((string)(*it), pattern.substr(offset), expands, pattern.length()-offset);
    694     }
    695 
    696     return absolutePattern(pattern, expands, pattern.length() -
    697             pattern.find_last_of('/') - 1);
    698 }
    699 
    700 #define NUM_MASKS 8
    701 
    702 void Binder::initLockModifiers() {
    703     XModifierKeymap *modmap;
    704     KeyCode numLock, scrollLock;
    705 
    706     // preinit
    707     numLockMask_ = scrollLockMask_ = 0;
    708 
    709     // init modifier map
    710     modmap = XCORE->modifierMapping();
    711 
    712     // modifier masks we know
    713     static int masks[NUM_MASKS] = {
    714         ShiftMask, LockMask, ControlMask, Mod1Mask,
    715         Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
    716     };
    717 
    718     // temporary keysyms of evil keys
    719     numLock = XCORE->stringToKeyCode("Num_Lock");
    720     scrollLock = XCORE->stringToKeyCode("Scroll_Lock");
    721 
    722     if (modmap && modmap->max_keypermod > 0) {
    723         unsigned int max = NUM_MASKS * modmap->max_keypermod;
    724         for (unsigned int i = 0; i < max; i++) {
    725             if (numLock && (modmap->modifiermap[i] == numLock)) {
    726                 numLockMask_ = masks[i / modmap->max_keypermod];
    727             }
    728             else if (scrollLock && (modmap->modifiermap[i] == scrollLock)) {
    729                 scrollLockMask_ = masks[i / modmap->max_keypermod];
    730             }
    731         }
    732     }
    733 
    734     XCORE->freeModifierMapping(modmap);
    735     validModMask_ = 255 & ~(numLockMask_ | scrollLockMask_ | LockMask);
    736 }
    737 
    738 unsigned int Binder::validModMask() const {
    739     return validModMask_;
    740 }