//
// Client.cc for pekwm
// Copyright (C) 2002-2005 Claes Nasten <pekdon{@}pekdon{.}net>
//
// client.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" // PDecor::TitleItem
#include "Client.hh"
#include "PScreen.hh"
#include "Config.hh"
#include "KeyGrabber.hh"
#include "Theme.hh"
#include "AutoProperties.hh"
#include "Frame.hh"
#include "Workspaces.hh"
#include "Viewport.hh"
#include "WindowManager.hh"
#ifdef MENUS
#include "PMenu.hh"
#include "WORefMenu.hh"
#endif // MENUS
#include <cstdio>
extern "C" {
#include <X11/Xatom.h>
#ifdef HAVE_SHAPE
#include <X11/extensions/shape.h>
#endif // HAVE_SHAPE
}
#ifdef DEBUG
#include <iostream>
using std::cerr;
using std::endl;
#endif // DEBUG
using std::vector;
using std::list;
using std::find;
Client::Client(WindowManager *w, Window new_client, bool is_new) :
PWinObj(w->getScreen()->getDpy()),
_wm(w),
_size(NULL),
_transient(None), _strut(NULL),
_class_hint(NULL),
_alive(false), _marked(false),
_send_focus_message(false), _send_close_message(false),
_cfg_request_lock(false),
#ifdef HAVE_SHAPE
_shaped(false),
#endif // HAVE_SHAPE
_extended_net_name(false)
{
// Construct the client
PScreen::instance()->grabServer();
// PWinObj attributes
_window = new_client;
_type = WO_CLIENT;
if (!validate()) {
#ifdef DEBUG
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "Couldn't validate client." << endl;
#endif // DEBUG
PScreen::instance()->ungrabServer(true);
return;
}
XWindowAttributes attr;
if (!XGetWindowAttributes(_dpy, _window, &attr)) {
#ifdef DEBUG
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "Client died." << endl;
#endif // DEBUG
PScreen::instance()->ungrabServer(true);
return;
}
_gm.x = attr.x;
_gm.y = attr.y;
_gm.width = attr.width;
_gm.height = attr.height;
_cmap = attr.colormap;
_size = XAllocSizeHints();
// get trans window and the window attributes
getTransientForHint();
XSetWindowAttributes sattr;
sattr.event_mask =
PropertyChangeMask|StructureNotifyMask|FocusChangeMask;
sattr.do_not_propagate_mask =
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask;
// We don't want these masks to be propagated down to the frame
XChangeWindowAttributes(_dpy, _window, CWEventMask|CWDontPropagate, &sattr);
#ifdef HAVE_SHAPE
if (PScreen::instance()->hasExtensionShape()) {
XShapeSelectInput(_dpy, _window, ShapeNotifyMask);
}
#endif // HAVE_SHAPE
XAddToSaveSet(_dpy, _window);
XSetWindowBorderWidth(_dpy, _window, 0);
// Load the Class hint before loading the autoprops and
// getting client title as we search for TitleRule in the autoprops
_class_hint = new ClassHint();
readClassRoleHints();
getWMNormalHints();
getXClientName();
getXIconName();
// cyclic dependency, getting the name requires quiering autoprops
_class_hint->title = _title.getReal();
// Get Autoproperties before EWMH as we need the cfg_deny property.
AutoProperty *ap = readAutoprops(_wm->isStartup()
? APPLY_ON_NEW : APPLY_ON_START);
readMwmHints(); // read atoms
readEwmhHints();
readPekwmHints();
getWMProtocols();
XWMHints* hints = XGetWMHints(_dpy, _window);
if (hints != NULL) {
// get the input focus mode
if (hints->flags&InputHint) { // FIXME: More logic needed
// if (hints->input) {
// _focusable = true;
// } else {
// _focusable = false;
// }
}
// get initial state of the window
if (hints->flags&StateHint) {
setWmState(hints->initial_state);
} else {
setWmState(NormalState);
}
XFree(hints);
}
// are we iconified
if (getWmState() == IconicState) {
_iconified = true;
}
// grab keybindings and mousebutton actions
KeyGrabber::instance()->grabKeys(_window);
grabButtons();
// don't want to spread the giveInputFocuses
bool do_focus = is_new ? Config::instance()->isFocusNew() : false;
bool do_autogroup = true;
// If pekwm allready is running, check against autoproperty then
// tagged frame. If starting up, check if it has a FRAME_ID and if not
// use autoproperties.
if (_wm->isStartup()) {
// Check against tagged frame.
bool activate;
_parent = _wm->findTaggedFrame(activate);
if (_parent) {
Frame *frame = static_cast<Frame*>(_parent);
frame->addChild(this);
if (activate) {
frame->activateChild(this);
do_focus = frame->isFocused();
}
}
// Not startup, pekwm (re)start it is.
} else {
long id;
if (AtomUtil::getLong(_window,
PekwmAtoms::instance()->getAtom(PEKWM_FRAME_ID),
id)) {
do_autogroup = false;
_parent = _wm->findFrameFromId(id);
if (_parent) {
Frame *frame = static_cast<Frame*>(_parent);
frame->addChild(this);
frame->activateChild(this);
do_focus = frame->isFocused();
}
}
}
// Apply Autoproperties again to override EWMH state. It's done twice as
// we need the cfg_deny property when reading the EWMH state.
if (ap) {
applyAutoprops(ap);
if (do_autogroup && !_parent && (ap->group_size >= 0)) {
Frame* frame = _wm->findGroup(ap);
if (frame) {
frame->addChild(this);
if (ap->group_behind == false) {
frame->activateChild(this);
do_focus = frame->isFocused();
}
if (ap->group_raise) {
frame->raise();
}
}
}
}
// Tell the world about our state
updateEwmhStates();
PScreen::instance()->ungrabServer(true);
// if we don't have a frame allready, create a new one
if (_parent == NULL) {
_parent = new Frame(_wm, this, ap);
}
// make sure the window is mapped, this is done after it has been
// added to the decor/frame as otherwise IsViewable state won't
// be correct and we don't know wheter or not to place the window
PWinObj::mapWindow();
// Let us hear what autoproperties has to say about focusing
if (is_new && (ap != NULL) && ap->isMask(AP_FOCUS_NEW)) {
do_focus = ap->focus_new;
}
// Only can give input focus to mapped windows
if (_parent->isMapped()) {
// Ordinary focus
if (do_focus) {
_parent->giveInputFocus();
// Check if we are transient, and if we want to focus
} else if ((_transient != None)
&& Config::instance()->isFocusNewChild()) {
Client *trans_for = _wm->findClientFromWindow(_transient);
if (trans_for && trans_for->isFocused()) {
_parent->giveInputFocus();
}
}
}
// finished creating the client, so now adding it to the client list
_wm->addToClientList(this);
_alive = true;
_wo_list.push_back(this);
}
//! @brief Client destructor
Client::~Client(void)
{
// remove from lists
_wo_list.remove(this);
_wm->removeFromClientList(this);
PScreen::instance()->grabServer();
#ifdef MENUS
WORefMenu *wo_ref_menu;
wo_ref_menu = static_cast<WORefMenu*>(_wm->getMenu(WINDOWMENU_TYPE));
if (this == wo_ref_menu->getWORef()) {
wo_ref_menu->setWORef(NULL);
wo_ref_menu->unmapAll();
}
wo_ref_menu = static_cast<WORefMenu*>(_wm->getMenu(DECORMENU_TYPE));
if (this == wo_ref_menu->getWORef()) {
wo_ref_menu->setWORef(NULL);
wo_ref_menu->unmapAll();
}
#endif // MENUS
// removes gravity and moves it back to root if we are alive
bool focus = false;
if (_parent && (_parent->getType() == PWinObj::WO_FRAME)) {
focus = _parent->isFocused();
static_cast<PDecor*>(_parent)->removeChild(this);
}
// Focus the parent if we had focus before
if (focus && (_transient != None)) {
Client *trans_cli = _wm->findClientFromWindow(_transient);
if ((trans_cli != NULL) &&
(((Frame*) trans_cli->getParent())->getActiveChild() == trans_cli)) {
trans_cli->getParent()->giveInputFocus();
}
}
// Clean up if the client still is alive, it'll be dead all times
// except when we exit pekwm
if (_alive) {
XUngrabButton(_dpy, AnyButton, AnyModifier, _window);
KeyGrabber::instance()->ungrabKeys(_window);
XRemoveFromSaveSet(_dpy, _window);
PWinObj::mapWindow();
}
// free names and size hint
if (_size)
XFree(_size);
removeStrutHint();
if (_class_hint)
delete _class_hint;
PScreen::instance()->ungrabServer(true);
}
// START - PWinObj interface.
//! @brief Maps the window.
void
Client::mapWindow(void)
{
if (_mapped)
return;
if (_iconified) {
_iconified = false;
setWmState(NormalState);
updateEwmhStates();
}
if(!_transient) // unmap our transient windows if we have any
_wm->findTransientsToMapOrUnmap(_window, false);
XSelectInput(_dpy, _window, NoEventMask);
PWinObj::mapWindow();
XSelectInput(_dpy, _window,
PropertyChangeMask|StructureNotifyMask|FocusChangeMask);
}
//! @brief Sets the client to WithdrawnState and unmaps it.
void
Client::unmapWindow(void)
{
if (!_mapped)
return;
if (_iconified) { // set the state of the window
setWmState(IconicState);
updateEwmhStates();
}
XSelectInput(_dpy, _window, NoEventMask);
PWinObj::unmapWindow();
XSelectInput(_dpy, _window,
PropertyChangeMask|StructureNotifyMask|FocusChangeMask);
}
//! @brief Iconifies the client and adds it to the iconmenu
void
Client::iconify(void)
{
if (_iconified)
return;
_iconified = true;
if (!_transient)
_wm->findTransientsToMapOrUnmap(_window, true);
unmapWindow();
}
//! @brief Toggle client sticky state
void
Client::stick(void)
{
PWinObj::stick();
updateEwmhStates();
}
//! @brief Update the position variables.
void
Client::move(int x, int y, bool do_virtual)
{
bool request = ((_gm.x != x) || (_gm.y != y));
_gm.x = x;
_gm.y = y;
if (request)
configureRequestSend();
}
//! @brief
void
Client::moveVirtual(int x, int y)
{
AtomUtil::setPosition(_window,
PekwmAtoms::instance()->getAtom(PEKWM_FRAME_VPOS),
x, y);
}
//! @brief Resizes the client window to specified size.
void
Client::resize(uint width, uint height)
{
bool request = ((_gm.width != width) || (_gm.height != height));
PWinObj::resize(width, height);
if (request)
configureRequestSend();
}
//! @brief Sets the workspce and updates the _NET_WM_DESKTOP hint.
void
Client::setWorkspace(uint workspace)
{
if (workspace != NET_WM_STICKY_WINDOW) {
if (workspace >= Workspaces::instance()->size()) {
workspace = Workspaces::instance()->size() - 1;
}
_workspace = workspace;
if (_sticky) {
AtomUtil::setLong(_window, EwmhAtoms::instance()->getAtom(NET_WM_DESKTOP),
NET_WM_STICKY_WINDOW);
} else {
AtomUtil::setLong(_window, EwmhAtoms::instance()->getAtom(NET_WM_DESKTOP),
_workspace);
}
}
}
//! @brief Gives the Client input focus.
bool
Client::giveInputFocus(void)
{
return PWinObj::giveInputFocus();
}
//! @brief Reparents and sets _parent member, filtering unmap events
void
Client::reparent(PWinObj *parent, int x, int y)
{
XSelectInput(_dpy, _window, NoEventMask);
PWinObj::reparent(parent, x, y);
XSelectInput(_dpy, _window,
PropertyChangeMask|StructureNotifyMask|FocusChangeMask);
}
//! @brief
ActionEvent*
Client::handleUnmapEvent(XUnmapEvent *ev)
{
if ((ev->window != ev->event) && (ev->send_event != true)) {
return NULL;
}
#ifdef DEBUG
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "Client(" << this << ")::handleUnmapEvent(" << ev << ")" << endl
<< " *** unmapping client, window: " << _window << endl;
#endif // DEBUG
// FIXME: Listen mask should change as this doesn't work?
//_alive = false;
delete this;
return NULL;
}
// END - PWinObj interface.
//! @brief Checks if the window has any Destroy or Unmap notifys.
bool
Client::validate(void)
{
XSync(_dpy, false);
XEvent ev;
if (XCheckTypedWindowEvent(_dpy, _window, DestroyNotify, &ev) ||
XCheckTypedWindowEvent(_dpy, _window, UnmapNotify, &ev)) {
XPutBackEvent(_dpy, &ev);
return false;
}
return true;
}
//! @brief Checks if the window has attribute IsViewable set
bool
Client::isViewable(void)
{
XWindowAttributes attr;
XGetWindowAttributes(_dpy, _window, &attr);
return (attr.map_state == IsViewable);
}
//! @brief Grabs all the mouse button actions on the client.
void
Client::grabButtons(void)
{
// Make sure we don't have any buttons grabbed.
XUngrabButton(_dpy, AnyButton, AnyModifier, _window);
list<ActionEvent> *actions =
Config::instance()->getMouseActionList(MOUSE_ACTION_LIST_CHILD_FRAME);
list<ActionEvent>::iterator it(actions->begin());
for (; it != actions->end(); ++it) {
if ((it->type == MOUSE_EVENT_PRESS) || (it->type == MOUSE_EVENT_RELEASE)) {
// No need to grab mod less events, replied with the frame
if ((it->mod == 0) || (it->mod == MOD_ANY)) {
continue;
}
grabButton(it->sym, it->mod,
ButtonPressMask|ButtonReleaseMask,
_window, None);
} else if (it->type == MOUSE_EVENT_MOTION) {
// FIXME: Add support for MOD_ANY
grabButton(it->sym, it->mod,
ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
_window, None);
}
}
}
//! @brief Sets CfgDeny state on Client
void
Client::setStateCfgDeny(StateAction sa, uint deny)
{
if (ActionUtil::needToggle(sa, _state.cfg_deny&deny) == false) {
return;
}
if (_state.cfg_deny&deny) {
_state.cfg_deny &= ~deny;
} else {
_state.cfg_deny |= deny;
}
}
void
Client::readClassRoleHints(void)
{
// class hint
XClassHint class_hint;
if (XGetClassHint(_dpy, _window, &class_hint)) {
_class_hint->h_name = class_hint.res_name;
_class_hint->h_class = class_hint.res_class;
XFree(class_hint.res_name);
XFree(class_hint.res_class);
}
// wm window role
_class_hint->h_role = "";
AtomUtil::getString(_window, IcccmAtoms::instance()->getAtom(WM_WINDOW_ROLE),
_class_hint->h_role);
}
//! @brief Loads the Clients state from EWMH atoms.
void
Client::readEwmhHints(void)
{
EwmhAtoms *ewmh = EwmhAtoms::instance(); // convenience
// which workspace do we belong to?
long workspace = -1;
AtomUtil::getLong(_window, ewmh->getAtom(NET_WM_DESKTOP), workspace);
if (workspace < 0) {
_workspace = Workspaces::instance()->getActive();
AtomUtil::setLong(_window, ewmh->getAtom(NET_WM_DESKTOP), _workspace);
} else {
_workspace = workspace;
}
// try to figure out what kind of window we are and alter it acordingly
int items;
Atom *atoms = NULL;
bool found_window_type = false;
atoms = (Atom*)
AtomUtil::getEwmhPropData(_window, ewmh->getAtom(WINDOW_TYPE),
XA_ATOM, items);
if (atoms) {
for (int i = 0; i < items; ++i) {
found_window_type = true;
if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_DESKTOP)) {
// desktop windows, make it the same size as the screen and place
// it below all windows withouth any decorations, also make it sticky
_gm.x = _gm.y = 0;
_gm.width = PScreen::instance()->getWidth();
_gm.height = PScreen::instance()->getHeight();
setTitlebar(false);
setBorder(false);
_sticky = true;
_state.placed = true;
_state.skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP;
_layer = LAYER_DESKTOP;
break;
} else if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_DOCK)) {
setTitlebar(false);
setBorder(false);
_sticky = true;
_layer = LAYER_DOCK;
break;
} else if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_TOOLBAR)) {
setTitlebar(false);
setBorder(false);
_state.skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE;
break;
} else if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_MENU)) {
_layer = LAYER_MENU;
_state.skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP;
break;
// } else if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_UTILITY)) {
// }
// } else if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_DIALOG)) {
// }
} else if (atoms[i] == ewmh->getAtom(WINDOW_TYPE_SPLASH)) {
setTitlebar(false);
setBorder(false);
} else {
found_window_type = false;
}
}
XFree(atoms);
}
if (!found_window_type) {
Atom type = ewmh->getAtom(WINDOW_TYPE_NORMAL);
AtomUtil::setAtom(_window, ewmh->getAtom(WINDOW_TYPE), type);
}
// The _NET_WM_STATE overides the _NET_WM_TYPE
NetWMStates win_states;
if (getEwmhStates(win_states) == true) {
if (win_states.hidden) _iconified = true;
if (win_states.shaded) _state.shaded = true;
if (win_states.max_vert) _state.maximized_vert = true;
if (win_states.max_horz) _state.maximized_horz = true;
if (win_states.skip_taskbar) _state.skip_taskbar = true;
if (win_states.skip_pager) _state.skip_pager = true;
if (win_states.sticky) _sticky = true;
if (win_states.above) _layer = LAYER_BELOW;
if (win_states.below) _layer = LAYER_ABOVE_DOCK;
if (win_states.fullscreen) _state.fullscreen = true;
}
// check if we have a strut
getStrutHint();
}
//! @brief Loads the Clients state from MWM atoms.
void
Client::readMwmHints(void)
{
MwmHints *mwm_hints = getMwmHints(_window);
if (mwm_hints != NULL) {
if (mwm_hints->flags&MWM_HINTS_FUNCTIONS) {
if (mwm_hints->functions&MWM_FUNC_ALL != 0) {
if (mwm_hints->functions&MWM_FUNC_RESIZE == 0)
_actions.resize = false;
if (mwm_hints->functions&MWM_FUNC_MOVE == 0)
_actions.move = false;
if (mwm_hints->functions&MWM_FUNC_ICONIFY == 0)
_actions.minimize = false;
if (mwm_hints->functions&MWM_FUNC_CLOSE == 0)
_actions.close = false;
if (mwm_hints->functions&MWM_FUNC_MAXIMIZE == 0) {
_actions.maximize_vert = true;
_actions.maximize_horz = true;
}
}
}
if (mwm_hints->flags&MWM_HINTS_DECORATIONS) {
if (!mwm_hints->decorations&MWM_DECOR_ALL) {
setTitlebar(mwm_hints->decorations&MWM_DECOR_TITLE);
setBorder(mwm_hints->decorations&MWM_DECOR_BORDER);
}
}
XFree(mwm_hints);
}
}
//! @brief Reads non-standard pekwm hints
void
Client::readPekwmHints(void)
{
long value;
// virtual position overides real position
AtomUtil::getPosition(_window,
PekwmAtoms::instance()->getAtom(PEKWM_FRAME_VPOS),
_gm.x, _gm.y);
// get decor state
if (AtomUtil::getLong(_window,
PekwmAtoms::instance()->getAtom(PEKWM_FRAME_DECOR),
value) == true) {
_state.decor = value;
}
// get skip state
if (AtomUtil::getLong(_window,
PekwmAtoms::instance()->getAtom(PEKWM_FRAME_SKIP),
value) == true) {
_state.skip = value;
}
}
//! @brief Tries to find a AutoProp for the current client.
//! @param type Defaults to 0
AutoProperty*
Client::readAutoprops(uint type)
{
AutoProperty *data =
AutoProperties::instance()->findAutoProperty(_class_hint, _workspace, type);
if ((data == NULL) ||
(_transient
? !data->isApplyOn(APPLY_ON_TRANSIENT|APPLY_ON_TRANSIENT_ONLY)
: data->isApplyOn(APPLY_ON_TRANSIENT_ONLY))) {
return NULL;
}
applyAutoprops(data);
return data;
}
//! @brief Applies AutoPropery to this Client.
void
Client::applyAutoprops(AutoProperty *ap)
{
// Set the correct group of the window
_class_hint->group = ap->group_name;
// We only apply grouping if it's a new client or if we are restarting
// and have APPLY_ON_START set
if (ap->isMask(AP_STICKY))
_sticky = ap->sticky;
if (ap->isMask(AP_SHADED))
_state.shaded = ap->shaded;
if (ap->isMask(AP_MAXIMIZED_VERTICAL))
_state.maximized_vert = ap->maximized_vertical;
if (ap->isMask(AP_MAXIMIZED_HORIZONTAL))
_state.maximized_horz = ap->maximized_horizontal;
if (ap->isMask(AP_FULLSCREEN))
_state.fullscreen = ap->fullscreen;
if (ap->isMask(AP_ICONIFIED))
_iconified = ap->iconified;
if (ap->isMask(AP_TITLEBAR))
setTitlebar(ap->titlebar);
if (ap->isMask(AP_BORDER))
setBorder(ap->border);
if (ap->isMask(AP_LAYER) && (ap->layer <= LAYER_MENU))
_layer = ap->layer;
if (ap->isMask(AP_SKIP))
_state.skip = ap->skip;
if (ap->isMask(AP_FOCUSABLE))
_focusable = ap->focusable;
if (ap->isMask(AP_WORKSPACE)) {
_workspace = ap->workspace;
}
if (ap->isMask(AP_CFG_DENY)) {
_state.cfg_deny = ap->cfg_deny;
}
}
//! @brief Tries to get the NET_WM name, else fall back to XA_WM_NAME
void
Client::getXClientName(void)
{
// reset title data
_title.setReal("");
if (_title.isUserSet() == false) {
_title.setVisible("");
}
_title.setCount(0);
// read X11 property
XTextProperty text_property;
if ((XGetWMName(_dpy, _window, &text_property) == False) ||
(text_property.value == NULL) || (text_property.nitems == 0)){
return;
}
if (text_property.encoding == XA_STRING) {
_title.setReal((const char*) text_property.value);
} else {
char **list;
int num;
XmbTextPropertyToTextList(_dpy, &text_property, &list, &num);
if ((num > 0) && (*list != NULL)) {
_title.setReal(*list);
XFreeStringList(list);
}
}
XFree(text_property.value);
// mirror it on the visible
if (_title.isUserSet() == false) {
_title.setVisible(_title.getReal());
// apply title rules and find unique name, doesn't apply on user-set titles
titleApplyRule();
titleFindID();
}
}
//! @brief Searches for an TitleRule and if found, applies it
void
Client::titleApplyRule(void)
{
_class_hint->title = _title.getReal ();
TitleProperty *data =
AutoProperties::instance()->findTitleProperty (_class_hint);
_class_hint->title = "";
if (data)
_title.setRuleApplied (data->getTitleRule ().ed_s (_title.getWVisible ()));
else
_title.setRuleApplied (false);
}
//! @brief Searches for a unique ID within Clients having the same title
void
Client::titleFindID(void)
{
if (Config::instance()->getClientUniqueName() == false) {
return;
}
list<uint> ids_used;
list<Client*>::iterator it = _wm->client_begin();
for (; it != _wm->client_end(); ++it) {
if (*it != this) {
if ((*it)->getTitle()->getReal() == _title.getReal()) {
ids_used.push_back((*it)->getTitle()->getCount());
}
}
}
// more than one client having this name
if (ids_used.size() > 0) {
ids_used.sort();
list<uint>::iterator it( ids_used.begin());
for (uint i = 0; it != ids_used.end(); ++i, ++it) {
if (i < *it) {
_title.setCount(i);
break;
}
}
if (it == ids_used.end()) {
_title.setCount(ids_used.size());
}
}
// more than one client, add identifier
if (_title.getCount() > 0) {
char buf[8];
snprintf(buf, 8, "%i", _title.getCount());
_title.getWVisible().append(Config::instance()->getClientUniqueNamePre());
_title.getWVisible().append(buf);
_title.getWVisible().append(Config::instance()->getClientUniqueNamePost());
}
}
//! @brief Tries to get Client's icon name, and puts it in _icon_name.
void
Client::getXIconName(void)
{
_icon_name = "";
XTextProperty text_property;
if (!XGetWMIconName(_dpy, _window, &text_property) ||
!text_property.value || !text_property.nitems)
return;
if (text_property.encoding == XA_STRING) {
_icon_name = (const char*) text_property.value;
} else {
char **list;
int num;
XmbTextPropertyToTextList(_dpy, &text_property, &list, &num);
if (num && *list) {
_icon_name = *list;
XFreeStringList(list);
}
}
XFree (text_property.value);
}
//! @brief Sets the WM_STATE of the client to state
//! @param state State to set.
void
Client::setWmState(ulong state)
{
ulong data[2];
data[0] = state;
data[1] = None; // No Icon
XChangeProperty(_dpy, _window,
IcccmAtoms::instance()->getAtom(WM_STATE),
IcccmAtoms::instance()->getAtom(WM_STATE),
32, PropModeReplace, (uchar*) data, 2);
}
// If we can't find a _wm->wm_state we're going to have to assume
// Withdrawn. This is not exactly optimal, since we can't really
// distinguish between the case where no WM has run yet and when the
// state was explicitly removed (Clients are allowed to either set the
// atom to Withdrawn or just remove it... yuck.)
long
Client::getWmState(void)
{
Atom real_type;
int real_format;
long *data, state = WithdrawnState;
ulong items_read, items_left;
int status =
XGetWindowProperty(_dpy, _window, IcccmAtoms::instance()->getAtom(WM_STATE),
0L, 2L, False, IcccmAtoms::instance()->getAtom(WM_STATE),
&real_type, &real_format, &items_read, &items_left,
(uchar **) &data);
if ((status == Success) && items_read) {
state = *data;
XFree(data);
}
return state;
}
//! @brief Send XConfigureEvent, letting the client know about changes
void
Client::configureRequestSend(void)
{
if (_cfg_request_lock)
return;
XConfigureEvent e;
e.type = ConfigureNotify;
e.event = _window;
e.window = _window;
e.x = _gm.x;
e.y = _gm.y;
e.width = _gm.width;
e.height = _gm.height;
e.border_width = 0;
e.above = None;
e.override_redirect = False;
#ifdef DEBUG
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "Client(" << this << ")::sendConfigureRequest()" << endl
<< " *** x: " << e.x << " y: " << e.y
<< " width: " << e.width << " height: " << e.height << endl;
#endif // DEBUG
XSendEvent(_dpy, _window, false, StructureNotifyMask, (XEvent *) &e);
}
//! @brief Grabs a button on the window win
//! Grabs the button button, with the mod mod and mask mask on the window win
//! and cursor curs with "all" possible extra modifiers
void
Client::grabButton(int button, int mod, int mask, Window win, Cursor curs)
{
uint num_lock = PScreen::instance()->getNumLock();
uint scroll_lock = PScreen::instance()->getScrollLock();
XGrabButton(_dpy, button, mod,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
XGrabButton(_dpy, button, mod|LockMask,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
if (num_lock) {
XGrabButton(_dpy, button, mod|num_lock,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
XGrabButton(_dpy, button, mod|num_lock|LockMask,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
}
if (scroll_lock) {
XGrabButton(_dpy, button, mod|scroll_lock,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
XGrabButton(_dpy, button, mod|scroll_lock|LockMask,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
}
if (num_lock && scroll_lock) {
XGrabButton(_dpy, button, mod|num_lock|scroll_lock,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
XGrabButton(_dpy, button, mod|num_lock|scroll_lock|LockMask,
win, true, mask, GrabModeAsync, GrabModeAsync, None, curs);
}
}
//! @brief Toggles the clients always on top state
void
Client::alwaysOnTop(bool top)
{
_layer = top ? LAYER_ONTOP : LAYER_NORMAL;
updateEwmhStates();
}
//! @brief Toggles the clients always below state.
void
Client::alwaysBelow(bool below)
{
_layer = below ? LAYER_BELOW : LAYER_NORMAL;
updateEwmhStates();
}
//! @brief Sets the skip state, and updates the _PEKWM_FRAME_SKIP atom
void
Client::setSkip(uint skip)
{
_state.skip = skip;
AtomUtil::setLong(_window,
PekwmAtoms::instance()->getAtom(PEKWM_FRAME_SKIP),
_state.skip);
}
//! @brief Sends an WM_DELETE message to the client, else kills it.
void
Client::close(void)
{
IcccmAtoms *icccm = IcccmAtoms::instance(); // convenience
if (_send_close_message) {
sendXMessage(_window, icccm->getAtom(WM_PROTOCOLS), NoEventMask,
icccm->getAtom(WM_DELETE_WINDOW), CurrentTime);
} else {
kill();
}
}
//! @brief Kills the client using XKillClient
void
Client::kill(void)
{
XKillClient(_dpy, _window);
}
//! @brief Sets the positoin based on P or U position.
//! @return Returns true on success, else false.
bool
Client::setPUPosition(void)
{
if ((_size->flags&PPosition) || (_size->flags&USPosition)) {
if (_gm.x == 0) {
_gm.x = _size->x;
}
if (_gm.y == 0) {
_gm.y = _size->y;
}
return true;
}
return false;
}
//! @brief Get the size closest to the ResizeInc incremeter
//! @param r_w Pointer to put the new width in
//! @param r_h Pointer to put the new height in
//! @param w Width to calculate from
//! @param h Height to calculate from
bool
Client::getIncSize(uint *r_w, uint *r_h, uint w, uint h)
{
int basex, basey;
if (_size->flags&PResizeInc) {
basex = (_size->flags&PBaseSize)
? _size->base_width
: (_size->flags&PMinSize) ? _size->min_width : 0;
basey = (_size->flags&PBaseSize)
? _size->base_height
: (_size->flags&PMinSize) ? _size->min_height : 0;
*r_w = w - ((w - basex) % _size->width_inc);
*r_h = h - ((h - basey) % _size->height_inc);
return true;
}
return false;
}
//! @brief Gets a MwmHint structure from a window. Doesn't free memory.
Client::MwmHints*
Client::getMwmHints(Window win)
{
Atom real_type; int real_format;
ulong items_read, items_left;
MwmHints *data;
int status =
XGetWindowProperty(_dpy, win, _wm->getMwmHintsAtom(),
0L, 20L, False, _wm->getMwmHintsAtom(),
&real_type, &real_format,
&items_read, &items_left,
(uchar **) &data);
if (status == Success) {
if (items_read < MWM_HINTS_NUM) {
XFree(data);
data = NULL;
}
} else {
data = NULL;
}
return data;
}
// This happens when a window is iconified and destroys itself. An
// Unmap event wouldn't happen in that case because the window is
// already unmapped.
void
Client::handleDestroyEvent(XDestroyWindowEvent *e)
{
_alive = false;
delete this;
}
void
Client::handleColormapChange(XColormapEvent *e)
{
if (e->c_new) {
_cmap = e->colormap;
XInstallColormap(_dpy, _cmap);
}
}
int
Client::sendXMessage(Window window, Atom atom, long mask,
long v1, long v2, long v3, long v4, long v5)
{
XEvent e;
e.type = e.xclient.type = ClientMessage;
e.xclient.display = _dpy;
e.xclient.window = window;
e.xclient.format = 32;
e.xclient.message_type = atom;
e.xclient.data.l[0] = v1;
e.xclient.data.l[1] = v2;
e.xclient.data.l[2] = v3;
e.xclient.data.l[3] = v4;
e.xclient.data.l[4] = v5;
return XSendEvent(_dpy, window, false, mask, &e);
}
//! @brief
bool
Client::getEwmhStates(NetWMStates &win_states)
{
int num = 0;
Atom *states;
states = (Atom*)
AtomUtil::getEwmhPropData(_window, EwmhAtoms::instance()->getAtom(STATE),
XA_ATOM, num);
if (states != NULL) {
EwmhAtoms *ewmh = EwmhAtoms::instance(); // convenience
for (int i = 0; i < num; ++i) {
if (states[i] == ewmh->getAtom(STATE_MODAL)) {
win_states.modal = true;
} else if (states[i] == ewmh->getAtom(STATE_STICKY)) {
win_states.sticky = true;
} else if (states[i] == ewmh->getAtom(STATE_MAXIMIZED_VERT)
&& !isCfgDeny(CFG_DENY_STATE_MAXIMIZED_VERT)) {
win_states.max_vert = true;
} else if (states[i] == ewmh->getAtom(STATE_MAXIMIZED_HORZ)
&& !isCfgDeny(CFG_DENY_STATE_MAXIMIZED_HORZ)) {
win_states.max_horz = true;
} else if (states[i] == ewmh->getAtom(STATE_SHADED)) {
win_states.shaded = true;
} else if (states[i] == ewmh->getAtom(STATE_SKIP_TASKBAR)) {
win_states.skip_taskbar = true;
} else if (states[i] == ewmh->getAtom(STATE_SKIP_PAGER)) {
win_states.skip_pager = true;
} else if (states[i] == ewmh->getAtom(STATE_HIDDEN)
&& !isCfgDeny(CFG_DENY_STATE_HIDDEN)) {
win_states.hidden = true;
} else if (states[i] == ewmh->getAtom(STATE_FULLSCREEN)
&& !isCfgDeny(CFG_DENY_STATE_FULLSCREEN)) {
win_states.fullscreen = true;
} else if (states[i] == ewmh->getAtom(STATE_ABOVE)
&& !isCfgDeny(CFG_DENY_STATE_ABOVE)) {
win_states.above = true;
} else if (states[i] == ewmh->getAtom(STATE_BELOW)
&& !isCfgDeny(CFG_DENY_STATE_BELOW)) {
win_states.below = true;
}
}
XFree(states);
return true;
} else {
return false;
}
}
//! @brief Tells the world about our states, such as shaded etc.
void
Client::updateEwmhStates(void)
{
EwmhAtoms *ewmh = EwmhAtoms::instance();
list<Atom> states;
if (false) // we don't yet support modal state
states.push_back(ewmh->getAtom(STATE_MODAL));
if (_sticky)
states.push_back(ewmh->getAtom(STATE_STICKY));
if (_state.maximized_vert)
states.push_back(ewmh->getAtom(STATE_MAXIMIZED_VERT));
if (_state.maximized_horz)
states.push_back(ewmh->getAtom(STATE_MAXIMIZED_HORZ));
if (_state.shaded)
states.push_back(ewmh->getAtom(STATE_SHADED));
if (_state.skip_taskbar)
states.push_back(ewmh->getAtom(STATE_SKIP_TASKBAR));
if (_state.skip_pager)
states.push_back(ewmh->getAtom(STATE_SKIP_PAGER));
if (_iconified)
states.push_back(ewmh->getAtom(STATE_HIDDEN));
if (_state.fullscreen)
states.push_back(ewmh->getAtom(STATE_FULLSCREEN));
if (_layer == LAYER_ABOVE_DOCK)
states.push_back(ewmh->getAtom(STATE_ABOVE));
if (_layer == LAYER_BELOW)
states.push_back(ewmh->getAtom(STATE_BELOW));
Atom *atoms = new Atom[(states.size() > 0) ? states.size() : 1];
if (states.size() > 0) {
copy(states.begin(), states.end(), atoms);
}
AtomUtil::setAtoms(_window, ewmh->getAtom(STATE), atoms, states.size());
delete [] atoms;
}
//! @brief
void
Client::getWMNormalHints(void)
{
long dummy;
XGetWMNormalHints(_dpy, _window, _size, &dummy);
}
//! @brief
void
Client::getWMProtocols(void)
{
int count;
Atom *protocols;
if (XGetWMProtocols(_dpy, _window, &protocols, &count) != 0) {
IcccmAtoms *icccm = IcccmAtoms::instance(); // convenience
for (int i = 0; i < count; ++i) {
if (protocols[i] == icccm->getAtom(WM_TAKE_FOCUS)) {
_send_focus_message = true;
} else if (protocols[i] == icccm->getAtom(WM_DELETE_WINDOW)) {
_send_close_message = true;
}
}
XFree(protocols);
}
}
//! @brief
void
Client::getTransientForHint(void)
{
if (!_transient)
XGetTransientForHint(_dpy, _window, &_transient);
}
//! @brief
void
Client::getStrutHint(void)
{
int num = 0;
CARD32 *strut = NULL;
strut = (CARD32*)
AtomUtil::getEwmhPropData(_window,
EwmhAtoms::instance()->getAtom(NET_WM_STRUT),
XA_CARDINAL, num);
if (strut != NULL) {
if (_strut != NULL) {
PScreen::instance()->removeStrut(_strut);
} else {
_strut = new Strut();
}
*_strut = strut;
PScreen::instance()->addStrut(_strut);
XFree(strut);
} else if (_strut != NULL) {
PScreen::instance()->removeStrut(_strut);
delete _strut;
_strut = NULL;
}
Workspaces::instance()->getActiveViewport()->hintSetWorkarea();
}
//! @brief
void
Client::removeStrutHint(void)
{
if (_strut == NULL)
return;
PScreen::instance()->removeStrut(_strut);
delete _strut;
_strut = NULL;
Workspaces::instance()->getActiveViewport()->hintSetWorkarea();
}
syntax highlighted by Code2HTML, v. 0.9.1