// // WindowManager.cc for pekwm // Copyright © 2002-2009 Claes Nästén // // windowmanager.cc for aewm++ // Copyright (C) 2000 Frank Hale // http://sapphire.sourceforge.net/ // // This program is licensed under the GNU GPL. // See the LICENSE file for more information. // #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include "PWinObj.hh" #include "PDecor.hh" #include "Frame.hh" #include "Client.hh" #include "WindowManager.hh" #include "PScreen.hh" #include "ScreenResources.hh" #include "ActionHandler.hh" #include "AutoProperties.hh" #include "Config.hh" #include "Theme.hh" #include "PFont.hh" #include "PTexture.hh" #include "ColorHandler.hh" #include "FontHandler.hh" #include "PixmapHandler.hh" #include "TextureHandler.hh" #include "Workspaces.hh" #include "Util.hh" #include "RegexString.hh" #include "KeyGrabber.hh" #include "MenuHandler.hh" #include "Harbour.hh" #include "HarbourMenu.hh" #include "DockApp.hh" #include "CmdDialog.hh" #include "SearchDialog.hh" #include "StatusWindow.hh" #include "WorkspaceIndicator.hh" #include #include #include #include #include #include extern "C" { #include #include #include #include #include #include #include #ifdef HAVE_XRANDR #include #endif // HAVE_XRANDR } using std::cout; using std::cerr; using std::endl; using std::find; using std::list; using std::map; using std::mem_fun; using std::string; using std::vector; using std::wstring; // Static initializers WindowManager *WindowManager::_instance = 0; extern "C" { static bool is_signal_hup = false; static bool is_signal_int_term = false; static bool is_signal_alrm = false; /** * Signal handler setting signal flags. */ static void sigHandler(int signal) { switch (signal) { case SIGHUP: is_signal_hup = true; break; case SIGINT: case SIGTERM: is_signal_int_term = true; break; case SIGCHLD: wait(0); break; case SIGALRM: // Do nothing, just used to break out of waiting is_signal_alrm = true; break; } } } // extern "C" // WindowManager /** * Create window manager instance and run main routine. */ WindowManager* WindowManager::start(const std::string &command_line, const std::string &config_file, bool replace) { if (_instance) { delete _instance; } // Setup window manager _instance = new WindowManager(command_line, config_file); if (_instance->setupDisplay(replace)) { _instance->scanWindows(); Frame::resetFrameIDs(); static_cast(PWinObj::getRootPWinObj())->setEwmhDesktopNames(); // add all frames to the MRU list _instance->_mru_list.resize(Frame::frame_size()); copy(Frame::frame_begin(), Frame::frame_end (), _instance->_mru_list.begin()); _instance->execStartFile(); } else { delete _instance; _instance = 0; } return _instance; } /** * Destroy current running window manager. */ void WindowManager::destroy(void) { delete _instance; _instance = 0; } //! @brief Constructor for WindowManager class WindowManager::WindowManager(const std::string &command_line, const std::string &config_file) : _screen(0), _screen_resources(0), _keygrabber(0), _config(0), _color_handler(0), _font_handler(0), _texture_handler(0), _theme(0), _action_handler(0), _autoproperties(0), _workspaces(0), _harbour(0), _cmd_dialog(0), _search_dialog(0), _status_window(0), _workspace_indicator(0), _command_line(command_line), _startup(false), _shutdown(false), _reload(false), _allow_grouping(true), _hint_wo(0), _root_wo(0) { struct sigaction act; // Set up the signal handlers. act.sa_handler = sigHandler; act.sa_mask = sigset_t(); act.sa_flags = SA_NOCLDSTOP | SA_NODEFER; sigaction(SIGTERM, &act, 0); sigaction(SIGINT, &act, 0); sigaction(SIGHUP, &act, 0); sigaction(SIGCHLD, &act, 0); sigaction(SIGALRM, &act, 0); // construct _config = new Config(); _config->load(config_file); _config->loadMouseConfig(_config->getMouseConfigFile()); } //! @brief WindowManager destructor WindowManager::~WindowManager(void) { cleanup(); delete _cmd_dialog; delete _search_dialog; delete _status_window; delete _workspace_indicator; MenuHandler::destroy(); delete _harbour; delete _root_wo; delete _hint_wo; delete _action_handler; delete _autoproperties; delete _keygrabber; delete _workspaces; delete _config; delete _theme; delete _color_handler; delete _font_handler; delete _texture_handler; delete _screen_resources; if (_screen) { Display *dpy = _screen->getDpy(); delete _screen; XCloseDisplay(dpy); } } //! @brief Checks if the start file is executable then execs it. void WindowManager::execStartFile(void) { string start_file(_config->getStartFile()); bool exec = Util::isExecutable(start_file); if (! exec) { start_file = SYSCONFDIR "/start"; exec = Util::isExecutable(start_file); } if (exec) { Util::forkExec(start_file); } } //! @brief Cleans up, Maps windows etc. void WindowManager::cleanup(void) { // update all nonactive clients properties list::iterator it_f(Frame::frame_begin()); for (; it_f != Frame::frame_end(); ++it_f) { (*it_f)->updateInactiveChildInfo(); } _harbour->removeAllDockApps(); // To preserve stacking order when destroying the frames, we go through // the PWinObj list from the Workspaces and put all Frames into our own // list, then we delete the frames in order. if (_workspaces) { list frame_list; list::iterator it_w(_workspaces->begin()); for (; it_w != _workspaces->end(); ++it_w) { if ((*it_w)->getType() == PWinObj::WO_FRAME) { frame_list.push_back(static_cast(*it_w)); } } // Delete all Frames. This reparents the Clients. for (it_f = frame_list.begin(); it_f != frame_list.end(); ++it_f) { delete *it_f; } } // Delete all Clients. list client_list(Client::client_begin(), Client::client_end()); list::iterator it_c(client_list.begin()); for (; it_c != client_list.end(); ++it_c) { delete *it_c; } if (_keygrabber) { _keygrabber->ungrabKeys(_screen->getRoot()); } // destroy screen edge screenEdgeDestroy(); XInstallColormap(_screen->getDpy(), _screen->getColormap()); XSetInputFocus(_screen->getDpy(), PointerRoot, RevertToPointerRoot, CurrentTime); } /** * Setup display and claim resources. */ bool WindowManager::setupDisplay(bool replace) { Display *dpy = XOpenDisplay(0); if (! dpy) { cerr << "Can not open display!" << endl << "Your DISPLAY variable currently is set to: " << getenv("DISPLAY") << endl; exit(1); } // Setup screen, init atoms and claim the display. _screen = new PScreen(dpy, _config->isHonourRandr()); PWinObj::setDisplay(dpy); Atoms::init(); try { // Create hint window _before_ root window. _hint_wo = new HintWO(_screen->getRoot(), replace); } catch (string &ex) { return false; } // Create root PWinObj _root_wo = new RootWO(_screen->getRoot()); PWinObj::setRootPWinObj(_root_wo); _color_handler = new ColorHandler(dpy); _font_handler = new FontHandler(); _texture_handler = new TextureHandler(); _action_handler = new ActionHandler(); // Setup the font trimming PFont::setTrimString(_config->getTrimTitle()); // load colors, fonts _screen_resources = new ScreenResources(); _theme = new Theme(_screen); _autoproperties = new AutoProperties(); _autoproperties->load(); _workspaces = new Workspaces(_config->getWorkspaces(), _config->getWorkspacesPerRow()); _harbour = new Harbour(_screen, _theme, _workspaces); MenuHandler::init(_theme); _cmd_dialog = new CmdDialog(_theme); _search_dialog = new SearchDialog(_theme); _status_window = new StatusWindow(_theme); _workspace_indicator = new WorkspaceIndicator(_theme, _timer_action); XDefineCursor(dpy, _screen->getRoot(), _screen_resources->getCursor(ScreenResources::CURSOR_ARROW)); #ifdef HAVE_XRANDR XRRSelectInput(dpy, _screen->getRoot(), RRScreenChangeNotifyMask|RRCrtcChangeNotifyMask); #endif // HAVE_XRANDR _keygrabber = new KeyGrabber(_screen); _keygrabber->load(_config->getKeyFile()); _keygrabber->grabKeys(_screen->getRoot()); // Create screen edge windows screenEdgeCreate(); screenEdgeMapUnmap(); return true; } //! @brief Goes through the window and creates Clients/DockApps. void WindowManager::scanWindows(void) { if (_startup) { // only done once when we start return; } uint num_wins; Window d_win1, d_win2, *wins; XWindowAttributes attr; // Lets create a list of windows on the display XQueryTree(_screen->getDpy(), _screen->getRoot(), &d_win1, &d_win2, &wins, &num_wins); list win_list(wins, wins + num_wins); XFree(wins); list::iterator it(win_list.begin()); // We filter out all windows with the the IconWindowHint // set not pointing to themselves, making DockApps // work as they are supposed to. for (; it != win_list.end(); ++it) { if (*it == None) { continue; } XWMHints *wm_hints = XGetWMHints(_screen->getDpy(), *it); if (wm_hints) { if ((wm_hints->flags&IconWindowHint) && (wm_hints->icon_window != *it)) { list::iterator i_it(find(win_list.begin(), win_list.end(), wm_hints->icon_window)); if (i_it != win_list.end()) *i_it = None; } XFree(wm_hints); } } Client *client; for (it = win_list.begin(); it != win_list.end(); ++it) { if (*it == None) { continue; } XGetWindowAttributes(_screen->getDpy(), *it, &attr); if (! attr.override_redirect && attr.map_state != IsUnmapped) { XWMHints *wm_hints = XGetWMHints(_screen->getDpy(), *it); if (wm_hints) { if ((wm_hints->flags&StateHint) && (wm_hints->initial_state == WithdrawnState)) { _harbour->addDockApp(new DockApp(_screen, _theme, *it)); } else { client = new Client(*it); if (! client->isAlive()) { delete client; } } XFree(wm_hints); } else { client = new Client(*it); if (! client->isAlive()) delete client; } } } // Try to focus the ontop window, if no window we give root focus PWinObj *wo = _workspaces->getTopWO(PWinObj::WO_FRAME); if (wo && wo->isMapped()) { wo->giveInputFocus(); } else { _root_wo->giveInputFocus(); } // Try to find transients for all clients, on restarts ordering might // not be correct. list::iterator it_client = Client::client_begin(); for (; it_client != Client::client_end(); ++it_client) { if ((*it_client)->isTransient() && ! (*it_client)->getTransientClient()) { (*it_client)->findAndRaiseIfTransient(); } } // We won't be needing these anymore until next restart _autoproperties->removeApplyOnStart(); _startup = true; } //! @brief Creates and places screen edge void WindowManager::screenEdgeCreate(void) { if (_screen_edge_list.size() != 0) { return; } bool indent = Config::instance()->getScreenEdgeIndent(); _screen_edge_list.push_back(new EdgeWO(_screen->getRoot(), SCREEN_EDGE_LEFT, indent && (_config->getScreenEdgeSize(SCREEN_EDGE_LEFT) > 0))); _screen_edge_list.push_back(new EdgeWO(_screen->getRoot(), SCREEN_EDGE_RIGHT, indent && (_config->getScreenEdgeSize(SCREEN_EDGE_RIGHT) > 0))); _screen_edge_list.push_back(new EdgeWO(_screen->getRoot(), SCREEN_EDGE_TOP, indent && (_config->getScreenEdgeSize(SCREEN_EDGE_TOP) > 0))); _screen_edge_list.push_back(new EdgeWO(_screen->getRoot(), SCREEN_EDGE_BOTTOM, indent && (_config->getScreenEdgeSize(SCREEN_EDGE_BOTTOM) > 0))); // make sure the edge stays ontop list::iterator it(_screen_edge_list.begin()); for (; it != _screen_edge_list.end(); ++it) { _workspaces->insert(*it); } screenEdgeResize(); } //! @brief void WindowManager::screenEdgeDestroy(void) { if (_screen_edge_list.size() == 0) { return; } list::iterator it(_screen_edge_list.begin()); for (; it != _screen_edge_list.end(); ++it) { _workspaces->remove(*it); delete *it; } } //! @brief void WindowManager::screenEdgeResize(void) { assert(_screen_edge_list.size() == 4); uint l_size = std::max(_config->getScreenEdgeSize(SCREEN_EDGE_LEFT), 1); uint r_size = std::max(_config->getScreenEdgeSize(SCREEN_EDGE_RIGHT), 1); uint t_size = std::max(_config->getScreenEdgeSize(SCREEN_EDGE_TOP), 1); uint b_size = std::max(_config->getScreenEdgeSize(SCREEN_EDGE_BOTTOM), 1); list::iterator it(_screen_edge_list.begin()); // Left edge (*it)->moveResize(0, 0, l_size, _screen->getHeight()); ++it; // Right edge (*it)->moveResize(_screen->getWidth() - r_size, 0, r_size, _screen->getHeight()); ++it; // Top edge (*it)->moveResize(l_size, 0, _screen->getWidth() - l_size - r_size, t_size); ++it; // Bottom edge (*it)->moveResize(l_size, _screen->getHeight() - b_size, _screen->getWidth() - l_size - r_size, b_size); for (it = _screen_edge_list.begin(); it != _screen_edge_list.end(); ++it) { (*it)->configureStrut(_config->getScreenEdgeIndent() && (_config->getScreenEdgeSize((*it)->getEdge()) > 0)); } _screen->updateStrut(); } void WindowManager::screenEdgeMapUnmap(void) { assert (_screen_edge_list.size() == 4); list::iterator it(_screen_edge_list.begin()); for (; it != _screen_edge_list.end(); ++it) { if ((_config->getScreenEdgeSize((*it)->getEdge()) > 0) && (_config->getEdgeListFromPosition((*it)->getEdge())->size() > 0)) { (*it)->mapWindow(); } else { (*it)->unmapWindow(); } } } //! @brief Reloads configuration and updates states. void WindowManager::doReload(void) { doReloadConfig(); doReloadTheme(); doReloadMouse(); doReloadKeygrabber(); doReloadAutoproperties(); MenuHandler::instance()->reloadMenus(); // Special case for HARBOUR menu which is not included in the menu map _harbour->getHarbourMenu()->reload(static_cast(0)); doReloadHarbour(); _root_wo->setEwmhDesktopNames(); _reload = false; } /** * Reload main config file. */ void WindowManager::doReloadConfig(void) { // If any of these changes, re-fetch of all names is required bool old_client_unique_name = _config->getClientUniqueName(); string old_client_unique_name_pre = _config->getClientUniqueNamePre(); string old_client_unique_name_post = _config->getClientUniqueNamePost(); // Reload configuration if (! _config->load(_config->getConfigFile())) { return; } // Update what might have changed in the cfg touching the hints _workspaces->setSize(_config->getWorkspaces()); _workspaces->setPerRow(_config->getWorkspacesPerRow()); _workspaces->setNames(); // Flush pixmap cache and set size _screen_resources->getPixmapHandler()->setCacheSize(_config->getScreenPixmapCacheSize()); // Set the font title trim before reloading the themes PFont::setTrimString(_config->getTrimTitle()); // Update the ClientUniqueNames if needed if ((old_client_unique_name != _config->getClientUniqueName()) || (old_client_unique_name_pre != _config->getClientUniqueNamePre()) || (old_client_unique_name_post != _config->getClientUniqueNamePost())) { for_each(Client::client_begin(), Client::client_end(), mem_fun(&Client::readName)); } // Resize the screen edge screenEdgeResize(); screenEdgeMapUnmap(); _screen->updateStrut(); } /** * Reload theme file and update decorations. */ void WindowManager::doReloadTheme(void) { // Reload the theme if (! _theme->load(_config->getThemeFile())) { return; } // Reload the themes on all decors for_each(PDecor::pdecor_begin(), PDecor::pdecor_end(), mem_fun(&PDecor::loadDecor)); } /** * Reload mouse configuration and re-grab buttons on all windows. */ void WindowManager::doReloadMouse(void) { if (! _config->loadMouseConfig(_config->getMouseConfigFile())) { return; } for_each(Client::client_begin(), Client::client_end(), mem_fun(&Client::grabButtons)); } /** * Reload keygrabber configuration and re-grab keys on all windows. */ void WindowManager::doReloadKeygrabber(bool force) { // Reload the keygrabber if (! _keygrabber->load(_config->getKeyFile(), force)) { return; } _keygrabber->ungrabKeys(_screen->getRoot()); _keygrabber->grabKeys(_screen->getRoot()); // Regrab keys and buttons list::iterator c_it(Client::client_begin()); for (; c_it != Client::client_end(); ++c_it) { (*c_it)->grabButtons(); _keygrabber->ungrabKeys((*c_it)->getWindow()); _keygrabber->grabKeys((*c_it)->getWindow()); } } /** * Reload autoproperties. */ void WindowManager::doReloadAutoproperties(void) { if (! _autoproperties->load()) { return; } // NOTE: we need to load autoproperties after decor have been updated // as otherwise old theme data pointer will be used and sig 11 pekwm. list::iterator it_c(Client::client_begin()); for (; it_c != Client::client_end(); ++it_c) { (*it_c)->readAutoprops(APPLY_ON_RELOAD); } list::iterator it_f(Frame::frame_begin()); for (; it_f != Frame::frame_end(); ++it_f) { (*it_f)->readAutoprops(APPLY_ON_RELOAD); } } /** * Reload harbour configuration. */ void WindowManager::doReloadHarbour(void) { _harbour->loadTheme(); _harbour->rearrange(); _harbour->restack(); _harbour->updateHarbourSize(); } //! @brief Exit pekwm and restart with the command command void WindowManager::restart(std::string command) { if (command.size() == 0) { command = _command_line; } _restart_command = command; _shutdown = true; } // Event handling routins beneath this ===================================== void WindowManager::doEventLoop(void) { XEvent ev; Timer::timed_event_list events; while (! _shutdown && ! is_signal_int_term) { // Handle timeouts if (is_signal_alrm) { is_signal_alrm = false; if (_timer_action.getTimedOut(events)) { Timer::timed_event_list_it it(events.begin()); for (; it != events.end(); ++it) { _action_handler->handleAction((*it)->data); } events.clear(); } } // Reload if requested if (is_signal_hup || _reload) { is_signal_hup = false; doReload(); } // Get next event, drop event handling if none was given if (_screen->getNextEvent(ev)) { switch (ev.type) { case MapRequest: handleMapRequestEvent(&ev.xmaprequest); break; case UnmapNotify: handleUnmapEvent(&ev.xunmap); break; case DestroyNotify: handleDestroyWindowEvent(&ev.xdestroywindow); break; case ConfigureRequest: handleConfigureRequestEvent(&ev.xconfigurerequest); break; case ClientMessage: handleClientMessageEvent(&ev.xclient); break; case ColormapNotify: handleColormapEvent(&ev.xcolormap); break; case PropertyNotify: _screen->setLastEventTime(ev.xproperty.time); handlePropertyEvent(&ev.xproperty); break; case MappingNotify: handleMappingEvent(&ev.xmapping); break; case Expose: handleExposeEvent(&ev.xexpose); break; case KeyPress: case KeyRelease: _screen->setLastEventTime(ev.xkey.time); handleKeyEvent(&ev.xkey); break; case ButtonPress: _screen->setLastEventTime(ev.xbutton.time); handleButtonPressEvent(&ev.xbutton); break; case ButtonRelease: _screen->setLastEventTime(ev.xbutton.time); handleButtonReleaseEvent(&ev.xbutton); break; case MotionNotify: _screen->setLastEventTime(ev.xmotion.time); handleMotionEvent(&ev.xmotion); break; case EnterNotify: _screen->setLastEventTime(ev.xcrossing.time); handleEnterNotify(&ev.xcrossing); break; case LeaveNotify: _screen->setLastEventTime(ev.xcrossing.time); handleLeaveNotify(&ev.xcrossing); break; case FocusIn: handleFocusInEvent(&ev.xfocus); break; case FocusOut: handleFocusOutEvent(&ev.xfocus); break; case SelectionClear: // Another window cerr << " *** INFO: Being replaced by another WM" << endl; _shutdown = true; break; default: #ifdef HAVE_SHAPE if (_screen->hasExtensionShape() && (ev.type == _screen->getEventShape())) { handleShapeEvent(&ev.xany); } #endif // HAVE_SHAPE #ifdef HAVE_XRANDR if (_screen->hasExtensionXRandr() && (ev.type == _screen->getEventXRandr())) { handleXRandrEvent(reinterpret_cast(&ev)); } #endif // HAVE_XRANDR break; } } } } //! @brief Handle XKeyEvents void WindowManager::handleKeyEvent(XKeyEvent *ev) { ActionEvent *ae = 0; PWinObj *wo, *wo_orig; wo = wo_orig = PWinObj::getFocusedPWinObj(); PWinObj::Type type = (wo) ? wo->getType() : PWinObj::WO_SCREEN_ROOT; switch (type) { case PWinObj::WO_CLIENT: case PWinObj::WO_FRAME: case PWinObj::WO_SCREEN_ROOT: case PWinObj::WO_MENU: if (ev->type == KeyPress) { ae = _keygrabber->findAction(ev, type); } break; case PWinObj::WO_CMD_DIALOG: case PWinObj::WO_SEARCH_DIALOG: if (ev->type == KeyPress) { ae = wo->handleKeyPress(ev); } else { ae = wo->handleKeyRelease(ev); } wo = static_cast(wo)->getWORef(); break; default: if (wo) { if (ev->type == KeyPress) { ae = wo->handleKeyPress(ev); } else { ae = wo->handleKeyRelease(ev); } } break; } if (ae) { // HACK: Always close CmdDialog and SearchDialog before actions if (wo_orig && (wo_orig->getType() == PWinObj::WO_CMD_DIALOG || wo_orig->getType() == PWinObj::WO_SEARCH_DIALOG)) { ::Action close_action; ActionEvent close_ae; close_ae.action_list.push_back(close_action); close_ae.action_list.back().setAction(ACTION_CLOSE); ActionPerformed close_ap(wo_orig, close_ae); _action_handler->handleAction(close_ap); } ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.key = ev; _action_handler->handleAction(ap); } // flush Enter events caused by keygrabbing XEvent e; while (XCheckTypedEvent(_screen->getDpy(), EnterNotify, &e)) { if (! e.xcrossing.send_event) { _screen->setLastEventTime(e.xcrossing.time); } } } //! @brief void WindowManager::handleButtonPressEvent(XButtonEvent *ev) { // Clear event queue while (XCheckTypedEvent(_screen->getDpy(), ButtonPress, (XEvent *) ev)) { if (! ev->send_event) { _screen->setLastEventTime(ev->time); } } ActionEvent *ae = 0; PWinObj *wo = 0; wo = PWinObj::findPWinObj(ev->window); if (wo) { ae = wo->handleButtonPress(ev); if (wo->getType() == PWinObj::WO_FRAME) { // this is done so that clicking the titlebar executes action on // the client clicked on, doesn't apply when subwindow is set (meaning // a titlebar button beeing pressed) if ((ev->subwindow == None) && (ev->window == static_cast(wo)->getTitleWindow())) { wo = static_cast(wo)->getChildFromPos(ev->x); } else { wo = static_cast(wo)->getActiveChild(); } } } if (ae) { ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.button = ev; _action_handler->handleAction(ap); } else { DockApp *da = _harbour->findDockAppFromFrame(ev->window); if (da) { _harbour->handleButtonEvent(ev, da); } } } //! @brief void WindowManager::handleButtonReleaseEvent(XButtonEvent *ev) { // Flush ButtonReleases while (XCheckTypedEvent(_screen->getDpy(), ButtonRelease, (XEvent *) ev)) { if (! ev->send_event) { _screen->setLastEventTime(ev->time); } } ActionEvent *ae = 0; PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo == _root_wo && ev->subwindow != None) { wo = PWinObj::findPWinObj(ev->subwindow); } if (wo) { // Kludge for the case that wo is freed by handleButtonRelease(.). PWinObj::Type wotype = wo->getType(); ae = wo->handleButtonRelease(ev); if (wotype == PWinObj::WO_FRAME) { // this is done so that clicking the titlebar executes action on // the client clicked on, doesn't apply when subwindow is set (meaning // a titlebar button beeing pressed) if ((ev->subwindow == None) && (ev->window == static_cast(wo)->getTitleWindow())) { wo = static_cast(wo)->getChildFromPos(ev->x); } else { wo = static_cast(wo)->getActiveChild(); } } if (ae) { ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.button = ev; _action_handler->handleAction(ap); } } else { DockApp *da = _harbour->findDockAppFromFrame(ev->window); if (da) { _harbour->handleButtonEvent(ev, da); } } } void WindowManager::handleConfigureRequestEvent(XConfigureRequestEvent *ev) { Client *client = Client::findClientFromWindow(ev->window); if (client) { ((Frame*) client->getParent())->handleConfigureRequest(ev, client); } else { DockApp *da = _harbour->findDockApp(ev->window); if (da) { _harbour->handleConfigureRequestEvent(ev, da); } else { // Since this window isn't yet a client lets delegate // the configure request back to the window so it can use it. XWindowChanges wc; wc.x = ev->x; wc.y = ev->y; wc.width = ev->width; wc.height = ev->height; wc.sibling = ev->above; wc.stack_mode = ev->detail; XConfigureWindow(_screen->getDpy(), ev->window, ev->value_mask, &wc); } } } /** * Handle motion event, match on event window expect when event window * is root and subwindow is set then also match on menus. */ void WindowManager::handleMotionEvent(XMotionEvent *ev) { ActionEvent *ae = 0; PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo == _root_wo && ev->subwindow != None) { wo = PWinObj::findPWinObj(ev->subwindow); } if (wo) { if (wo->getType() == PWinObj::WO_CLIENT) { ae = wo->getParent()->handleMotionEvent(ev); } else if (wo->getType() == PWinObj::WO_FRAME) { ae = wo->handleMotionEvent(ev); // this is done so that clicking the titlebar executes action on // the client clicked on if (ev->subwindow != None) { wo = static_cast(wo)->getActiveChild(); } else { wo = static_cast(wo)->getChildFromPos(ev->x); } } else { ae = wo->handleMotionEvent(ev); } if (ae) { ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.motion = ev; _action_handler->handleAction(ap); } } else { DockApp *da = _harbour->findDockAppFromFrame(ev->window); if (da) { _harbour->handleMotionNotifyEvent(ev, da); } } } //! @brief void WindowManager::handleMapRequestEvent(XMapRequestEvent *ev) { PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo) { wo->handleMapRequest(ev); } else { XWindowAttributes attr; XGetWindowAttributes(_screen->getDpy(), ev->window, &attr); if (! attr.override_redirect) { // We need to figure out whether or not this is a dockapp. XWMHints *wm_hints = XGetWMHints(_screen->getDpy(), ev->window); if (wm_hints) { if ((wm_hints->flags&StateHint) && (wm_hints->initial_state == WithdrawnState)) { _harbour->addDockApp(new DockApp(_screen, _theme, ev->window)); } else { Client *client = new Client(ev->window, true); if (! client->isAlive()) { delete client; } } XFree(wm_hints); } else { Client *client = new Client(ev->window, true); if (! client->isAlive()) { delete client; } } } } } //! @brief void WindowManager::handleUnmapEvent(XUnmapEvent *ev) { PWinObj *wo = PWinObj::findPWinObj(ev->window); PWinObj *wo_search = 0; PWinObj::Type wo_type = PWinObj::WO_NO_TYPE; if (wo) { wo_type = wo->getType(); if (wo_type == PWinObj::WO_CLIENT) { wo_search = wo->getParent(); } wo->handleUnmapEvent(ev); if (wo == PWinObj::getFocusedPWinObj()) { PWinObj::setFocusedPWinObj(0); } } else { DockApp *da = _harbour->findDockApp(ev->window); if (da) { if (ev->window == ev->event) { _harbour->removeDockApp(da); } } } if (wo_type != PWinObj::WO_MENU && wo_type != PWinObj::WO_CMD_DIALOG && wo_type != PWinObj::WO_SEARCH_DIALOG && ! PWinObj::getFocusedPWinObj()) { findWOAndFocus(wo_search); } } void WindowManager::handleDestroyWindowEvent(XDestroyWindowEvent *ev) { Client *client = Client::findClientFromWindow(ev->window); if (client) { PWinObj *wo_search = client->getParent(); client->handleDestroyEvent(ev); if (! PWinObj::getFocusedPWinObj()) { findWOAndFocus(wo_search); } } else { DockApp *da = _harbour->findDockApp(ev->window); if (da) { da->setAlive(false); _harbour->removeDockApp(da); } } } void WindowManager::handleEnterNotify(XCrossingEvent *ev) { // Clear event queue while (XCheckTypedEvent(_screen->getDpy(), EnterNotify, (XEvent *) ev)) { if (! ev->send_event) { _screen->setLastEventTime(ev->time); } } if ((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab)) { return; } PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo) { if (wo->getType() == PWinObj::WO_CLIENT) { wo = wo->getParent(); } ActionEvent *ae = wo->handleEnterEvent(ev); if (ae) { ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.crossing = ev; _action_handler->handleAction(ap); } } } void WindowManager::handleLeaveNotify(XCrossingEvent *ev) { // Clear event queue while (XCheckTypedEvent(_screen->getDpy(), LeaveNotify, (XEvent *) ev)) { if (! ev->send_event) { _screen->setLastEventTime(ev->time); } } if ((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab)) { return; } PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo) { ActionEvent *ae = wo->handleLeaveEvent(ev); if (ae) { ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.crossing = ev; _action_handler->handleAction(ap); } } } //! @brief Handles FocusIn Events. void WindowManager::handleFocusInEvent(XFocusChangeEvent *ev) { if ((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab)) { return; } PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo) { // To simplify logic, changing client in frame wouldn't update the // focused window because wo != focused_wo woule be true. if (wo->getType() == PWinObj::WO_FRAME) { wo = static_cast(wo)->getActiveChild(); } if (! wo->isFocusable() || ! wo->isMapped()) { findWOAndFocus(0); } else if (wo != PWinObj::getFocusedPWinObj()) { // If it's a valid FocusIn event with accepatable target lets flush // all EnterNotify and LeaveNotify as they can interfere with // focusing if Sloppy or Follow like focus model is used. XEvent e_flush; while (XCheckTypedEvent(_screen->getDpy(), EnterNotify, &e_flush)) { if (! e_flush.xcrossing.send_event) { _screen->setLastEventTime(e_flush.xcrossing.time); } } while (XCheckTypedEvent(_screen->getDpy(), LeaveNotify, &e_flush)) { if (! e_flush.xcrossing.send_event) { _screen->setLastEventTime(e_flush.xcrossing.time); } } PWinObj *focused_wo = PWinObj::getFocusedPWinObj(); // convenience // unfocus last window if (focused_wo) { if (focused_wo->getType() == PWinObj::WO_CLIENT) { focused_wo->getParent()->setFocused(false); } else { focused_wo->setFocused(false); } _workspaces->setLastFocused(_workspaces->getActive(), focused_wo); } PWinObj::setFocusedPWinObj(wo); if (wo->getType() == PWinObj::WO_CLIENT) { wo->getParent()->setFocused(true); _root_wo->setEwmhActiveWindow(wo->getWindow()); // update the MRU list _mru_list.remove(wo->getParent()); _mru_list.push_back(wo->getParent()); } else { wo->setFocused(true); _root_wo->setEwmhActiveWindow(None); } } } } //! @brief Handles FocusOut Events. void WindowManager::handleFocusOutEvent(XFocusChangeEvent *ev) { // Get the last focus in event, no need to go through them all. while (XCheckTypedEvent(_screen->getDpy(), FocusOut, (XEvent *) ev)) ; } /** * Handle XClientMessageEvent events. * * @param ev Event to handle. */ void WindowManager::handleClientMessageEvent(XClientMessageEvent *ev) { if (ev->window == _screen->getRoot()) { // root window messages if (ev->format == 32) { if (ev->message_type == Atoms::getAtom(NET_CURRENT_DESKTOP)) { _workspaces->setWorkspace(ev->data.l[0], true); } else if (ev->message_type == Atoms::getAtom(NET_NUMBER_OF_DESKTOPS)) { if (ev->data.l[0] > 0) { _workspaces->setSize(ev->data.l[0]); } } } } else { // client messages Client *client = Client::findClientFromWindow(ev->window); if (client) { static_cast(client->getParent())->handleClientMessage(ev, client); } } } void WindowManager::handleColormapEvent(XColormapEvent *ev) { Client *client = Client::findClient(ev->window); if (client) { client = static_cast(((Frame*) client->getParent())->getActiveChild()); client->handleColormapChange(ev); } } //! @brief Handles XPropertyEvents void WindowManager::handlePropertyEvent(XPropertyEvent *ev) { if (ev->window == _screen->getRoot()) { if (ev->atom == Atoms::getAtom(NET_DESKTOP_NAMES)) { _root_wo->readEwmhDesktopNames(); _workspaces->setNames(); } return; } Client *client = Client::findClientFromWindow(ev->window); if (client) { ((Frame*) client->getParent())->handlePropertyChange(ev, client); } } //! @brief Handles XMappingEvent void WindowManager::handleMappingEvent(XMappingEvent *ev) { if (ev->request == MappingKeyboard || ev->request == MappingModifier) { XRefreshKeyboardMapping(ev); _screen->setLockKeys(); InputDialog::reloadKeysymMap(); doReloadKeygrabber(true); } } void WindowManager::handleExposeEvent(XExposeEvent *ev) { ActionEvent *ae = 0; PWinObj *wo = PWinObj::findPWinObj(ev->window); if (wo) { ae = wo->handleExposeEvent(ev); } if (ae) { ActionPerformed ap(wo, *ae); ap.type = ev->type; ap.event.expose = ev; _action_handler->handleAction(ap); } } //! @brief Handle shape events applying shape to clients void WindowManager::handleShapeEvent(XAnyEvent *ev) { #ifdef HAVE_SHAPE Client *client = Client::findClient(ev->window); if (client && client->getParent()) { static_cast(client->getParent())->handleShapeEvent(ev); } #endif // HAVE_SHAPE } #ifdef HAVE_XRANDR //! @brief Handles XRandr events //! @param ev XRRNotifyEvent to handle. void WindowManager::handleXRandrEvent(XRRNotifyEvent *ev) { if (ev->subtype == RRNotify_CrtcChange) { handleXRandrCrtcChangeEvent(reinterpret_cast(ev)); } else { handleXRandrScreenChangeEvent(reinterpret_cast(ev)); } } /** * Handle screen change event. * * Reads the screen geometry and head information all over, updates * the screen edge and harbour. * * @param ev XRRScreenChangeNotifyEvent event to handle. */ void WindowManager::handleXRandrScreenChangeEvent(XRRScreenChangeNotifyEvent *ev) { #ifdef DEBUG cerr << __FILE__ << "@" << __LINE__ << ": WindowManager::handleXRandrScreenChangeEvent()" << endl; #endif // DEBUG _screen->updateGeometry(ev->width, ev->height); _harbour->updateGeometry(); screenEdgeResize(); // Make sure windows are visible after resize list::iterator it(PDecor::pdecor_begin()); for (; it != PDecor::pdecor_end(); ++it) { _workspaces->placeWoInsideScreen(*it); } } //! @brief Handle crtc change event, does nothing. //! @param ev XRRCrtcChangeNotifyEvent event to handle. void WindowManager::handleXRandrCrtcChangeEvent(XRRCrtcChangeNotifyEvent *ev) { #ifdef DEBUG cerr << __FILE__ << "@" << __LINE__ << ": WindowManager::handleXRandrCrtcChangeEvent()" << endl; #endif // DEBUG } #endif // HAVE_XRANDR // Event handling routines stop ============================================ //! @brief Searches for a PWinObj to focus, and then gives it input focus void WindowManager::findWOAndFocus(PWinObj *search) { PWinObj *focus = 0; if (PWinObj::windowObjectExists(search) && (search->isMapped()) && (search->isFocusable())) { focus = search; } // search window object didn't exist, go through the MRU list if (! focus) { list::reverse_iterator f_it = _mru_list.rbegin(); for (; ! focus && (f_it != _mru_list.rend()); ++f_it) { if ((*f_it)->isMapped() && (*f_it)->isFocusable()) { focus = *f_it; } } } if (focus) { focus->giveInputFocus(); } else if (! PWinObj::getFocusedPWinObj()) { _root_wo->giveInputFocus(); _root_wo->setEwmhActiveWindow(None); } } //! @brief Raises the client and all window having transient relationship with it //! @param client Client part of the famliy //! @param raise If true, raise frames, else lowers them void WindowManager::familyRaiseLower(Client *client, bool raise) { Client *parent; Window win_search; if (! client->getTransientClient()) { parent = client; win_search = client->getWindow(); } else { parent = client->getTransientClient(); win_search = client->getTransientClientWindow(); } list client_list; Client::findFamilyFromWindow(client_list, win_search); if (parent) { // make sure parent gets underneath the children if (raise) { client_list.push_front(parent); } else { client_list.push_back(parent); } } Frame *frame; list::iterator it(client_list.begin()); for (; it != client_list.end(); ++it) { frame = dynamic_cast((*it)->getParent()); if (frame) { if (raise) { frame->raise(); } else { frame->lower(); } } } } //! @brief Remove from MRU list. void WindowManager::removeFromFrameList(Frame *frame) { _mru_list.remove(frame); } /** * Match Frame against autoproperty. */ bool WindowManager::findGroupMatchProperty(Frame *frame, AutoProperty *property) { if ((property->group_global || (property->isMask(AP_WORKSPACE) ? (frame->getWorkspace() == property->workspace && ! frame->isIconified()) : frame->isMapped())) && (property->group_size == 0 || signed(frame->size()) < property->group_size) && (((frame->getClassHint()->group.size() > 0) ? (frame->getClassHint()->group == property->group_name) : false) || AutoProperties::matchAutoClass(*frame->getClassHint(), static_cast(property)))) { return true; } return false; } /** * Tries to find a Frame to autogroup with. This is called recursively * if workspace specific matching is on to avoid conflicts with the * global property. */ Frame* WindowManager::findGroup(AutoProperty *property) { if (! _allow_grouping) { return 0; } Frame *frame = 0; if (property->group_global && property->isMask(AP_WORKSPACE)) { bool group_global_orig = property->group_global; property->group_global= false; frame = findGroup(property); property->group_global= group_global_orig; } if (! frame) { frame = findGroupMatch(property); } return frame; } /** * Do matching against Frames searching for a suitable Frame for * grouping. */ Frame* WindowManager::findGroupMatch(AutoProperty *property) { Frame *frame = 0; // Matching against the focused window first if requested if (property->group_focused_first && PWinObj::isFocusedPWinObj(PWinObj::WO_CLIENT)) { Frame *fo_frame = static_cast(PWinObj::getFocusedPWinObj()->getParent()); if (findGroupMatchProperty(fo_frame, property)) { frame = fo_frame; } } // Moving on to the rest of the frames. if (! frame) { list::iterator it(Frame::frame_begin()); for (; it != Frame::frame_end(); ++it) { if (findGroupMatchProperty(*it, property)) { frame = *it; break; } } } return frame; } //! @brief Attaches all marked clients to frame void WindowManager::attachMarked(Frame *frame) { list::iterator it(Client::client_begin()); for (; it != Client::client_end(); ++it) { if ((*it)->isMarked()) { if ((*it)->getParent() != frame) { ((Frame*) (*it)->getParent())->removeChild(*it); (*it)->setWorkspace(frame->getWorkspace()); frame->addChild(*it); } (*it)->setStateMarked(STATE_UNSET); } } } //! @brief Attaches the Client/Frame into the Next/Prev Frame void WindowManager::attachInNextPrevFrame(Client *client, bool frame, bool next) { if (! client) return; Frame *new_frame; if (next) { new_frame = getNextFrame((Frame*) client->getParent(), true, SKIP_FOCUS_TOGGLE); } else { new_frame = getPrevFrame((Frame*) client->getParent(), true, SKIP_FOCUS_TOGGLE); } if (new_frame) { // we found a frame if (frame) { new_frame->addDecor((Frame*) client->getParent()); } else { client->getParent()->setFocused(false); ((Frame*) client->getParent())->removeChild(client); new_frame->addChild(client); new_frame->activateChild(client); new_frame->giveInputFocus(); } } } //! @brief Finds the next frame in the list //! //! @param frame Frame to search from //! @param mapped Match only agains mapped frames //! @param mask Defaults to 0 Frame* WindowManager::getNextFrame(Frame* frame, bool mapped, uint mask) { if (! frame || (Frame::frame_size() < 2)) { return 0; } Frame *next_frame = 0; list::iterator f_it(find(Frame::frame_begin(), Frame::frame_end(), frame)); if (f_it != Frame::frame_end()) { list::iterator n_it(f_it); if (++n_it == Frame::frame_end()) { n_it = Frame::frame_begin(); } while (! next_frame && (n_it != f_it)) { if (! (*n_it)->isSkip(mask) && (! mapped || (*n_it)->isMapped())) next_frame = (*n_it); if (++n_it == Frame::frame_end()) { n_it = Frame::frame_begin(); } } } return next_frame; } //! @brief Finds the previous frame in the list //! //! @param frame Frame to search from //! @param mapped Match only agains mapped frames //! @param mask Defaults to 0 Frame* WindowManager::getPrevFrame(Frame* frame, bool mapped, uint mask) { if (! frame || (Frame::frame_size() < 2)) { return 0; } Frame *next_frame = 0; list::iterator f_it(find(Frame::frame_begin(), Frame::frame_end(), frame)); if (f_it != Frame::frame_end()) { list::iterator n_it(f_it); if (n_it == Frame::frame_begin()) { n_it = --Frame::frame_end(); } else { --n_it; } while (! next_frame && (n_it != f_it)) { if (! (*n_it)->isSkip(mask) && (! mapped || (*n_it)->isMapped())) { next_frame = (*n_it); } if (n_it == Frame::frame_begin()) { n_it = --Frame::frame_end(); } else { --n_it; } } } return next_frame; }