mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
eed5749909
Also center ede-launch window.
1749 lines
48 KiB
C++
1749 lines
48 KiB
C++
//
|
|
// WindowManager.cc for pekwm
|
|
// Copyright © 2002-2009 Claes Nästén <me{@}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.
|
|
//
|
|
|
|
#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 <iostream>
|
|
#include <list>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <cassert>
|
|
|
|
extern "C" {
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.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::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<RootWO*>(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<Frame*>::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*> 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::client_begin(), Client::client_end());
|
|
list<Client*>::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<Window> win_list(wins, wins + num_wins);
|
|
XFree(wins);
|
|
|
|
list<Window>::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<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);
|
|
}
|
|
}
|
|
|
|
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<Client*>::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<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)
|
|
{
|
|
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<EdgeWO*>::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<EdgeWO*>::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<CfgParser::Entry*>(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<Client*>::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<Client*>::iterator it_c(Client::client_begin());
|
|
for (; it_c != Client::client_end(); ++it_c) {
|
|
(*it_c)->readAutoprops(APPLY_ON_RELOAD);
|
|
}
|
|
|
|
list<Frame*>::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<ActionPerformed>::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<ActionPerformed>::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<XRRNotifyEvent*>(&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<InputDialog*>(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<Frame*>(wo)->getTitleWindow())) {
|
|
wo = static_cast<Frame*>(wo)->getChildFromPos(ev->x);
|
|
} else {
|
|
wo = static_cast<Frame*>(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<Frame*>(wo)->getTitleWindow())) {
|
|
wo = static_cast<Frame*>(wo)->getChildFromPos(ev->x);
|
|
} else {
|
|
wo = static_cast<Frame*>(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<Frame*>(wo)->getActiveChild();
|
|
} else {
|
|
wo = static_cast<Frame*>(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<Frame*>(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<Frame*>(client->getParent())->handleClientMessage(ev, client);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
WindowManager::handleColormapEvent(XColormapEvent *ev)
|
|
{
|
|
Client *client = Client::findClient(ev->window);
|
|
if (client) {
|
|
client =
|
|
static_cast<Client*>(((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<Frame*>(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<XRRCrtcChangeNotifyEvent*>(ev));
|
|
} else {
|
|
handleXRandrScreenChangeEvent(reinterpret_cast<XRRScreenChangeNotifyEvent*>(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<PDecor*>::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<PWinObj*>::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*> 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<Client*>::iterator it(client_list.begin());
|
|
for (; it != client_list.end(); ++it) {
|
|
frame = dynamic_cast<Frame*>((*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*>(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<Frame*>(PWinObj::getFocusedPWinObj()->getParent());
|
|
if (findGroupMatchProperty(fo_frame, property)) {
|
|
frame = fo_frame;
|
|
}
|
|
}
|
|
|
|
// Moving on to the rest of the frames.
|
|
if (! frame) {
|
|
list<Frame*>::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<Client*>::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<Frame*>::iterator f_it(find(Frame::frame_begin(), Frame::frame_end(), frame));
|
|
|
|
if (f_it != Frame::frame_end()) {
|
|
list<Frame*>::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<Frame*>::iterator f_it(find(Frame::frame_begin(), Frame::frame_end(), frame));
|
|
|
|
if (f_it != Frame::frame_end()) {
|
|
list<Frame*>::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;
|
|
}
|