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 }