wmi

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

kernel.cpp (33564B)


      1 // Copyright (c) 2003 - 2009 Anselm R Garbe <anselm@garbe.us>
      2 // See LICENSE for license details.
      3 
      4 extern "C" {
      5 #include <assert.h>
      6 #include <stdlib.h> // getenv stuff
      7 #include <string.h>
      8 #include <unistd.h>
      9 #include <X11/Xlib.h>
     10 #include <X11/Xatom.h>
     11 #include <X11/Xutil.h>
     12 }
     13 
     14 #include <sstream>
     15 
     16 #include "kernel.h"
     17 
     18 #include "action.h"
     19 #include "actions.h"
     20 #include "atoms.h"
     21 #include "binder.h"
     22 #include "box.h"
     23 #include "client.h"
     24 #include "clientbar.h"
     25 #include "cursors.h"
     26 #include "container.h"
     27 #include "draw.h"
     28 #include "expander.h"
     29 #include "float.h"
     30 #include "frame.h"
     31 #include "inputbar.h"
     32 #include "launcher.h"
     33 #include "loader.h"
     34 #include "logger.h"
     35 #include "menu.h"
     36 #include "monitor.h"
     37 #include "prompt.h"
     38 #include "shortcut.h"
     39 #include "slot.h"
     40 #include "split.h"
     41 #include "statusbar.h"
     42 #include "thing.h"
     43 #include "util.h"
     44 #include "validators.h"
     45 #include "workspace.h"
     46 #include "xcore.h"
     47 
     48 Kernel::Kernel() {
     49 }
     50 
     51 void Kernel::initMonitors() {
     52 
     53     unsigned int monitorCount = ScreenCount(display_);
     54 
     55     for (unsigned int i = 0; i < monitorCount; i++) {
     56         Monitor *monitor = new Monitor(display_, i);
     57         monitors_->attach(monitor);
     58     }
     59 }
     60 
     61 int Kernel::start(char *argv[], Display *display, MSettings *themeSettings,
     62                    MSettings *commonSettings, MSettings *actionSettings,
     63                    MSettings *sessionSettings, bool isFirstRun)
     64 {
     65 
     66     runlevel_ = START;
     67 
     68     // init, order is very important
     69     display_ = display;
     70     monitors_ = new CMonitor(this);
     71     themeSettings_ = themeSettings;
     72     commonSettings_ = commonSettings;
     73     actionSettings_ = actionSettings;
     74     sessionSettings_ = sessionSettings;
     75     actionBindings_ = new MBindings();
     76     isRecording_ = false;
     77     isMenuMode_ = false;
     78     isSloppyMode_ = true;
     79     isStackedTabbing_ = Util::get(commonSettings_, "cycle.mode") != "default";
     80     borderWidth_ = Util::strToUInt(Util::get(commonSettings_, "border.width"));
     81 
     82     LOGDEBUG("--- begin of new WMI session ---");
     83     XSetErrorHandler(XCore::handleErrors);
     84     LOGDEBUG("error handler installed");
     85     XCORE->setDisplay(display_);
     86     LOGDEBUG("xcore initialized");
     87     Atoms::initAtoms();
     88     LOGDEBUG("atoms initialized");
     89     Cursors::initCursors();
     90     LOGDEBUG("cursors initialized");
     91     Actions::instance()->initInternActions(actionBindings_);
     92     LOGDEBUG("intern actions initialized");
     93     initActionBindings();
     94     LOGDEBUG("action bindings initialized");
     95     initMonitors();
     96     LOGDEBUG("monitors initialized");
     97     initKeys();
     98     LOGDEBUG("keys initialized");
     99     Launcher::instance()->exec(
    100             Util::get(themeSettings_, "exec"));
    101     if (isFirstRun) {
    102         Actions::instance()->executeTerm(0, "man wmi");
    103     }
    104     // Sync all X buffers and wait until all events has been
    105     // processed
    106     XCORE->sync();
    107     initWindows();
    108     LOGDEBUG("windows initialized");
    109 
    110     defaultPrompt_ = new Prompt(": ", &Binder::queryActionKeysForPattern);
    111 
    112     Action* startupAction = new Action("startup", &Actions::sequence,
    113                                        &Validators::isAlwaysPossible,
    114                                        Action::SEQUENCE, 
    115                                        strdup(Util::get(commonSettings_,
    116                                                  "startup.chain").c_str()));
    117     startupAction->perform();
    118     delete startupAction;
    119     Expander::instance(); // rehash expander
    120 
    121     // Launch main event loop
    122     runlevel_ = RUN;
    123     int result = run();
    124 
    125     serialize();
    126     LOGDEBUG("current state serialized");
    127     LOGDEBUG("--- end of WMI session ---");
    128 
    129     cleanup();
    130 
    131     if (runlevel_ == RESTART) {
    132         saveSettings();
    133         execvp(argv[0], argv);
    134         LOGERROR("restart failed", false);
    135         exit(1);
    136     }
    137 
    138     return result;
    139 }
    140 
    141 Kernel::~Kernel() {
    142 }
    143 
    144 Prompt *Kernel::defaultPrompt() const {
    145     return defaultPrompt_;
    146 }
    147 
    148 Window Kernel::defaultRootWindow() {
    149     return DefaultRootWindow(display_);
    150 }
    151 
    152 Window Kernel::rootWindow() {
    153     return focusedMonitor()->rootWindow();
    154 }
    155 
    156 Client *Kernel::focusedClient() {
    157 
    158     assert(focusedMonitor() != 0); // always, while running
    159     Workspace *focusedWorkspace = focusedMonitor()->focused();
    160     assert(focusedWorkspace != 0);
    161 
    162     return focusedWorkspace->topClient();
    163 }
    164 
    165 void Kernel::killClient(Client *client) {
    166     if (client) {
    167         XCORE->kill(client->clientWindow(), client->protocols());
    168     }
    169 }
    170 
    171 void Kernel::updateBars() {
    172 
    173     for (LMonitor::iterator it = monitors_->begin();
    174          it != monitors_->end(); it++)
    175     {
    176         Monitor *monitor = (Monitor *)*it;
    177         monitor->statusBar()->illuminate();
    178         monitor->clientBar()->illuminate();
    179     }
    180 }
    181 
    182 void Kernel::initWindows() {
    183 
    184     for (LMonitor::iterator it = monitors_->begin();
    185          it != monitors_->end(); it++)
    186     {
    187         Monitor *monitor = (Monitor *)*it;
    188         monitor->scanWindows();
    189     }
    190 }
    191 
    192 void Kernel::initKeys() {
    193 
    194     XCORE->ungrabKeyboard();
    195     Binder *bm = Binder::instance();
    196 
    197     for (LMonitor::iterator it = monitors_->begin();
    198          it != monitors_->end(); it++)
    199     {
    200         Monitor *monitor = (Monitor *)*it;
    201         bm->initKeys(monitor->rootWindow());
    202     }
    203 }
    204 
    205 void Kernel::initActionBindings() {
    206 
    207     Action::Type type = Action::UNKNOWN;
    208 
    209     string settingsKey, bind, postfix, spec, keys;
    210     spec = "";
    211     Action *action = 0;
    212 
    213     // extract all extern actions and chain sequences
    214     for (MSettings::iterator it = actionSettings_->begin();
    215          it != actionSettings_->end(); it++)
    216     {
    217         settingsKey = (*it).first;
    218         if (settingsKey.substr(0, 6) == "extern") {
    219             type = Action::EXTERN;
    220         }
    221         else if (settingsKey.substr(0, 5) == "chain") {
    222             type = Action::SEQUENCE;
    223         }
    224         else {
    225             type = Action::UNKNOWN;
    226         }
    227 
    228         if (type != Action::UNKNOWN) { // settingsKey has a valid action prefix
    229             bind = Util::nthToken(settingsKey, '.', 2);
    230             LOGDEBUG("found " + bind);
    231             postfix = Util::nthToken(settingsKey, '.', 3);
    232 
    233             if ((type == Action::EXTERN) && (postfix == "cmd")) {
    234                 spec = (*it).second;
    235             }
    236             else if ((type == Action::SEQUENCE) && (postfix == "seq")) {
    237                 spec = (*it).second;
    238             }
    239 
    240             if (spec != "") {
    241 
    242                 if (type == Action::EXTERN) {
    243                     action = new Action(bind, &Actions::execute,
    244                                         &Validators::isWorkspaceFocused, type,
    245                                         (char *)spec.c_str());
    246                 }
    247                 else if (type == Action::SEQUENCE) {
    248 
    249                     // We determine the first sequential action and use
    250                     // its possibility method (or if it's user defined we
    251                     // allow sequential performing under all
    252                     // circumstances).
    253                     string firstActionKey = Util::nthToken(spec, ',', 1);
    254                     IsValid isValid = 0;
    255                     Action *firstAction =
    256                         Util::get(actionBindings_, firstActionKey);
    257                     if (firstAction) {
    258                         isValid = firstAction->getIsValid();
    259                     }
    260                     else {
    261                         isValid = &Validators::isAlwaysPossible;
    262                     }
    263                     action = new Action(bind, &Actions::sequence,
    264                                         isValid, type, (char *)spec.c_str());
    265                 }
    266             }
    267 
    268             if (action != 0) {
    269 
    270                 LOGDEBUG("creating action: " + bind);
    271                 (*actionBindings_)[bind] = action;
    272                 action = 0;
    273                 spec = "";
    274             }
    275         }
    276     }
    277 
    278     // extract all key bindings, if they're defined
    279     for (MSettings::iterator it = actionSettings_->begin();
    280          it != actionSettings_->end(); it++)
    281     {
    282         settingsKey = (*it).first;
    283         if (settingsKey.substr(0, 6) == "extern") {
    284             type = Action::EXTERN;
    285         }
    286         else if (settingsKey.substr(0, 6) == "intern") {
    287             type = Action::INTERN;
    288         }
    289         else if (settingsKey.substr(0, 5) == "chain") {
    290             type = Action::SEQUENCE;
    291         }
    292         else {
    293             type = Action::UNKNOWN;
    294         }
    295 
    296         if (type != Action::UNKNOWN) {
    297 
    298             bind = Util::nthToken(settingsKey, '.', 2);
    299             LOGDEBUG("found " + bind);
    300             postfix = Util::nthToken(settingsKey, '.', 3);
    301 
    302             // settingsKey has a valid action prefix
    303             if (postfix == "keys") {
    304                 keys = (*it).second;
    305 
    306                 if (keys != "") {
    307 
    308                     // fetch action
    309                     action = Util::get(actionBindings_, bind);
    310                     if (action) {
    311                         action->setShortcut(Shortcut::shortcut(keys));
    312                     }
    313 
    314                     keys = "";
    315                 }
    316             }
    317         }
    318     }
    319 }
    320 
    321 bool Kernel::isWMIWindow(Window window) {
    322 
    323     return isRootWindow(window) ||
    324 #ifdef SLOT_SUPPORT
    325            slotWindow(window) ||
    326 #endif // SLOT_SUPPORT
    327            menuWindow(window) ||
    328            barWindow(window) ||
    329            boxWindow(window) ||
    330            thingWindow(window);
    331 }
    332 
    333 bool Kernel::isRootWindow(Window window) {
    334 
    335     for (LMonitor::iterator it = monitors_->begin();
    336          it != monitors_->end(); it++)
    337     {
    338         Monitor *monitor = (Monitor *)*it;
    339         if (monitor->rootWindow() == window) {
    340             monitors_->focus(monitor);
    341             return true;
    342         }
    343     }
    344 
    345     return false;
    346 }
    347 
    348 Menu *Kernel::menuWindow(Window window) {
    349 
    350     for (LMonitor::iterator it = monitors_->begin();
    351          it != monitors_->end(); it++)
    352     {
    353         Monitor *monitor = (Monitor *)*it;
    354         Menu *menu = monitor->menuWindow(window); 
    355         if (menu) {
    356             monitors_->focus(monitor);
    357             return menu;
    358         }
    359     }
    360 
    361     return 0;
    362 }
    363 
    364 #ifdef SLOT_SUPPORT
    365 Slot *Kernel::slotWindow(Window window) {
    366 
    367     for (LMonitor::iterator it = monitors_->begin();
    368          it != monitors_->end(); it++)
    369     {
    370         Monitor *monitor = (Monitor *)*it;
    371         Slot *slot = monitor->slotWindow(window);
    372         if (slot) {
    373             monitors_->focus(monitor);
    374             return slot;
    375         }
    376     }
    377 
    378     return 0;
    379 }
    380 #endif // SLOT_SUPPORT
    381 
    382 Thing *Kernel::thingWindow(Window window) {
    383 
    384     for (LMonitor::iterator it = monitors_->begin();
    385          it != monitors_->end(); it++)
    386     {
    387         Monitor *monitor = (Monitor *)*it;
    388         Thing *thing = monitor->thingWindow(window);
    389         if (thing) {
    390             monitors_->focus(monitor);
    391             return thing;
    392         }
    393     }
    394 
    395     return 0;
    396 }
    397 
    398 Box *Kernel::boxWindow(Window window) {
    399 
    400     for (LMonitor::iterator it = monitors_->begin();
    401          it != monitors_->end(); it++)
    402     {
    403         Monitor *monitor = (Monitor *)*it;
    404         Box *box = monitor->box();
    405         if (box->window() == window) {
    406             return box;
    407         }
    408     }
    409     return 0;
    410 }
    411 
    412 Bar *Kernel::barWindow(Window window) {
    413 
    414     for (LMonitor::iterator it = monitors_->begin();
    415          it != monitors_->end(); it++)
    416     {
    417         Monitor *monitor = (Monitor *)*it;
    418         Bar *bar = monitor->barWindow(window);
    419         if (bar) {
    420             monitors_->focus(monitor);
    421             return bar;
    422         }
    423     }
    424 
    425     return 0;
    426 }
    427 
    428 Client *Kernel::clientForWindow(Window window) {
    429 
    430     for (LMonitor::iterator it = monitors_->begin();
    431          it != monitors_->end(); it++)
    432     {
    433         Monitor *monitor = (Monitor *)*it;
    434         Client *client = monitor->clientForWindow(window);
    435         if (client != 0) {
    436             monitors_->focus(monitor);
    437             return client;
    438         }
    439     }
    440     return 0;
    441 }
    442 
    443 bool Kernel::isInputMode() {
    444     return focusedMonitor()->inputBar()->isVisible();
    445 }
    446 
    447 void Kernel::ungrabShortcutOnAllMonitors(Shortcut *shortcut) {
    448 
    449     Binder *bm = Binder::instance();
    450 
    451     for (LMonitor::iterator it = monitors_->begin();
    452          it != monitors_->end(); it++)
    453     {
    454         Monitor *monitor = (Monitor *)*it;
    455         bm->ungrabShortcut(shortcut, monitor->rootWindow());
    456     }
    457 }
    458 
    459 void Kernel::grabShortcutOnAllMonitors(Shortcut *shortcut) {
    460 
    461     Binder *bm = Binder::instance();
    462 
    463     for (LMonitor::iterator it = monitors_->begin();
    464          it != monitors_->end(); it++)
    465     {
    466         Monitor *monitor = (Monitor *)*it;
    467         bm->grabShortcut(shortcut, monitor->rootWindow());
    468     }
    469 }
    470 
    471 void Kernel::toggleShortcuts() {
    472 
    473     static bool isGrabbed = false;
    474 
    475     isGrabbed = !isGrabbed;
    476     for (MBindings::iterator it = actionBindings_->begin();
    477             it != actionBindings_->end(); it++)
    478     {
    479         Action *action = (*it).second;
    480         Shortcut *shortcut = action->listenOn();
    481         if (shortcut
    482             && (action->getToPerform() != &Actions::inputMode)
    483             && (action->getToPerform() != &Actions::toggleShortcuts))
    484         {
    485             if (isGrabbed) {
    486                 ungrabShortcutOnAllMonitors(shortcut);
    487             }
    488             else {
    489                 grabShortcutOnAllMonitors(shortcut);
    490             }
    491         }
    492     }
    493 }
    494 
    495 void Kernel::bindShortcut(string argument) {
    496 
    497     unsigned int argDelim = argument.find_first_of('+');
    498     if (argDelim == string::npos) {
    499         return;
    500     }
    501     string bind = argument.substr(0, argDelim);
    502     string keys = argument.substr(argDelim + 1);
    503 
    504     Action *action = Util::get(actionBindings_, bind);
    505 
    506     if (action) {
    507         Shortcut *oldShortcut = action->shortcut();
    508         if (oldShortcut) {
    509             bool doUngrab = true;
    510             for (MBindings::iterator it = actionBindings_->begin();
    511                     it != actionBindings_->end(); it++)
    512             {
    513                 Action *action = (*it).second;
    514                 Shortcut *s = action->listenOn();
    515                 if (s &&
    516                     (s->modMask() == oldShortcut->modMask()) &&
    517                     (s->keyCode() == oldShortcut->keyCode()) &&
    518                     (s->button() == oldShortcut->button()))
    519                 {
    520                     doUngrab = false;
    521                     break;
    522                 }
    523             }
    524             if (doUngrab) {
    525                 ungrabShortcutOnAllMonitors(oldShortcut);
    526             }
    527             delete oldShortcut;
    528         }
    529 
    530         string prefix;
    531         switch (action->type()) {
    532         case Action::INTERN:
    533             prefix = "intern.";
    534             break;
    535         case Action::EXTERN:
    536             prefix = "extern.";
    537             break;
    538         case Action::SEQUENCE:
    539             prefix = "chain.";
    540             break;
    541         case Action::UNKNOWN:
    542             prefix = "unknown.";
    543             break;
    544         }
    545 
    546         if (keys == "") {
    547             Util::remove(actionSettings_, prefix + bind + ".keys");
    548             return;
    549         }
    550         action->setShortcut(Shortcut::shortcut(keys));
    551 
    552         (*actionSettings_)[prefix + bind + ".keys"] = keys;
    553         if (action->listenOn()) {
    554             grabShortcutOnAllMonitors(action->listenOn());
    555         }
    556     }
    557 }
    558 
    559 void Kernel::serialize() {
    560 
    561     for (LMonitor::iterator it = monitors_->begin();
    562          it != monitors_->end(); it++)
    563     {
    564         Monitor *monitor = *it;
    565         monitor->serialize();
    566     }
    567 }
    568 
    569 void Kernel::installCursor(Cursor cursor, Window window) {
    570     XSetWindowAttributes attr;
    571     attr.cursor = cursor;
    572     XCORE->setWindowAttributes(window, CWCursor, &attr);
    573 }
    574 
    575 
    576 void Kernel::addClient(Client *client) {
    577 
    578     focusedMonitor()->addClient(client);
    579 }
    580 
    581 void Kernel::saveSettings() {
    582 
    583     // actions and session will be safed under any circumstances saved
    584     // comments will be lost in these files
    585     const char *home = getenv("HOME");
    586 
    587     Loader::saveSettings(actionSettings_, (string)home +
    588             "/.wmi/actions.session");
    589     Loader::saveSettings(sessionSettings_, (string)home +
    590             "/.wmi/wmi.session");
    591 }
    592 
    593 void Kernel::cleanup() {
    594 
    595     for (LMonitor::iterator it = monitors_->begin();
    596             it != monitors_->end(); it++)
    597     {
    598         Monitor *monitor = *it;
    599         monitor->cleanup();
    600     }
    601     XCORE->setInputFocus(PointerRoot);
    602     Cursors::cleanup(); 
    603     delete monitors_;
    604     // TODO: theme, etc.
    605     XCloseDisplay(display_);
    606 }
    607 
    608 //////// EVENT HANDLING //////////////
    609 
    610 void Kernel::handleButtonRelease(XButtonEvent *event) {
    611 
    612     event->state &= Binder::instance()->validModMask();
    613 
    614     Bar *bar = barWindow(event->window);
    615     if (bar) {
    616         bar->handleButtonRelease(event);
    617         return;
    618     }
    619 
    620     Thing *thing = thingWindow(event->window);
    621     if (thing) {
    622         thing->handleButtonRelease(event);
    623     }
    624 }
    625 
    626 void Kernel::handleButtonPress(XButtonEvent *event) {
    627 
    628     event->state &= Binder::instance()->validModMask();
    629 
    630     if (event->state != 0) {
    631         // no AnyModifier, so this seems to be shortcut
    632         Binder::instance()->handleButton(event->root, event);
    633         return;
    634     }
    635 
    636     Menu *menu = menuWindow(event->window);
    637     if (menu) {
    638         menu->handleButtonPress(event);
    639         return;
    640     }
    641 
    642     Bar *bar = barWindow(event->window);
    643     if (bar) {
    644         bar->handleButtonPress(event);
    645         return;
    646     }
    647 
    648 #ifdef SLOT_SUPPORT
    649     Slot *slot = slotWindow(event->window);
    650     if (slot) {
    651         slot->handleButtonPress(event);
    652         return;
    653     }
    654 #endif
    655 
    656     Thing *thing = thingWindow(event->window);
    657 
    658     if (thing) {
    659         thing->handleButtonPress(event);
    660     }
    661 
    662     Client *client = clientForWindow(event->window);
    663     if (client) {
    664         client->handleButtonPress(event);
    665     }
    666 }
    667 
    668 void Kernel::handleConfigureRequest(XConfigureRequestEvent *event) {
    669 
    670     Client *client = clientForWindow(event->window);
    671 
    672     if (client) {
    673         client->handleConfigureRequest(event);
    674     }
    675     else {
    676         XWindowChanges wc;
    677 
    678         wc.x = event->x;
    679         wc.y = event->y;
    680         wc.width = event->width;
    681         wc.height = event->height;
    682         wc.border_width = 0;
    683         wc.sibling = None;
    684         wc.stack_mode = Above;
    685         event->value_mask &= ~CWStackMode;
    686         event->value_mask |= CWBorderWidth;
    687 
    688         XCORE->configureWindow(event->window, event->value_mask, &wc);
    689     }
    690 }
    691 
    692 void Kernel::handleDestroyNotify(XDestroyWindowEvent *event) {
    693 
    694     Client *client = clientForWindow(event->window);
    695 
    696     if (client) {
    697         if (event->window == client->clientWindow()) {
    698             client->setDestroyed(true);
    699             client->monitor()->removeClient(client);
    700             delete client;
    701         }
    702     }
    703 }
    704 
    705 void Kernel::handleExpose(XExposeEvent *event) {
    706 
    707     Bar *bar = barWindow(event->window);
    708     if (bar) {
    709         bar->illuminate();
    710         return;
    711     }
    712 
    713 #ifdef SLOT_SUPPORT
    714     Slot *slot = slotWindow(event->window);
    715     if (slot) {
    716         slot->illuminate();
    717         return;
    718     }
    719 #endif // SLOT_SUPPORT
    720 
    721     Thing *thing = thingWindow(event->window);
    722     if (thing) {
    723         thing->illuminate();
    724     }
    725 }
    726 
    727 void Kernel::handleKeyPress(XKeyEvent *event) {
    728 
    729     event->state &= Binder::instance()->validModMask();
    730 
    731     if (isInputMode()) {
    732         focusedMonitor()->inputBar()->handleInput(event);
    733     }
    734     else {
    735         Binder::instance()->handleKey(event->root, event);
    736         updateBars();
    737     }
    738 }
    739 
    740 void Kernel::handleMapRequest(XMapRequestEvent *event) {
    741 
    742     if (isWMIWindow(event->window)) {
    743         return;
    744     }
    745 
    746     XWindowAttributes attr;
    747     XCORE->windowAttributes(event->window, &attr);
    748 
    749     if (attr.override_redirect) {
    750         LOGDEBUG("override redirect!");
    751         return;
    752     }
    753 
    754     Client *client = clientForWindow(event->window);
    755 
    756     if (!client) {
    757         client = new Client(focusedMonitor(), event->window, &attr);
    758         focusedMonitor()->addClient(client);
    759         LOGDEBUG("client created");
    760     }
    761     else if (client->attached()) {
    762         client->setRequestsFocus(true);
    763         client->attached()->setRequestsFocus(true);
    764         return; // we ignore Map Requests from those clients
    765     }
    766 
    767     ostringstream oss;
    768     oss << "client dims: " << client->x() << "," <<
    769         client->y() << "," << client->width() << "," <<
    770         client->height();
    771     LOGDEBUG(oss.str());
    772 
    773     /** Initialize names. */
    774     focusedMonitor()->attachClient(client);
    775 }
    776 
    777 void Kernel::handleMotionNotify(XMotionEvent *event) {
    778 
    779     Menu *menu = menuWindow(event->window);
    780     if (menu) {
    781         menu->handleMotionNotify(event);
    782     }
    783 
    784     Bar *bar = barWindow(event->window);
    785     if (bar) {
    786         bar->handleMotionNotify(event);
    787         return;
    788     }
    789 
    790     Thing *thing = thingWindow(event->window);
    791     if (thing) {
    792         if (!thing->isFocused() && isSloppyMode_) {
    793             Workspace *workspace = focusedMonitor()->focused();
    794             workspace->focus(thing, false);
    795         }
    796         thing->handleMotionNotify(event);
    797     }
    798 
    799     // focusses the monitor if multihead
    800     isRootWindow(event->root);
    801 }
    802 
    803 void Kernel::handlePropertyNotify(XPropertyEvent *event) {
    804 
    805     if (!isRootWindow(event->window)) {
    806 
    807         Client *client = clientForWindow(event->window);
    808         ostringstream oss;
    809         oss << "property atom: " << XCORE->atomName(event->atom);
    810         LOGDEBUG(oss.str());
    811 
    812         if (client) {
    813             client->handlePropertyNotify(event);
    814         }
    815         return;
    816     }
    817 
    818     // check if status text has been changed
    819     if (event->atom == Atoms::WMI_STATUSTEXT ||
    820         event->atom == Atoms::WMI_METERTEXT)
    821     {
    822         focusedMonitor()->updateBars();
    823         return;
    824     }
    825     else if (event->atom == Atoms::WMI_ACTIONCMD) {
    826 
    827         // TODO
    828         // action[+arg1[...]]
    829         string actionCmd;
    830         if (XCORE->textProperty(defaultRootWindow(),
    831                     Atoms::WMI_ACTIONCMD, &actionCmd))
    832         {
    833             string bind, arg;
    834             unsigned int pos = actionCmd.find_first_of('+');
    835             if (pos != string::npos) {
    836                 bind = actionCmd.substr(0, pos);
    837                 arg = actionCmd.substr(pos + 1);
    838             }
    839             else {
    840                 bind = actionCmd;
    841             }
    842 
    843             // query action
    844             Action *action = Util::get(actionBindings_, bind);
    845             if (action && pos != string::npos) {
    846                 action->setArgument((char *)arg.c_str());
    847             }
    848 
    849             if (action) {
    850                 if ((action->promptsCount() > 0) && !action->argument()) {
    851                     focusedMonitor()->inputBar()->runArgument(action);
    852                     // action will be performed by argument processing
    853                 }
    854                 else {
    855                     action->perform();
    856                 }
    857             }
    858 
    859         }
    860         return;
    861     }
    862     else if (event->atom == Atoms::WMI_PRETTYPRINT_REQUEST) {
    863 
    864         XTextProperty property;
    865         XCORE->stringListToTextProperty(
    866                 Binder::instance()->prettyPrintKeyBindings(), &property);
    867         XCORE->setTextProperty(display_, defaultRootWindow(),
    868                 &property, Atoms::WMI_PRETTYPRINT_RESPONSE);
    869     }
    870 }
    871 
    872 void Kernel::handleUnmapNotify(XUnmapEvent *event) {
    873     LOGDEBUG("handle unmap notify");
    874     Client *client = clientForWindow(event->window);
    875 
    876     if (client) {
    877         LOGDEBUG("got unmap client");
    878         client->handleUnmapNotify(event);
    879     }
    880 }
    881 
    882 void Kernel::handleClientMessage(XClientMessageEvent *event) {
    883 
    884     LOGDEBUG("handle client message");
    885     if (event->message_type == Atoms::WM_CHANGE_STATE) {
    886         Client *client = clientForWindow(event->window);
    887 
    888         if (client  && client->monitor()
    889            && (event->format == 32)
    890            && (event->data.l[0] == IconicState))
    891         {
    892             client->monitor()->detachClient(client);
    893         }
    894     }
    895 }
    896 
    897 int Kernel::run() {
    898     XEvent event;
    899 
    900     LOGDEBUG("main event loop launched");
    901     ostringstream oss;
    902     while (runlevel_ == Kernel::RUN) {
    903 
    904         XCORE->nextEvent(&event);
    905         switch (event.type) {
    906         case ButtonPress:
    907             LOGDEBUG("button press event");
    908             handleButtonPress(&event.xbutton);
    909             break;
    910         case ButtonRelease:
    911             LOGDEBUG("button release event");
    912             handleButtonRelease(&event.xbutton);
    913             break;
    914         case ClientMessage:
    915             LOGDEBUG("client message event");
    916             handleClientMessage(&event.xclient);
    917             break;
    918         case ConfigureRequest:
    919             LOGDEBUG("configure request event");
    920             handleConfigureRequest(&event.xconfigurerequest);
    921             break;
    922         case DestroyNotify:
    923             LOGDEBUG("destroy window event");
    924             handleDestroyNotify(&event.xdestroywindow);
    925             break;
    926         case Expose:
    927             LOGDEBUG("expose event");
    928             handleExpose(&event.xexpose);
    929             break;
    930         case KeyPress:
    931             LOGDEBUG("keypress event");
    932             handleKeyPress(&event.xkey);
    933             break;
    934         case MapRequest:
    935             LOGDEBUG("map request event");
    936             handleMapRequest(&event.xmaprequest);
    937             break;
    938         case MotionNotify:
    939             LOGDEBUG("motion event");
    940             handleMotionNotify(&event.xmotion);
    941             break;
    942         case PropertyNotify:
    943             LOGDEBUG("property event");
    944             handlePropertyNotify(&event.xproperty);
    945             break;
    946         case UnmapNotify:
    947             LOGDEBUG("unmap event");
    948             handleUnmapNotify(&event.xunmap);
    949             break;
    950         }
    951 
    952     }
    953     return 0;
    954 }
    955 
    956 void Kernel::runResizeMode(Thing *thing, XButtonEvent *buttonEvent,
    957                            Direction dir, bool resize)
    958 {
    959     Window window = thing->window();
    960     if (!window) {
    961         return;
    962     }
    963 
    964     Monitor *monitor = thing->monitor();
    965     Window rootWindow = monitor->rootWindow();
    966     Thing::Type type = thing->type();
    967     Workspace *workspace = (type == Thing::CLIENT) ?
    968         ((Client *)thing)->attached() : ((Frame *)thing)->attached();
    969 
    970     if (!workspace) {
    971         return;
    972     }
    973 
    974     int origX, origY, lastX, lastY, newX, newY, dx, dy;
    975     Rectangle rect(*thing);
    976 
    977     XCORE->translateCoordinates(window, rootWindow,
    978                                  buttonEvent->x, buttonEvent->y,
    979                                  &lastX, &lastY);
    980     newX = newY = 0;
    981     origX = lastX;
    982     origY = lastY;
    983 
    984     XCORE->grabPointer(window,
    985                        ButtonMotionMask | ButtonReleaseMask,
    986                        buttonEvent->time);
    987 
    988     XEvent event;
    989     bool done = false, found = false, firstMotion = true;
    990 
    991     XCORE->grabServer();
    992     while (!done) {
    993         found = false;
    994 
    995         while (XCORE->checkMaskEvent(
    996                     ButtonReleaseMask | ButtonMotionMask, &event))
    997         {
    998             found = true;
    999 
   1000             if (event.type != MotionNotify) {
   1001                 break;
   1002             }
   1003         }
   1004 
   1005         if (!found) {
   1006             usleep(20000);
   1007             continue;
   1008         }
   1009 
   1010         switch (event.type) {
   1011         case ButtonRelease:
   1012             if (!firstMotion) {
   1013                 monitor->illuminateTransRect(&rect, thing->titleBarHeight());
   1014 
   1015                 dx = newX - origX;
   1016                 dy = newY - origY;
   1017 
   1018                 if ((dir == LEFT) || (dir == UP)) {
   1019                     // normalization
   1020                     dx *= -1;
   1021                     dy *= -1;
   1022                 }
   1023 
   1024                 if (type == Thing::FRAME) {
   1025                     Split::resize(workspace->root(),
   1026                                   ((Frame *)thing)->tree(),
   1027                                   dir, dx, dy);
   1028                 }
   1029                 else {
   1030                     thing->copy(&rect);
   1031                     thing->resize();
   1032                     thing->illuminate();
   1033                 }
   1034                 XCORE->sync();
   1035             }
   1036             XCORE->ungrabPointer(event.xbutton.time);
   1037             XCORE->ungrabServer();
   1038             done = true;
   1039             break; // ButtonRelease
   1040         case MotionNotify:
   1041             XCORE->translateCoordinates(event.xmotion.window,
   1042                     rootWindow, event.xmotion.x, event.xmotion.y,
   1043                     &newX, &newY);
   1044 
   1045             dx = newX - lastX;
   1046             dy = newY - lastY;
   1047 
   1048             lastX = newX;
   1049             lastY = newY;
   1050 
   1051             if (firstMotion) {
   1052                 firstMotion = false;
   1053             }
   1054             else {
   1055                 monitor->illuminateTransRect(&rect, thing->titleBarHeight());
   1056             }
   1057 
   1058             if (type == Thing::FRAME) {
   1059                 switch (dir) {
   1060                 case LEFT:
   1061                     rect.setX(rect.x() + dx);
   1062                     rect.setWidth(rect.width() - dx);
   1063                     break;
   1064                 case RIGHT:
   1065                     rect.setWidth(rect.width() + dx);
   1066                     break;
   1067                 case UP:
   1068                     rect.setY(rect.y() + dy);
   1069                     rect.setHeight(rect.height() - dy);
   1070                     break;
   1071                 case DOWN:
   1072                     rect.setHeight(rect.height() + dy);
   1073                     break;
   1074                 default:
   1075                     break;
   1076                 }
   1077             }
   1078             else {
   1079 
   1080                 if (resize) {
   1081                     Float::resize(&rect, dir, dx, dy);
   1082                 }
   1083                 else {
   1084                     Float::move(&rect, dx, dy);
   1085                 }
   1086             }
   1087             monitor->illuminateTransRect(&rect, thing->titleBarHeight());
   1088             break; // MotionNotify
   1089         }
   1090     }
   1091 }
   1092 
   1093 void Kernel::beginRecording() {
   1094     isRecording_ = true;
   1095     assert(recordedActions_.size() == 0);
   1096 }
   1097 
   1098 void Kernel::endChainRecording(string name) {
   1099     isRecording_ = false;
   1100     if (recordedActions_.size() == 0) {
   1101         return;
   1102     }
   1103 
   1104     string chain = "";
   1105     Action *firstAction = 0;
   1106     for (LAction::iterator it = recordedActions_.begin();
   1107          it != recordedActions_.end(); )
   1108     {
   1109         Action *action = *it;
   1110 
   1111         it++;
   1112         if (action->argument()) {
   1113             chain += action->id() + "+" + action->argument();
   1114         }
   1115         else {
   1116             chain += action->id();
   1117         }
   1118         if (it != recordedActions_.end()) {
   1119             chain += ",";
   1120         }
   1121 
   1122         if (it == recordedActions_.begin()) {
   1123             firstAction = action;
   1124         }
   1125         else {
   1126             delete action;
   1127         }
   1128     }
   1129     // clean up
   1130     recordedActions_.clear();
   1131 
   1132     IsValid isValid = 0;
   1133     if (firstAction) {
   1134         isValid = firstAction->getIsValid();
   1135     }
   1136     else {
   1137         isValid = &Validators::isAlwaysPossible;
   1138     }
   1139     delete firstAction;
   1140 
   1141     (*actionSettings_)["chain." + name + ".seq"] = chain;
   1142     Action *action = new Action(name, &Actions::sequence, isValid,
   1143                                 Action::SEQUENCE, (char *)chain.c_str());
   1144     (*actionBindings_)[name] = action;
   1145 }
   1146 
   1147 void Kernel::cancelRecording() {
   1148     isRecording_ = false;
   1149     if (recordedActions_.size() == 0) {
   1150         return;
   1151     }
   1152     // clean up
   1153     recordedActions_.clear();
   1154 }
   1155 
   1156 
   1157 void Kernel::endScriptRecording(string name) {
   1158     isRecording_ = false;
   1159     if (recordedActions_.size() == 0) {
   1160         return;
   1161     }
   1162 
   1163     string script = "#!/bin/sh\n";
   1164     for (LAction::iterator it = recordedActions_.begin();
   1165          it != recordedActions_.end(); )
   1166     {
   1167         Action *action = *it;
   1168 
   1169         it++;
   1170         script += "wmiremote -a \"";
   1171         if (action->argument()) {
   1172             script += action->id() + "+" + action->argument();
   1173         }
   1174         else {
   1175             script += action->id();
   1176         }
   1177         script += "\"\n";
   1178 
   1179         delete action;
   1180     }
   1181     // clean up
   1182     recordedActions_.clear();
   1183 
   1184 
   1185     Loader::saveFile(name, script);
   1186 }
   1187 
   1188 
   1189 MSettings *Kernel::themeSettings() {
   1190     return themeSettings_; 
   1191 }
   1192 
   1193 MSettings *Kernel::commonSettings() {
   1194     return commonSettings_; 
   1195 }
   1196 
   1197 MSettings *Kernel::actionSettings() {
   1198     return actionSettings_; 
   1199 }
   1200 
   1201 MSettings *Kernel::sessionSettings() {
   1202     return sessionSettings_; 
   1203 }
   1204 
   1205 Display *Kernel::display() const {
   1206     return display_;
   1207 }
   1208 
   1209 Monitor *Kernel::focusedMonitor() const {
   1210     return monitors_->focused();
   1211 }
   1212 
   1213 Kernel::Runlevel Kernel::runlevel() const {
   1214     return runlevel_;
   1215 }
   1216 
   1217 void Kernel::restart() {
   1218     runlevel_ = RESTART;
   1219 }
   1220 
   1221 void Kernel::stop() {
   1222     runlevel_ = STOP;
   1223 }
   1224 
   1225 MBindings *Kernel::actionBindings() const {
   1226     return actionBindings_;
   1227 }
   1228 
   1229 
   1230 bool Kernel::isRecording() const {
   1231     return isRecording_;
   1232 }
   1233 
   1234 LAction *Kernel::recordedActions() {
   1235     return &recordedActions_;
   1236 }
   1237 
   1238 unsigned int Kernel::borderWidth() const {
   1239     return borderWidth_;
   1240 }
   1241 
   1242 void Kernel::setMenuMode(bool isMenuMode) {
   1243     isMenuMode_ = isMenuMode;
   1244 }
   1245 
   1246 void Kernel::toggleSloppyMode() {
   1247     isSloppyMode_ = !isSloppyMode_;
   1248 }
   1249 
   1250 bool Kernel::isStackedTabbing() const {
   1251     return isStackedTabbing_;
   1252 }
   1253 
   1254 CMonitor *Kernel::monitors() const {
   1255     return monitors_;
   1256 }
   1257 
   1258 void Kernel::selectMonitor(string displayString) {
   1259 
   1260     for (LMonitor::iterator it = monitors_->begin();
   1261             it != monitors_->end(); it++)
   1262     {
   1263         Monitor *monitor = *it;
   1264         if (monitor->displayString() == displayString) {
   1265             if (monitors_->focused() != monitor) {
   1266                 XCORE->warpPointer(monitor->rootWindow(),
   1267                                    monitor->width() / 2,
   1268                                    monitor->height() / 2);
   1269                 monitors_->focus(monitor);
   1270                 Workspace *ws = monitor->focused();
   1271                 ws->focus(ws->topClient());
   1272             }
   1273             return;
   1274         }
   1275     }
   1276 }
   1277 
   1278 void Kernel::grabMove() {
   1279 
   1280     Client *client = focusedClient();
   1281     if (!client || client->frame()) {
   1282         return;
   1283     }
   1284     XCORE->warpPointer(client->window(),
   1285                        client->width() / 2,
   1286                        client->height() / 2);
   1287     XButtonEvent event;
   1288     event.x = client->width() / 2;
   1289     event.y = client->height() / 2;
   1290     event.time = CurrentTime;
   1291     runResizeMode(client, &event, LEFT, false);
   1292 }