//
// WindowManager.cc for pekwm
// Copyright (C) 2002-2005 Claes Nasten <pekdon{@}pekdon{.}net>
//
// windowmanager.cc for aewm++
// Copyright (C) 2000 Frank Hale <frankhale@yahoo.com>
// http://sapphire.sourceforge.net/
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#include "../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 "Viewport.hh"
#include "Workspaces.hh"
#include "Util.hh"
#include "RegexString.hh"
#include "KeyGrabber.hh"
#ifdef HARBOUR
#include "Harbour.hh"
#include "DockApp.hh"
#endif // HARBOUR
#include "CmdDialog.hh"
#include "StatusWindow.hh"
#include "PMenu.hh" // we need this for the "Alt+Tabbing"
#ifdef MENUS
#include "WORefMenu.hh"
#include "ActionMenu.hh"
#include "FrameListMenu.hh"
#include "DecorMenu.hh"
#ifdef HARBOUR
#include "HarbourMenu.hh"
#endif // HARBOUR
#endif // MENUS
#include <iostream>
#include <list>
#include <algorithm>
#include <functional>
#include <memory>
extern "C" {
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
#endif // HAVE_XRANDR
}
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::list;
using std::vector;
using std::map;
using std::mem_fun;
using std::find;
const string WindowManager::_wm_name = string("pekwm");
WindowManager *wm;
//! @brief
void
sigHandler(int signal)
{
switch (signal) {
case SIGHUP:
wm->reload();
break;
case SIGINT:
case SIGTERM:
wm->shutdown();
break;
case SIGCHLD:
wait(NULL);
break;
}
}
//! @brief
int
handleXError(Display *dpy, XErrorEvent *e)
{
if ((e->error_code == BadAccess) &&
(e->resourceid == (RootWindow(dpy, DefaultScreen(dpy))))) {
cerr << "pekwm: root window unavailable, can't start!" << endl;
exit(1);
}
#ifdef DEBUG
else {
char error_buf[256];
XGetErrorText(dpy, e->error_code, error_buf, 256);
cerr << "XError: " << error_buf << " id: " << e->resourceid << endl;
}
#endif // DEBUG
return 0;
}
// WindowManager::EdgeWO
//! @brief EdgeWO constructor
WindowManager::EdgeWO::EdgeWO(Display *dpy, Window root, EdgeType edge) :
PWinObj(dpy),
_edge(edge)
{
_type = WO_SCREEN_EDGE;
_layer = LAYER_NONE; // hack, goes over LAYER_MENU
_sticky = true; // don't map/unmap
_iconified = true; // hack, to be ignored when placing
_focusable = false; // focusing input only windows crashes X
XSetWindowAttributes sattr;
sattr.override_redirect = True;
sattr.event_mask =
EnterWindowMask|LeaveWindowMask|ButtonPressMask|ButtonReleaseMask;
_window =
XCreateWindow(_dpy, root,
0, 0, 1, 1, 0,
CopyFromParent, InputOnly, CopyFromParent,
CWOverrideRedirect|CWEventMask, &sattr);
_wo_list.push_back(this);
}
//! @brief EdgeWO destructor
WindowManager::EdgeWO::~EdgeWO(void)
{
_wo_list.remove(this);
XDestroyWindow(_dpy, _window);
}
//! @brief
void
WindowManager::EdgeWO::mapWindow(void)
{
if (_mapped)
return;
PWinObj::mapWindow();
_iconified = true;
}
//! @brief
ActionEvent*
WindowManager::EdgeWO::handleEnterEvent(XCrossingEvent *ev)
{
return ActionHandler::findMouseAction(BUTTON_ANY, ev->state,
MOUSE_EVENT_ENTER,
Config::instance()->getEdgeListFromPosition(_edge));
}
//! @brief
ActionEvent*
WindowManager::EdgeWO::handleButtonPress(XButtonEvent *ev)
{
return ActionHandler::findMouseAction(ev->button, ev->state,
MOUSE_EVENT_PRESS,
Config::instance()->getEdgeListFromPosition(_edge));
}
//! @brief
ActionEvent*
WindowManager::EdgeWO::handleButtonRelease(XButtonEvent *ev)
{
MouseEventType mb = MOUSE_EVENT_RELEASE;
// first we check if it's a double click
if (PScreen::instance()->isDoubleClick(ev->window, ev->button - 1, ev->time,
Config::instance()->getDoubleClickTime())) {
PScreen::instance()->setLastClickID(ev->window);
PScreen::instance()->setLastClickTime(ev->button - 1, 0);
mb = MOUSE_EVENT_DOUBLE;
} else {
PScreen::instance()->setLastClickID(ev->window);
PScreen::instance()->setLastClickTime(ev->button - 1, ev->time);
}
return ActionHandler::findMouseAction(ev->button, ev->state, mb,
Config::instance()->getEdgeListFromPosition(_edge));
}
// WindowManager::RootWO
//! @brief RootWO constructor
WindowManager::RootWO::RootWO(Display *dpy, Window root) :
PWinObj(dpy)
{
_type = WO_SCREEN_ROOT;
_layer = LAYER_NONE;
_mapped = true;
_window = root;
_gm.width = PScreen::instance()->getWidth();
_gm.height = PScreen::instance()->getHeight();
_wo_list.push_back(this);
}
//! @brief RootWO destructor
WindowManager::RootWO::~RootWO(void)
{
_wo_list.remove(this);
}
//! @brief
ActionEvent*
WindowManager::RootWO::handleButtonPress(XButtonEvent *ev)
{
return ActionHandler::findMouseAction(ev->button, ev->state,
MOUSE_EVENT_PRESS,
Config::instance()->getMouseActionList(MOUSE_ACTION_LIST_ROOT));
}
//! @brief
ActionEvent*
WindowManager::RootWO::handleButtonRelease(XButtonEvent *ev)
{
MouseEventType mb = MOUSE_EVENT_RELEASE;
// first we check if it's a double click
if (PScreen::instance()->isDoubleClick(ev->window, ev->button - 1, ev->time,
Config::instance()->getDoubleClickTime())) {
PScreen::instance()->setLastClickID(ev->window);
PScreen::instance()->setLastClickTime(ev->button - 1, 0);
mb = MOUSE_EVENT_DOUBLE;
} else {
PScreen::instance()->setLastClickID(ev->window);
PScreen::instance()->setLastClickTime(ev->button - 1, ev->time);
}
return ActionHandler::findMouseAction(ev->button, ev->state, mb,
Config::instance()->getMouseActionList(MOUSE_ACTION_LIST_ROOT));
}
//! @brief
ActionEvent*
WindowManager::RootWO::handleMotionEvent(XMotionEvent *ev)
{
uint button = PScreen::instance()->getButtonFromState(ev->state);
return ActionHandler::findMouseAction(button, ev->state,
MOUSE_EVENT_MOTION,
Config::instance()->getMouseActionList(MOUSE_ACTION_LIST_ROOT));
}
//! @brief
ActionEvent*
WindowManager::RootWO::handleEnterEvent(XCrossingEvent *ev)
{
return ActionHandler::findMouseAction(BUTTON_ANY, ev->state,
MOUSE_EVENT_ENTER,
Config::instance()->getMouseActionList(MOUSE_ACTION_LIST_ROOT));
}
//! @brief
ActionEvent*
WindowManager::RootWO::handleLeaveEvent(XCrossingEvent *ev)
{
return ActionHandler::findMouseAction(BUTTON_ANY, ev->state,
MOUSE_EVENT_LEAVE,
Config::instance()->getMouseActionList(MOUSE_ACTION_LIST_ROOT));
}
// WindowManager
//! @brief Constructor for WindowManager class
WindowManager::WindowManager(const std::string &command_line, const std::string &config_file) :
_screen(NULL), _screen_resources(NULL),
_keygrabber(NULL),
_config(NULL), _color_handler(NULL),
_font_handler(NULL), _texture_handler(NULL),
_theme(NULL), _action_handler(NULL),
_autoproperties(NULL), _workspaces(NULL),
#ifdef HARBOUR
_harbour(NULL),
#endif // HARBOUR
_cmd_dialog(NULL), _status_window(NULL),
_command_line(command_line),
_startup(false), _shutdown(false),
_tagged_frame(NULL), _tag_activate(true), _allow_grouping(true),
_root_wo(NULL),
_pekwm_atoms(NULL), _icccm_atoms(NULL), _ewmh_atoms(NULL)
{
::wm = this;
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, NULL);
sigaction(SIGINT, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGCHLD, &act, NULL);
// construct
_config = new Config();
_config->load(config_file);
setupDisplay();
scanWindows();
setupFrameIds();
// add all frames to the MRU list
_mru_list.resize (_frame_list.size ());
copy (_frame_list.begin (), _frame_list.end (), _mru_list.begin ());
// make sure windows are inside the virtual viewports
for (uint i = 0; i < _workspaces->size(); ++i) {
_workspaces->getViewport(i)->makeAllInsideVirtual();
}
execStartFile();
doEventLoop();
}
//! @brief WindowManager destructor
WindowManager::~WindowManager(void)
{
cleanup();
if (_root_wo != NULL)
delete _root_wo;
#ifdef HARBOUR
if (_harbour) {
delete _harbour;
}
#endif // HARBOUR
if (_cmd_dialog)
delete _cmd_dialog;
if (_status_window)
delete _status_window;
if (_action_handler)
delete _action_handler;
if (_autoproperties)
delete _autoproperties;
#ifdef MENUS
map<MenuType, PMenu*>::iterator it(_menus.begin());
for (; it != _menus.end(); ++it)
delete it->second;
_menus.clear();
#endif // MENUS
if (_pekwm_atoms != NULL)
delete _pekwm_atoms;
if (_icccm_atoms != NULL)
delete _icccm_atoms;
if (_ewmh_atoms != NULL)
delete _ewmh_atoms;
if (_keygrabber != NULL)
delete _keygrabber;
if (_workspaces != NULL)
delete _workspaces;
if (_config != NULL)
delete _config;
if (_theme != NULL)
delete _theme;
if (_color_handler != NULL)
delete _color_handler;
if (_font_handler != NULL)
delete _font_handler;
if (_texture_handler != NULL)
delete _texture_handler;
if (_screen_resources != NULL)
delete _screen_resources;
if (_screen != NULL) {
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<Frame*>::iterator it_f(_frame_list.begin());
for (; it_f != _frame_list.end(); ++it_f) {
(*it_f)->updateInactiveChildInfo();
}
// make sure windows are inside the real screen
for (uint i = 0; i < _workspaces->size(); ++i) {
_workspaces->getViewport(i)->makeAllInsideReal();
}
// remove all dockapps
#ifdef HARBOUR
_harbour->removeAllDockApps();
#endif // HARBOUR
// 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.
list<Frame*> frame_list;
list<PWinObj*>::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<Frame*>(*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*> client_list(_client_list);
list<Client*>::iterator it_c(client_list.begin());
for (; it_c != client_list.end(); ++it_c)
delete *it_c;
#ifdef DEBUG
if (_frame_list.size()) {
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "WindowManager::cleanup()" << endl
<< " *** _frame_list.size() > 0: " << _frame_list.size() << endl;
}
if (_client_list.size()) {
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "WindowManager::cleanup()" << endl
<< " *** _client_list.size() > 0: " << _client_list.size() << endl;
}
#endif // DEBUG
_keygrabber->ungrabKeys(_screen->getRoot());
// destroy screen edge
screenEdgeDestroy();
// destroy atom windows
XDestroyWindow(_screen->getDpy(), _extended_hints_win);
XInstallColormap(_screen->getDpy(), _screen->getColormap());
XSetInputFocus(_screen->getDpy(), PointerRoot,
RevertToPointerRoot, CurrentTime);
}
//! @brief
void
WindowManager::setupDisplay(void)
{
Display *dpy = XOpenDisplay(NULL);
if (!dpy) {
cerr << "Can not open display!" << endl
<< "Your DISPLAY variable currently is set to: "
<< getenv("DISPLAY") << endl;
exit(1);
}
XSetErrorHandler(handleXError);
_screen = new PScreen(dpy);
_color_handler = new ColorHandler(dpy);
_font_handler = new FontHandler();
_texture_handler = new TextureHandler();
_action_handler = new ActionHandler(this);
// setup the font trimming
PFont::setTrimString(_config->getTrimTitle());
// load colors, fonts
_screen_resources = new ScreenResources();
_theme = new Theme(_screen);
_autoproperties = new AutoProperties();
_autoproperties->load();
_pekwm_atoms = new PekwmAtoms();
_icccm_atoms = new IcccmAtoms();
_ewmh_atoms = new EwmhAtoms();
_workspaces = new Workspaces(_config->getWorkspaces());
// set initial values of hints
initHints();
#ifdef HARBOUR
_harbour = new Harbour(_screen, _theme, _workspaces);
#endif // HARBOUR
_cmd_dialog = new CmdDialog(_screen->getDpy(), _theme, "CmdDialog");
_status_window = new StatusWindow(_screen->getDpy(), _theme);
XDefineCursor(dpy, _screen->getRoot(),
_screen_resources->getCursor(ScreenResources::CURSOR_ARROW));
// select root window events
XSetWindowAttributes sattr;
sattr.event_mask =
SubstructureRedirectMask|SubstructureNotifyMask|
ColormapChangeMask|FocusChangeMask|
EnterWindowMask|
PropertyChangeMask|
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
XChangeWindowAttributes(dpy, _screen->getRoot(), CWEventMask, &sattr);
#ifdef HAVE_XRANDR
#ifdef X_RRScreenChangeSelectInput
XRRScreenChangeSelectInput(dpy, _screen->getRoot(), True);
#else //! X_RRScreenChangeSelectInput
XRRSelectInput(dpy, _screen->getRoot(), RRScreenChangeNotifyMask);
#endif // X_RRScreenChangeSelectInput
#endif // HAVE_XRANDR
_keygrabber = new KeyGrabber(_screen);
_keygrabber->load(_config->getKeyFile());
_keygrabber->grabKeys(_screen->getRoot());
#ifdef MENUS
_menus[WINDOWMENU_TYPE] = new ActionMenu(this, WINDOWMENU_TYPE, "");
_menus[ROOTMENU_TYPE] = new ActionMenu(this, ROOTMENU_TYPE, "");
_menus[GOTOMENU_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, GOTOMENU_TYPE);
_menus[GOTOCLIENTMENU_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, GOTOCLIENTMENU_TYPE);
_menus[ICONMENU_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, ICONMENU_TYPE);
_menus[ATTACH_CLIENT_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, ATTACH_CLIENT_TYPE);
_menus[ATTACH_FRAME_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, ATTACH_FRAME_TYPE);
_menus[ATTACH_CLIENT_IN_FRAME_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, ATTACH_CLIENT_IN_FRAME_TYPE);
_menus[ATTACH_FRAME_IN_FRAME_TYPE] =
new FrameListMenu(_screen, _theme, _frame_list, ATTACH_FRAME_IN_FRAME_TYPE);
_menus[DECORMENU_TYPE] = new DecorMenu(_screen, _theme, _action_handler);
_menus[WINDOWMENU_TYPE]->reload();
_menus[ROOTMENU_TYPE]->reload();
_menus[DECORMENU_TYPE]->reload();
#endif // MENUS
// Create screen edge windows
screenEdgeCreate();
screenEdgeMapUnmap();
// Create root PWinObj
_root_wo = new RootWO(dpy, _screen->getRoot());
PWinObj::setRootPWinObj(_root_wo);
}
//! @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<Window> win_list(wins, wins + num_wins);
XFree(wins);
list<Window>::iterator it(win_list.begin());
#ifdef HARBOUR
// If we have the Harbour on, we filter out all windows with the
// the IconWindowHint set not pointing to themself, 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<Window>::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);
}
}
#endif // HARBOUR
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)) {
#ifdef HARBOUR
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(this, *it);
if (!client->isAlive())
delete client;
}
XFree(wm_hints);
} else
#endif // HARBOUR
{
client = new Client(this, *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 != NULL) && wo->isMapped()) {
wo->giveInputFocus();
} else {
_root_wo->giveInputFocus();
}
// 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;
}
_screen_edge_list.push_back(new EdgeWO(_screen->getDpy(), _screen->getRoot(), SCREEN_EDGE_LEFT));
_screen_edge_list.push_back(new EdgeWO(_screen->getDpy(), _screen->getRoot(), SCREEN_EDGE_RIGHT));
_screen_edge_list.push_back(new EdgeWO(_screen->getDpy(), _screen->getRoot(), SCREEN_EDGE_TOP));
_screen_edge_list.push_back(new EdgeWO(_screen->getDpy(), _screen->getRoot(), SCREEN_EDGE_BOTTOM));
// make sure the edge stays ontop
list<EdgeWO*>::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<EdgeWO*>::iterator it(_screen_edge_list.begin());
for (; it != _screen_edge_list.end(); ++it) {
_workspaces->remove(*it);
delete *it;
}
}
//! @brief
void
WindowManager::screenEdgeResize(void)
{
if (_screen_edge_list.size() != 4)
return;
uint size = _config->getScreenEdgeSize(); // convenience
if (size == 0)
size = 1;
list<EdgeWO*>::iterator it(_screen_edge_list.begin());
// left edge
(*it)->move(0, 0);
(*it)->resize(size, _screen->getHeight());
++it;
// right edge
(*it)->move(_screen->getWidth() - size, 0);
(*it)->resize(size, _screen->getHeight());
++it;
// top edge
(*it)->move(size, 0);
(*it)->resize(_screen->getWidth() - (size * 2), size);
++it;
// bottom edge
(*it)->move(size, _screen->getHeight() - size);
(*it)->resize(_screen->getWidth() - (size * 2), size);
}
void
WindowManager::screenEdgeMapUnmap(void)
{
if (_screen_edge_list.size() != 4) {
return;
}
list<EdgeWO*>::iterator it(_screen_edge_list.begin());
for (; it != _screen_edge_list.end(); ++it) {
if ((_config->getScreenEdgeSize() > 0) &&
(_config->getEdgeListFromPosition((*it)->getEdge())->size() > 0)) {
(*it)->mapWindow();
} else {
(*it)->unmapWindow();
}
}
}
//! @brief Reloads configuration and updates states.
void
WindowManager::doReload(void)
{
// unique client names state
bool old_client_unique_name = _config->getClientUniqueName();
string old_client_unique_name_pre =
_config->getClientUniqueNamePre();
string old_client_unique_name_post =
_config->getClientUniqueNamePost();
#ifdef HARBOUR
// I do not want to restack or rearrange if nothing has changed.
uint old_harbour_placement = _config->getHarbourPlacement();
bool old_harbour_stacking = _config->isHarbourOntop();
#ifdef HAVE_XINERAMA
int old_harbour_head_nr = _config->getHarbourHead();
#endif // HAVE_XINERAMA
#endif // HARBOUR
_config->load(_config->getConfigFile()); // reload the config
_autoproperties->load(); // reload autoprops config
// update what might have changed in the cfg toouching the hints
_workspaces->setSize(_config->getWorkspaces());
// 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_list.begin(), _client_list.end(),
mem_fun(&Client::getXClientName));
}
// reload the theme
_theme->load(_config->getThemeFile());
// resize the viewports
for (uint i = 0; i < _workspaces->size(); ++i) {
_workspaces->getViewport(i)->reload();
}
// resize the screen edge
screenEdgeResize();
screenEdgeMapUnmap();
// regrab the buttons on the client windows
for_each(_client_list.begin(), _client_list.end(),
mem_fun(&Client::grabButtons));
// reload the keygrabber
_keygrabber->load(_config->getKeyFile());
_keygrabber->ungrabKeys(_screen->getRoot());
_keygrabber->grabKeys(_screen->getRoot());
// regrab keys
list<Client*>::iterator c_it(_client_list.begin());
for (; c_it != _client_list.end(); ++c_it) {
_keygrabber->ungrabKeys((*c_it)->getWindow());
_keygrabber->grabKeys((*c_it)->getWindow());
}
// update the status window and cmd dialog theme
_cmd_dialog->loadDecor();
_status_window->loadDecor();
// reload the themes on the frames
for_each(_frame_list.begin(), _frame_list.end(),
mem_fun(&PDecor::loadDecor));
// 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<Frame*>::iterator f_it(_frame_list.begin());
for (; f_it != _frame_list.end(); ++f_it) {
(*f_it)->readAutoprops();
}
#ifdef HARBOUR
_harbour->loadTheme();
if (old_harbour_placement != _config->getHarbourPlacement())
_harbour->rearrange();
if (old_harbour_stacking != _config->isHarbourOntop())
_harbour->restack();
#ifdef HAVE_XINERAMA
if (old_harbour_head_nr != _config->getHarbourHead())
_harbour->rearrange();
#endif // HAVE_XINERAMA
_harbour->updateHarbourSize();
#endif // HARBOUR
#ifdef MENUS
map<MenuType, PMenu*>::iterator pm_it(_menus.begin());
for (; pm_it != _menus.end(); ++pm_it) {
pm_it->second->loadDecor();
pm_it->second->reload();
}
#ifdef HARBOUR
_harbour->getHarbourMenu()->loadDecor();
_harbour->getHarbourMenu()->reload();
#endif // HARBOUR
#endif // MENUS
_screen->updateStrut();
_reload = false;
}
//! @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)
{
Display *dpy = _screen->getDpy();
#ifdef HAVE_SHAPE
Client *client = NULL; // used for shape events
#endif // HAVE_SHAPE
XEvent ev;
while (!_shutdown) {
if (_reload)
doReload();
XNextEvent(dpy, &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:
handlePropertyEvent(&ev.xproperty);
break;
case Expose:
handleExposeEvent(&ev.xexpose);
break;
case KeyPress:
case KeyRelease:
handleKeyEvent(&ev.xkey);
break;
case ButtonPress:
handleButtonPressEvent(&ev.xbutton);
break;
case ButtonRelease:
handleButtonReleaseEvent(&ev.xbutton);
break;
case MotionNotify:
handleMotionEvent(&ev.xmotion);
break;
case EnterNotify:
handleEnterNotify(&ev.xcrossing);
break;
case LeaveNotify:
handleLeaveNotify(&ev.xcrossing);
break;
case FocusIn:
handleFocusInEvent(&ev.xfocus);
break;
case FocusOut:
handleFocusOutEvent(&ev.xfocus);
break;
default:
#ifdef HAVE_SHAPE
if (_screen->hasExtensionShape() &&
(ev.type == _screen->getEventShape())) {
client = findClient(ev.xany.window);
if ((client != NULL) && (client->getParent() != NULL)) {
static_cast<Frame*>(client->getParent())->handleShapeEvent(&ev.xany);
}
}
#endif // HAVE_SHAPE
#ifdef HAVE_XRANDR
if (_screen->hasExtensionXRandr() &&
(ev.type == _screen->getEventXRandr())) {
handleXRandrEvent((XRRScreenChangeNotifyEvent*) &ev);
}
#endif // HAVE_XRANDR
break;
}
}
}
//! @brief Handle XKeyEvents
void
WindowManager::handleKeyEvent(XKeyEvent *ev)
{
ActionEvent *ae = NULL;
PWinObj *wo = PWinObj::getFocusedPWinObj();
PWinObj::Type type = (wo == NULL) ? PWinObj::WO_SCREEN_ROOT : wo->getType();
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:
if (ev->type == KeyPress) {
ae = wo->handleKeyPress(ev);
} else {
ae = wo->handleKeyRelease(ev);
}
wo = static_cast<CmdDialog*>(wo)->getWORef();
break;
default:
if (wo != NULL) {
if (ev->type == KeyPress) {
ae = wo->handleKeyPress(ev);
} else {
ae = wo->handleKeyRelease(ev);
}
}
break;
}
if (ae != NULL) {
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));
}
//! @brief
void
WindowManager::handleButtonPressEvent(XButtonEvent *ev)
{
// Clear event queue
while (XCheckTypedEvent(_screen->getDpy(), ButtonPress, (XEvent *) ev));
ActionEvent *ae = NULL;
PWinObj *wo = NULL;
wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
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<Frame*>(wo)->getTitleWindow())) {
wo = static_cast<Frame*>(wo)->getChildFromPos(ev->x);
} else {
wo = static_cast<Frame*>(wo)->getActiveChild();
}
}
}
if (ae != NULL) {
ActionPerformed ap(wo, *ae);
ap.type = ev->type;
ap.event.button = ev;
_action_handler->handleAction(ap);
}
#ifdef HARBOUR
else {
DockApp *da = _harbour->findDockAppFromFrame(ev->window);
if (da) {
_harbour->handleButtonEvent(ev, da);
}
}
#endif // HARBOUR
}
//! @brief
void
WindowManager::handleButtonReleaseEvent(XButtonEvent *ev)
{
// Flush ButtonReleases
while (XCheckTypedEvent(_screen->getDpy(), ButtonRelease, (XEvent *) ev));
ActionEvent *ae = NULL;
PWinObj *wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
ae = wo->handleButtonRelease(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<Frame*>(wo)->getTitleWindow())) {
wo = static_cast<Frame*>(wo)->getChildFromPos(ev->x);
} else {
wo = static_cast<Frame*>(wo)->getActiveChild();
}
}
if (ae != NULL) {
ActionPerformed ap(wo, *ae);
ap.type = ev->type;
ap.event.button = ev;
_action_handler->handleAction(ap);
}
}
#ifdef HARBOUR
else {
DockApp *da = _harbour->findDockAppFromFrame(ev->window);
if (da) {
_harbour->handleButtonEvent(ev, da);
}
}
#endif // HARBOUR
}
void
WindowManager::handleConfigureRequestEvent(XConfigureRequestEvent *ev)
{
Client *client = findClientFromWindow(ev->window);
if (client) {
((Frame*) client->getParent())->handleConfigureRequest(ev, client);
} else {
#ifdef HARBOUR
DockApp *da = _harbour->findDockApp(ev->window);
if (da) {
_harbour->handleConfigureRequestEvent(ev, da);
} else
#endif // HARBOUR
{
// 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);
}
}
}
//! @brief
void
WindowManager::handleMotionEvent(XMotionEvent *ev)
{
ActionEvent *ae = NULL;
PWinObj *wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
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<Frame*>(wo)->getActiveChild();
} else {
wo = static_cast<Frame*>(wo)->getChildFromPos(ev->x);
}
} else {
ae = wo->handleMotionEvent(ev);
}
if (ae != NULL) {
ActionPerformed ap(wo, *ae);
ap.type = ev->type;
ap.event.motion = ev;
_action_handler->handleAction(ap);
}
}
#ifdef HARBOUR
else {
DockApp *da = _harbour->findDockAppFromFrame(ev->window);
if (da) {
_harbour->handleMotionNotifyEvent(ev, da);
}
}
#endif // HARBOUR
}
//! @brief
void
WindowManager::handleMapRequestEvent(XMapRequestEvent *ev)
{
PWinObj *wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
wo->handleMapRequest(ev);
} else {
XWindowAttributes attr;
XGetWindowAttributes(_screen->getDpy(), ev->window, &attr);
if (!attr.override_redirect) {
// if we have the harbour enabled, we need to figure out wheter or
// not this is a dockapp.
#ifdef HARBOUR
XWMHints *wm_hints = XGetWMHints(_screen->getDpy(), ev->window);
if (wm_hints != NULL) {
if ((wm_hints->flags&StateHint) &&
(wm_hints->initial_state == WithdrawnState)) {
_harbour->addDockApp(new DockApp(_screen, _theme, ev->window));
} else {
Client *client = new Client(this, ev->window, true);
if (client->isAlive() == false) {
delete client;
}
}
XFree(wm_hints);
} else
#endif // HARBOUR
{
Client *client = new Client(this, ev->window, true);
if (client->isAlive() == false) {
delete client;
}
}
}
}
}
//! @brief
void
WindowManager::handleUnmapEvent(XUnmapEvent *ev)
{
PWinObj *wo = PWinObj::findPWinObj(ev->window);
PWinObj *wo_search = NULL;
PWinObj::Type wo_type = PWinObj::WO_NO_TYPE;
if (wo != NULL) {
wo_type = wo->getType();
if (wo_type == PWinObj::WO_CLIENT) {
wo_search = wo->getParent();
}
wo->handleUnmapEvent(ev);
if (wo == PWinObj::getFocusedPWinObj()) {
PWinObj::setFocusedPWinObj(NULL);
}
}
#ifdef HARBOUR
else {
DockApp *da = _harbour->findDockApp(ev->window);
if (da) {
if (ev->window == ev->event) {
_harbour->removeDockApp(da);
}
}
}
#endif // HARBOUR
if ((wo_type != PWinObj::WO_MENU) &&
(PWinObj::getFocusedPWinObj() == NULL)) {
findWOAndFocus(wo_search);
}
}
void
WindowManager::handleDestroyWindowEvent(XDestroyWindowEvent *ev)
{
Client *client = findClientFromWindow(ev->window);
if (client) {
PWinObj *wo_search = client->getParent();
client->handleDestroyEvent(ev);
if (PWinObj::getFocusedPWinObj() == NULL) {
findWOAndFocus(wo_search);
}
}
#ifdef HARBOUR
else {
DockApp *da = _harbour->findDockApp(ev->window);
if (da) {
da->setAlive(false);
_harbour->removeDockApp(da);
}
}
#endif // HARBOUR
}
void
WindowManager::handleEnterNotify(XCrossingEvent *ev)
{
// Clear event queue
while (XCheckTypedEvent(_screen->getDpy(), EnterNotify, (XEvent *) ev));
if ((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab)) {
return;
}
PWinObj *wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
if (wo->getType() == PWinObj::WO_CLIENT) {
wo = wo->getParent();
}
ActionEvent *ae = wo->handleEnterEvent(ev);
if (ae != NULL) {
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->mode == NotifyGrab) || (ev->mode == NotifyUngrab)) {
return;
}
PWinObj *wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
ActionEvent *ae = wo->handleLeaveEvent(ev);
if (ae != NULL) {
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)
{
// Get the last focus in event, no need to go through them all.
while (XCheckTypedEvent(_screen->getDpy(), FocusIn, (XEvent *) 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<Frame*>(wo)->getActiveChild();
if (!wo->isFocusable() || !wo->isMapped()) {
findWOAndFocus(NULL);
} 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));
while (XCheckTypedEvent(_screen->getDpy(), LeaveNotify, &e_flush));
PWinObj *focused_wo = PWinObj::getFocusedPWinObj(); // convenience
// unfocus last window
if (focused_wo != NULL) {
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);
setEwmhActiveWindow(wo->getWindow());
} else {
wo->setFocused(true);
setEwmhActiveWindow(None);
}
// update the MRU list
if (wo->getType() == PWinObj::WO_CLIENT) {
_mru_list.remove(wo->getParent());
_mru_list.push_back(wo->getParent());
}
}
}
}
//! @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));
if ((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab))
return;
// Match against focusd PWinObj if any, if matches we see if
// there are any FocusIn events in the queue, if not we give focus to
// the root window as we don't want to get left without focus.
if (PWinObj::getFocusedPWinObj()
&& (*PWinObj::getFocusedPWinObj() == ev->window)) {
if (XCheckTypedEvent(_screen->getDpy(), FocusIn, (XEvent *) ev)) {
// We found a Event, put it back and handle it later on.
XPutBackEvent(_screen->getDpy(), (XEvent *) ev);
} else {
// No event found, give the root PWinObj focus.
PWinObj::getRootPWinObj()->giveInputFocus();
}
}
}
void
WindowManager::handleClientMessageEvent(XClientMessageEvent *ev)
{
if (ev->window == _screen->getRoot()) {
// root window messages
if (ev->format == 32) {
if (ev->message_type == _ewmh_atoms->getAtom(NET_CURRENT_DESKTOP)) {
_workspaces->setWorkspace(ev->data.l[0], true);
} else if (ev->message_type ==
_ewmh_atoms->getAtom(NET_NUMBER_OF_DESKTOPS)) {
if (ev->data.l[0] > 0) {
_workspaces->setSize(ev->data.l[0]);
}
} else if (ev->message_type ==
_ewmh_atoms->getAtom(NET_DESKTOP_VIEWPORT)) {
_workspaces->getActiveViewport()->move(ev->data.l[0], ev->data.l[1]);
}
}
} else {
// client messages
Client *client = findClientFromWindow(ev->window);
if (client) {
static_cast<Frame*>(client->getParent())->handleClientMessage(ev, client);
}
}
}
void
WindowManager::handleColormapEvent(XColormapEvent *ev)
{
Client *client = findClient(ev->window);
if (client) {
client =
static_cast<Client*>(((Frame*) client->getParent())->getActiveChild());
client->handleColormapChange(ev);
}
}
void
WindowManager::handlePropertyEvent(XPropertyEvent *ev)
{
if (ev->window == _screen->getRoot())
return;
Client *client = findClientFromWindow(ev->window);
if (client != NULL) {
((Frame*) client->getParent())->handlePropertyChange(ev, client);
}
}
void
WindowManager::handleExposeEvent(XExposeEvent *ev)
{
ActionEvent *ae = NULL;
PWinObj *wo = PWinObj::findPWinObj(ev->window);
if (wo != NULL) {
ae = wo->handleExposeEvent(ev);
}
if (ae != NULL) {
ActionPerformed ap(wo, *ae);
ap.type = ev->type;
ap.event.expose = ev;
_action_handler->handleAction(ap);
}
}
#ifdef HAVE_XRANDR
//! @brief Handles XRandr event.
void
WindowManager::handleXRandrEvent(XRRScreenChangeNotifyEvent *ev)
{
// don't care about what it is, only update screen size
_screen->updateGeometry(ev->width, ev->height);
#ifdef HARBOUR
_harbour->updateGeometry();
#endif // HARBOUR
Viewport *vp;
for (uint i = 0; i < _workspaces->size(); ++i) {
if ((vp = _workspaces->getViewport(i)) != NULL) {
vp->updateGeometry();
}
}
screenEdgeResize();
}
#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 = NULL;
if ((PWinObj::windowObjectExists(search) == true) &&
(search->isMapped()) && (search->isFocusable()) &&
_workspaces->getActiveViewport()->isInside(search)) {
focus = search;
}
// search window object didn't exist, go through the MRU list
if (focus == NULL) {
list<PWinObj*>::reverse_iterator f_it = _mru_list.rbegin();
for (; (focus == NULL) && (f_it != _mru_list.rend()); ++f_it) {
if ((*f_it)->isMapped() && (*f_it)->isFocusable() &&
_workspaces->getActiveViewport()->isInside(*f_it)) {
focus = *f_it;
}
}
}
if (focus != NULL) {
focus->giveInputFocus();
} else if (PWinObj::getFocusedPWinObj() == NULL) {
_root_wo->giveInputFocus();
setEwmhActiveWindow(None);
}
}
//! @brief Finds the Client wich holds the window w, only matching the window.
//! @param win Window to use as criteria when searching
Client*
WindowManager::findClientFromWindow(Window win)
{
if (win == _screen->getRoot())
return NULL;
list<Client*>::iterator it(_client_list.begin());
for(; it != _client_list.end(); ++it) {
if (win == (*it)->getWindow())
return (*it);
}
return NULL;
}
//! @brief
Frame*
WindowManager::findFrameFromWindow(Window win)
{
if (win == _screen->getRoot())
return NULL;
list<Frame*>::iterator it(_frame_list.begin());
for(; it != _frame_list.end(); ++it) {
if (win == (*it)->getWindow()) { // operator == does more than that
return (*it);
}
}
return NULL;
}
//! @brief Finds the Client wich holds the Window w.
//! @param win Window to use as criteria when searching.
//! @return Pointer to the client if found, else NULL
Client*
WindowManager::findClient(Window win)
{
if ((win == None) || (win == _screen->getRoot()))
return NULL;
Frame *frame;
if (_client_list.size()) {
list<Client*>::iterator it(_client_list.begin());
for (; it != _client_list.end(); ++it) {
if (win == (*it)->getWindow()) {
return (*it);
} else {
frame = dynamic_cast<Frame*>((*it)->getParent());
if ((frame != NULL) && (*frame == win)) {
return (*it);
}
}
}
}
return NULL;
}
//! @brief Insert all clients with the transient for set to win
void
WindowManager::findFamily(std::list<Client*> &client_list, Window win)
{
// search for windows having transient for set to this window
list<Client*>::iterator it(_client_list.begin());
for (; it != _client_list.end(); ++it) {
if ((*it)->getTransientWindow() == win) {
client_list.push_back(*it);
}
}
}
//! @brief (Un)Maps all windows having transient_for set to win
void
WindowManager::findTransientsToMapOrUnmap(Window win, bool hide)
{
list<Client*> client_list;
findFamily(client_list, win);
list<Client*>::iterator it(client_list.begin());
for (; it != client_list.end(); ++it) {
if (static_cast<Frame*>((*it)->getParent())->getActiveChild() == *it) {
if (hide) {
(*it)->getParent()->iconify();
} else {
(*it)->getParent()->mapWindow();
}
}
}
}
//! @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->getTransientWindow() == None) {
parent = client;
win_search = client->getWindow();
} else {
parent = findClientFromWindow(client->getTransientWindow());
win_search = client->getTransientWindow();
}
list<Client*> client_list;
findFamily(client_list, win_search);
if (parent != NULL) { // make sure parent gets underneath the children
if (raise == true) {
client_list.push_front(parent);
} else {
client_list.push_back(parent);
}
}
Frame *frame;
list<Client*>::iterator it(client_list.begin());
for (; it != client_list.end(); ++it) {
frame = dynamic_cast<Frame*>((*it)->getParent());
if ((frame != NULL) && (frame->getActiveChild() == *it)) {
if (raise == true) {
frame->raise();
} else {
frame->lower();
}
}
}
}
//! @brief
void
WindowManager::removeFromClientList(Client *client)
{
_client_list.remove(client);
}
//! @brief
void
WindowManager::removeFromFrameList(Frame *frame)
{
if (_tagged_frame == frame) {
_tagged_frame = NULL;
}
_frame_list.remove(frame);
_mru_list.remove(frame);
_frameid_list.push_back(frame->getId());
}
//! @brief
Client*
WindowManager::findClient(const ClassHint *class_hint)
{
if (! _client_list.size())
return NULL;
list<Client*>::iterator it(_client_list.begin());
for (; it != _client_list.end(); ++it) {
if (*class_hint == *(*it)->getClassHint())
return *it;
}
return NULL;
}
//! @brief Tries to find a Frame to autogroup with
Frame*
WindowManager::findGroup(AutoProperty *property)
{
if (_allow_grouping == false) {
return NULL;
}
Frame *frame = NULL;
Viewport *vp = _workspaces->getActiveViewport();
#define MATCH_GROUP(F,P) \
((P->group_global || ((F)->isMapped() && vp->isInside(F))) && \
((P->group_size == 0) || (signed((F)->size()) < P->group_size)) && \
((((F)->getClassHint()->group.size() > 0) \
? ((F)->getClassHint()->group == P->group_name) : false) || \
AutoProperties::matchAutoClass(*(F)->getClassHint(), (Property*) P)))
// try to match the focused window first
if (property->group_focused_first &&
(PWinObj::getFocusedPWinObj() != NULL) &&
(PWinObj::getFocusedPWinObj()->getType() == PWinObj::WO_CLIENT)) {
Frame *fo_frame =
static_cast<Frame*>(PWinObj::getFocusedPWinObj()->getParent());
if (MATCH_GROUP(fo_frame, property)) {
frame = fo_frame;
}
}
// search the list of frames
if (frame == NULL) {
list<Frame*>::iterator it(_frame_list.begin());
for (; it != _frame_list.end(); ++it) {
if (MATCH_GROUP(*it, property)) {
frame = *it;
break;
}
}
}
#undef MATCH_GROUP
return frame;
}
//! @brief Sees if there is a tagged frame and if it's visible
Frame*
WindowManager::findTaggedFrame(bool &activate)
{
if (_tagged_frame && _tagged_frame->isMapped()) {
activate = _tag_activate;
return _tagged_frame;
}
return NULL;
}
//! @brief
Frame*
WindowManager::findFrameFromId(uint id)
{
list<Frame*>::iterator it(_frame_list.begin());
for (; it != _frame_list.end(); ++it) {
if ((*it)->getId() == id)
return (*it);
}
return NULL;
}
//! @brief
uint
WindowManager::findUniqueFrameId(void)
{
if (!_startup) // always return 0 while scanning windows
return 0;
uint id = 0;
if (_frameid_list.size()) {
id = _frameid_list.back();
_frameid_list.pop_back();
} else {
id = (_frame_list.size() + 1); // this is called before the frame is pushed
}
return id;
}
//! @brief
void
WindowManager::setupFrameIds(void)
{
list<Frame*>::iterator it(_frame_list.begin());
for (uint i = 1; it != _frame_list.end(); ++i, ++it)
(*it)->setId(i);
}
//! @brief
void
WindowManager::setTaggedFrame(Frame *frame, bool activate)
{
_tagged_frame = frame;
_tag_activate = activate;
}
//! @brief Attaches all marked clients to frame
void
WindowManager::attachMarked(Frame *frame)
{
list<Client*>::iterator it(_client_list.begin());
for (; it != _client_list.end(); ++it) {
if ((*it)->isMarked()) {
if ((*it)->getParent() != frame) {
((Frame*) (*it)->getParent())->removeChild(*it);
(*it)->setWorkspace(frame->getWorkspace());
frame->addChild(*it);
}
(*it)->mark();
}
}
}
//! @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_list.size() < 2))
return NULL;
Frame *next_frame = NULL;
list<Frame*>::iterator f_it(find(_frame_list.begin(), _frame_list.end(), frame));
if (f_it != _frame_list.end()) {
list<Frame*>::iterator n_it(f_it);
if (++n_it == _frame_list.end())
n_it = _frame_list.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_list.end())
n_it = _frame_list.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_list.size() < 2))
return NULL;
Frame *next_frame = NULL;
list<Frame*>::iterator f_it(find(_frame_list.begin(), _frame_list.end(), frame));
if (f_it != _frame_list.end()) {
list<Frame*>::iterator n_it(f_it);
if (n_it == _frame_list.begin())
n_it = --_frame_list.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_list.begin())
n_it = --_frame_list.end();
else
--n_it;
}
}
return next_frame;
}
#ifdef MENUS
//! @brief
void
WindowManager::hideAllMenus(void)
{
map<MenuType, PMenu*>::iterator it(_menus.begin());
for (; it != _menus.end(); ++it)
it->second->unmapAll();
}
#endif // MENUS
// here follows methods for hints and atoms
void
WindowManager::initHints(void)
{
// Motif hints
_atom_mwm_hints =
XInternAtom(_screen->getDpy(), "_MOTIF_WM_HINTS", False);
// Setup Extended Net WM hints
_extended_hints_win =
XCreateSimpleWindow(_screen->getDpy(), _screen->getRoot(),
-200, -200, 5, 5, 0, 0, 0);
XSetWindowAttributes pattr;
pattr.override_redirect = True;
XChangeWindowAttributes(_screen->getDpy(), _extended_hints_win,
CWOverrideRedirect, &pattr);
AtomUtil::setString(_extended_hints_win, _ewmh_atoms->getAtom(NET_WM_NAME),
_wm_name);
AtomUtil::setWindow(_extended_hints_win,
_ewmh_atoms->getAtom(NET_SUPPORTING_WM_CHECK),
_extended_hints_win);
AtomUtil::setWindow(_screen->getRoot(),
_ewmh_atoms->getAtom(NET_SUPPORTING_WM_CHECK),
_extended_hints_win);
setEwmhSupported();
AtomUtil::setLong(_screen->getRoot(),
_ewmh_atoms->getAtom(NET_NUMBER_OF_DESKTOPS),
_config->getWorkspaces());
_workspaces->getActiveViewport()->hintsUpdate();
AtomUtil::setLong(_screen->getRoot(),
_ewmh_atoms->getAtom(NET_CURRENT_DESKTOP), 0);
}
void
WindowManager::setEwmhSupported(void)
{
Atom *atoms = _ewmh_atoms->getAtomArray();
AtomUtil::setAtoms(_screen->getRoot(), _ewmh_atoms->getAtom(NET_SUPPORTED),
atoms, _ewmh_atoms->size());
delete [] atoms;
}
void
WindowManager::setEwmhActiveWindow(Window w)
{
AtomUtil::setWindow(_screen->getRoot(), _ewmh_atoms->getAtom(NET_ACTIVE_WINDOW), w);
}
syntax highlighted by Code2HTML, v. 0.9.1