openbox/src/client.cc

1870 lines
52 KiB
C++
Raw Normal View History

2002-11-06 07:01:34 +00:00
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2003-02-09 23:07:54 +00:00
#include "config.h"
2002-11-06 07:01:34 +00:00
#include "client.hh"
2002-12-04 00:26:45 +00:00
#include "frame.hh"
2002-12-04 01:04:31 +00:00
#include "screen.hh"
2002-11-06 07:01:34 +00:00
#include "openbox.hh"
#include "bindings.hh"
2002-11-06 07:01:34 +00:00
#include "otk/display.hh"
#include "otk/property.hh"
extern "C" {
#include <X11/Xlib.h>
#include <X11/Xutil.h>
2003-01-16 08:44:52 +00:00
#include <X11/Xatom.h>
2002-11-06 07:01:34 +00:00
#include "gettext.h"
#define _(str) gettext(str)
}
2003-02-07 09:37:20 +00:00
#include <climits>
#include <cassert>
#include <algorithm>
2002-11-06 07:01:34 +00:00
namespace ob {
2003-01-11 19:42:43 +00:00
Client::Client(int screen, Window window)
: otk::EventHandler(),
frame(0), _screen(screen), _window(window)
2002-11-06 07:01:34 +00:00
{
assert(screen >= 0);
2002-11-06 07:01:34 +00:00
assert(window);
ignore_unmaps = 0;
2002-11-06 07:01:34 +00:00
// update EVERYTHING the first time!!
// defaults
_wmstate = NormalState;
_focused = false;
2003-01-05 01:40:38 +00:00
_transient_for = 0;
_layer = Layer_Normal;
_urgent = false;
_positioned = false;
_disabled_decorations = 0;
_group = None;
_desktop = 0;
getArea();
getDesktop();
getState(); // do this before updateTransientFor! (for _modal)
getShaped();
2003-01-05 01:40:38 +00:00
updateTransientFor();
getMwmHints();
getType(); // this can change the mwmhints for special cases
2002-11-06 10:05:56 +00:00
updateProtocols();
getGravity(); // get the attribute gravity
updateNormalHints(); // this may override the attribute gravity
// got the type, the mwmhints, the protocols, and the normal hints (min/max
// sizes), so we're ready to set up
// the decorations/functions
setupDecorAndFunctions();
// also get the initial_state and set _iconic if we aren't "starting"
// when we're "starting" that means we should use whatever state was already
// on the window over the initial map state, because it was already mapped
updateWMHints(openbox->state() != Openbox::State_Starting);
updateTitle();
2002-11-07 08:22:27 +00:00
updateIconTitle();
updateClass();
updateStrut();
// this makes sure that these windows appear on all desktops
if (/*_type == Type_Dock ||*/ _type == Type_Desktop)
_desktop = 0xffffffff;
// set the desktop hint, to make sure that it always exists, and to reflect
// any changes we've made here
otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
otk::Property::atoms.cardinal, (unsigned)_desktop);
changeState();
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
Client::~Client()
2002-11-06 07:01:34 +00:00
{
2003-01-12 18:29:17 +00:00
// clean up childrens' references
while (!_transients.empty()) {
_transients.front()->_transient_for = 0;
_transients.pop_front();
}
// clean up parents reference to this
if (_transient_for)
_transient_for->_transients.remove(this); // remove from old parent
if (openbox->state() != Openbox::State_Exiting) {
2003-01-03 23:57:00 +00:00
// these values should not be persisted across a window unmapping/mapping
2003-01-16 08:44:52 +00:00
otk::Property::erase(_window, otk::Property::atoms.net_wm_desktop);
otk::Property::erase(_window, otk::Property::atoms.net_wm_state);
} else {
// if we're left in an iconic state, the client wont be mapped. this is
// bad, since we will no longer be managing the window on restart
if (_iconic)
XMapWindow(**otk::display, _window);
2003-01-03 23:57:00 +00:00
}
}
bool Client::validate() const
{
XSync(**otk::display, false); // get all events on the server
XEvent e;
if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &e) ||
XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &e)) {
XPutBackEvent(**otk::display, &e);
return false;
}
return true;
}
void Client::getGravity()
{
XWindowAttributes wattrib;
Status ret;
ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
assert(ret != BadWindow);
_gravity = wattrib.win_gravity;
}
2003-01-17 02:52:26 +00:00
2003-01-11 19:42:43 +00:00
void Client::getDesktop()
{
// defaults to the current desktop
_desktop = openbox->screen(_screen)->desktop();
if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop,
otk::Property::atoms.cardinal,
(long unsigned*)&_desktop)) {
#ifdef DEBUG
2003-01-25 16:36:55 +00:00
// printf("Window requested desktop: %ld\n", _desktop);
#endif
}
}
2003-01-11 19:42:43 +00:00
void Client::getType()
{
_type = (WindowType) -1;
unsigned long *val;
unsigned long num = (unsigned) -1;
2003-01-16 08:44:52 +00:00
if (otk::Property::get(_window, otk::Property::atoms.net_wm_window_type,
otk::Property::atoms.atom, &num, &val)) {
// use the first value that we know about in the array
for (unsigned long i = 0; i < num; ++i) {
2003-01-16 08:44:52 +00:00
if (val[i] == otk::Property::atoms.net_wm_window_type_desktop)
_type = Type_Desktop;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_dock)
_type = Type_Dock;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_toolbar)
_type = Type_Toolbar;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_menu)
_type = Type_Menu;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_utility)
_type = Type_Utility;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_splash)
_type = Type_Splash;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_dialog)
_type = Type_Dialog;
2003-01-16 08:44:52 +00:00
else if (val[i] == otk::Property::atoms.net_wm_window_type_normal)
_type = Type_Normal;
else if (val[i] == otk::Property::atoms.kde_net_wm_window_type_override){
// prevent this window from getting any decor or functionality
_mwmhints.flags &= MwmFlag_Functions | MwmFlag_Decorations;
_mwmhints.decorations = 0;
_mwmhints.functions = 0;
}
2003-01-06 04:44:00 +00:00
if (_type != (WindowType) -1)
break; // grab the first known type
}
delete val;
}
if (_type == (WindowType) -1) {
/*
* the window type hint was not set, which means we either classify ourself
* as a normal window or a dialog, depending on if we are a transient.
*/
2003-01-05 01:40:38 +00:00
if (_transient_for)
_type = Type_Dialog;
else
_type = Type_Normal;
}
2002-11-06 11:31:50 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::setupDecorAndFunctions()
2002-11-06 11:31:50 +00:00
{
// start with everything (cept fullscreen)
_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
_functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
Func_Shade;
if (_delete_window) {
_decorations |= Decor_Close;
_functions |= Func_Close;
}
2003-02-07 09:37:20 +00:00
if (!(_min_size.width() < _max_size.width() ||
_min_size.height() < _max_size.height())) {
_decorations &= ~(Decor_Maximize | Decor_Handle);
_functions &= ~(Func_Resize | Func_Maximize);
}
switch (_type) {
case Type_Normal:
// normal windows retain all of the possible decorations and
// functionality, and are the only windows that you can fullscreen
_functions |= Func_Fullscreen;
2003-02-01 00:22:16 +00:00
break;
2002-11-06 11:31:50 +00:00
case Type_Dialog:
// dialogs cannot be maximized
_decorations &= ~Decor_Maximize;
_functions &= ~Func_Maximize;
break;
2002-11-06 11:31:50 +00:00
case Type_Menu:
case Type_Toolbar:
case Type_Utility:
// these windows get less functionality
_decorations &= ~(Decor_Iconify | Decor_Handle);
_functions &= ~(Func_Iconify | Func_Resize);
break;
case Type_Desktop:
case Type_Dock:
case Type_Splash:
// none of these windows are manipulated by the window manager
_decorations = 0;
_functions = 0;
break;
2002-11-06 11:31:50 +00:00
}
// Mwm Hints are applied subtractively to what has already been chosen for
// decor and functionality
if (_mwmhints.flags & MwmFlag_Decorations) {
if (! (_mwmhints.decorations & MwmDecor_All)) {
if (! (_mwmhints.decorations & MwmDecor_Border))
2002-11-06 11:31:50 +00:00
_decorations &= ~Decor_Border;
if (! (_mwmhints.decorations & MwmDecor_Handle))
2002-11-06 11:31:50 +00:00
_decorations &= ~Decor_Handle;
if (! (_mwmhints.decorations & MwmDecor_Title))
2002-11-06 11:31:50 +00:00
_decorations &= ~Decor_Titlebar;
if (! (_mwmhints.decorations & MwmDecor_Iconify))
2002-11-06 11:31:50 +00:00
_decorations &= ~Decor_Iconify;
if (! (_mwmhints.decorations & MwmDecor_Maximize))
2002-11-06 11:31:50 +00:00
_decorations &= ~Decor_Maximize;
}
}
if (_mwmhints.flags & MwmFlag_Functions) {
if (! (_mwmhints.functions & MwmFunc_All)) {
if (! (_mwmhints.functions & MwmFunc_Resize))
_functions &= ~Func_Resize;
if (! (_mwmhints.functions & MwmFunc_Move))
_functions &= ~Func_Move;
if (! (_mwmhints.functions & MwmFunc_Iconify))
_functions &= ~Func_Iconify;
if (! (_mwmhints.functions & MwmFunc_Maximize))
_functions &= ~Func_Maximize;
// dont let mwm hints kill the close button
//if (! (_mwmhints.functions & MwmFunc_Close))
// _functions &= ~Func_Close;
2002-11-06 11:31:50 +00:00
}
}
// can't maximize without moving/resizing
if (!((_functions & Func_Move) && (_functions & Func_Resize)))
_functions &= ~Func_Maximize;
// finally, user specified disabled decorations are applied to subtract
// decorations
if (_disabled_decorations & Decor_Titlebar)
_decorations &= ~Decor_Titlebar;
if (_disabled_decorations & Decor_Handle)
_decorations &= ~Decor_Handle;
if (_disabled_decorations & Decor_Border)
_decorations &= ~Decor_Border;
if (_disabled_decorations & Decor_Iconify)
_decorations &= ~Decor_Iconify;
if (_disabled_decorations & Decor_Maximize)
_decorations &= ~Decor_Maximize;
if (_disabled_decorations & Decor_AllDesktops)
_decorations &= ~Decor_AllDesktops;
if (_disabled_decorations & Decor_Close)
_decorations &= ~Decor_Close;
// if we don't have a titlebar, then we cannot shade!
if (!(_decorations & Decor_Titlebar))
_functions &= ~Func_Shade;
changeAllowedActions();
if (frame) {
frame->adjustSize(); // change the decors on the frame
frame->adjustPosition(); // with more/less decorations, we may need to be
// moved
remaximize(); // with new decor, the window's maximized size may change
}
}
2003-01-11 19:42:43 +00:00
void Client::getMwmHints()
{
unsigned long num = MwmHints::elements;
unsigned long *hints;
_mwmhints.flags = 0; // default to none
2003-01-16 08:44:52 +00:00
if (!otk::Property::get(_window, otk::Property::atoms.motif_wm_hints,
otk::Property::atoms.motif_wm_hints, &num,
(unsigned long **)&hints))
return;
if (num >= MwmHints::elements) {
// retrieved the hints
_mwmhints.flags = hints[0];
_mwmhints.functions = hints[1];
_mwmhints.decorations = hints[2];
}
2002-11-06 11:31:50 +00:00
delete [] hints;
}
2003-01-11 19:42:43 +00:00
void Client::getArea()
{
XWindowAttributes wattrib;
Status ret;
2003-01-13 09:04:57 +00:00
ret = XGetWindowAttributes(**otk::display, _window, &wattrib);
assert(ret != BadWindow);
2003-02-07 09:37:20 +00:00
_area = otk::Rect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
2002-11-06 10:05:56 +00:00
_border_width = wattrib.border_width;
}
2003-01-11 19:42:43 +00:00
void Client::getState()
{
2003-01-03 18:21:28 +00:00
_modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
_iconic = _skip_taskbar = _skip_pager = false;
unsigned long *state;
unsigned long num = (unsigned) -1;
2003-01-16 08:44:52 +00:00
if (otk::Property::get(_window, otk::Property::atoms.net_wm_state,
otk::Property::atoms.atom, &num, &state)) {
for (unsigned long i = 0; i < num; ++i) {
2003-01-16 08:44:52 +00:00
if (state[i] == otk::Property::atoms.net_wm_state_modal)
_modal = true;
else if (state[i] == otk::Property::atoms.net_wm_state_shaded)
_shaded = true;
else if (state[i] == otk::Property::atoms.net_wm_state_hidden)
_iconic = true;
else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar)
2003-01-03 23:51:06 +00:00
_skip_taskbar = true;
2003-01-16 08:44:52 +00:00
else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager)
2003-01-03 23:51:06 +00:00
_skip_pager = true;
2003-01-16 08:44:52 +00:00
else if (state[i] == otk::Property::atoms.net_wm_state_fullscreen)
_fullscreen = true;
2003-01-16 08:44:52 +00:00
else if (state[i] == otk::Property::atoms.net_wm_state_maximized_vert)
_max_vert = true;
2003-01-16 08:44:52 +00:00
else if (state[i] == otk::Property::atoms.net_wm_state_maximized_horz)
_max_horz = true;
2003-01-16 08:44:52 +00:00
else if (state[i] == otk::Property::atoms.net_wm_state_above)
2003-01-03 18:21:28 +00:00
_above = true;
2003-01-16 08:44:52 +00:00
else if (state[i] == otk::Property::atoms.net_wm_state_below)
2003-01-03 18:21:28 +00:00
_below = true;
}
delete [] state;
}
}
2003-01-11 19:42:43 +00:00
void Client::getShaped()
{
_shaped = false;
#ifdef SHAPE
2003-01-13 09:04:57 +00:00
if (otk::display->shape()) {
int foo;
unsigned int ufoo;
2002-11-10 05:54:12 +00:00
int s;
2003-01-13 09:04:57 +00:00
XShapeSelectInput(**otk::display, _window, ShapeNotifyMask);
2002-11-10 05:54:12 +00:00
2003-01-13 09:04:57 +00:00
XShapeQueryExtents(**otk::display, _window, &s, &foo,
&foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
2002-11-10 05:54:12 +00:00
_shaped = (s != 0);
}
#endif // SHAPE
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::calcLayer() {
StackLayer l;
if (_iconic) l = Layer_Icon;
else if (_fullscreen) l = Layer_Fullscreen;
else if (_type == Type_Desktop) l = Layer_Desktop;
else if (_type == Type_Dock) {
if (!_below) l = Layer_Top;
else l = Layer_Normal;
}
else if (_above) l = Layer_Above;
else if (_below) l = Layer_Below;
else l = Layer_Normal;
if (l != _layer) {
_layer = l;
if (frame) {
/*
if we don't have a frame, then we aren't mapped yet (and this would
SIGSEGV :)
*/
openbox->screen(_screen)->raiseWindow(this);
}
}
2003-01-03 18:21:28 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::updateProtocols()
2002-11-06 11:31:50 +00:00
{
2002-11-06 10:05:56 +00:00
Atom *proto;
int num_return = 0;
_focus_notify = false;
_delete_window = false;
2002-11-06 10:05:56 +00:00
2003-01-13 09:04:57 +00:00
if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) {
2002-11-06 10:05:56 +00:00
for (int i = 0; i < num_return; ++i) {
2003-01-16 08:44:52 +00:00
if (proto[i] == otk::Property::atoms.wm_delete_window) {
// this means we can request the window to close
_delete_window = true;
2003-01-16 08:44:52 +00:00
} else if (proto[i] == otk::Property::atoms.wm_take_focus)
2002-11-06 10:05:56 +00:00
// if this protocol is requested, then the window will be notified
// by the window manager whenever it receives focus
_focus_notify = true;
}
XFree(proto);
}
}
2003-01-11 19:42:43 +00:00
void Client::updateNormalHints()
2002-11-06 07:01:34 +00:00
{
XSizeHints size;
long ret;
int oldgravity = _gravity;
2002-11-06 07:01:34 +00:00
// defaults
_min_ratio = 0.0;
_max_ratio = 0.0;
2003-02-07 09:37:20 +00:00
_size_inc = otk::Size(1, 1);
_base_size = otk::Size(0, 0);
_min_size = otk::Size(0, 0);
_max_size = otk::Size(INT_MAX, INT_MAX);
2002-11-06 07:01:34 +00:00
// get the hints from the window
2003-01-13 09:04:57 +00:00
if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) {
_positioned = (size.flags & (PPosition|USPosition));
if (size.flags & PWinGravity) {
2002-11-06 07:01:34 +00:00
_gravity = size.win_gravity;
// if the client has a frame, i.e. has already been mapped and is
// changing its gravity
if (frame && _gravity != oldgravity) {
// move our idea of the client's position based on its new gravity
2003-02-07 09:37:20 +00:00
int x = frame->area().x(), y = frame->area().y();
frame->frameGravity(x, y);
2003-02-07 09:37:20 +00:00
_area = otk::Rect(otk::Point(x, y), _area.size());
}
}
if (size.flags & PAspect) {
if (size.min_aspect.y) _min_ratio = size.min_aspect.x/size.min_aspect.y;
if (size.max_aspect.y) _max_ratio = size.max_aspect.x/size.max_aspect.y;
}
2002-12-04 00:26:45 +00:00
if (size.flags & PMinSize)
2003-02-07 09:37:20 +00:00
_min_size = otk::Size(size.min_width, size.min_height);
2002-12-04 00:26:45 +00:00
if (size.flags & PMaxSize)
2003-02-07 09:37:20 +00:00
_max_size = otk::Size(size.max_width, size.max_height);
2002-12-04 00:26:45 +00:00
if (size.flags & PBaseSize)
2003-02-07 09:37:20 +00:00
_base_size = otk::Size(size.base_width, size.base_height);
2002-12-04 00:26:45 +00:00
if (size.flags & PResizeInc)
2003-02-07 09:37:20 +00:00
_size_inc = otk::Size(size.width_inc, size.height_inc);
2002-11-06 07:01:34 +00:00
}
}
void Client::updateWMHints(bool initstate)
2002-11-06 07:01:34 +00:00
{
XWMHints *hints;
// assume a window takes input if it doesnt specify
_can_focus = true;
bool ur = false;
2002-11-06 07:01:34 +00:00
2003-01-13 09:04:57 +00:00
if ((hints = XGetWMHints(**otk::display, _window)) != NULL) {
2002-11-06 07:01:34 +00:00
if (hints->flags & InputHint)
_can_focus = hints->input;
// only do this when initstate is true!
if (initstate && (hints->flags & StateHint))
_iconic = hints->initial_state == IconicState;
2002-11-06 07:01:34 +00:00
if (hints->flags & XUrgencyHint)
ur = true;
if (hints->flags & WindowGroupHint) {
2002-11-06 07:01:34 +00:00
if (hints->window_group != _group) {
// XXX: remove from the old group if there was one
_group = hints->window_group;
// XXX: do stuff with the group
}
} else // no group!
_group = None;
2002-11-06 07:01:34 +00:00
XFree(hints);
}
if (ur != _urgent) {
_urgent = ur;
#ifdef DEBUG
printf("DEBUG: Urgent Hint for 0x%lx: %s\n",
(long)_window, _urgent ? "ON" : "OFF");
#endif
// fire the urgent callback if we're mapped, otherwise, wait until after
// we're mapped
if (frame)
fireUrgent();
}
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::updateTitle()
2002-11-06 07:01:34 +00:00
{
_title = "";
// try netwm
2003-01-16 08:44:52 +00:00
if (!otk::Property::get(_window, otk::Property::atoms.net_wm_name,
otk::Property::utf8, &_title)) {
2002-11-06 07:01:34 +00:00
// try old x stuff
2003-01-16 08:44:52 +00:00
otk::Property::get(_window, otk::Property::atoms.wm_name,
otk::Property::ascii, &_title);
2002-11-06 07:01:34 +00:00
}
if (_title.empty())
_title = _("Unnamed Window");
if (frame)
frame->adjustTitle();
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::updateIconTitle()
2002-11-07 08:22:27 +00:00
{
_icon_title = "";
// try netwm
2003-01-16 08:44:52 +00:00
if (!otk::Property::get(_window, otk::Property::atoms.net_wm_icon_name,
otk::Property::utf8, &_icon_title)) {
2002-11-07 08:22:27 +00:00
// try old x stuff
2003-01-16 08:44:52 +00:00
otk::Property::get(_window, otk::Property::atoms.wm_icon_name,
otk::Property::ascii, &_icon_title);
2002-11-07 08:22:27 +00:00
}
if (_title.empty())
_icon_title = _("Unnamed Window");
}
2003-01-11 19:42:43 +00:00
void Client::updateClass()
2002-11-06 07:01:34 +00:00
{
// set the defaults
2003-01-04 19:09:52 +00:00
_app_name = _app_class = _role = "";
2002-11-06 07:01:34 +00:00
2003-01-11 19:42:43 +00:00
otk::Property::StringVect v;
2002-11-06 07:01:34 +00:00
unsigned long num = 2;
2003-01-16 08:44:52 +00:00
if (otk::Property::get(_window, otk::Property::atoms.wm_class,
otk::Property::ascii, &num, &v)) {
2003-01-13 05:54:40 +00:00
if (num > 0) _app_name = v[0].c_str();
if (num > 1) _app_class = v[1].c_str();
2003-01-04 19:09:52 +00:00
}
2002-11-06 07:01:34 +00:00
2003-01-04 19:09:52 +00:00
v.clear();
num = 1;
2003-01-16 08:44:52 +00:00
if (otk::Property::get(_window, otk::Property::atoms.wm_window_role,
otk::Property::ascii, &num, &v)) {
2003-01-13 05:54:40 +00:00
if (num > 0) _role = v[0].c_str();
2003-01-04 19:09:52 +00:00
}
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::updateStrut()
{
unsigned long num = 4;
unsigned long *data;
2003-01-16 08:44:52 +00:00
if (!otk::Property::get(_window, otk::Property::atoms.net_wm_strut,
otk::Property::atoms.cardinal, &num, &data))
return;
if (num == 4) {
_strut.left = data[0];
_strut.right = data[1];
_strut.top = data[2];
_strut.bottom = data[3];
// updating here is pointless while we're being mapped cuz we're not in
// the screen's client list yet
if (frame)
2003-02-10 06:50:19 +00:00
openbox->screen(_screen)->updateStruts();
}
delete [] data;
}
2003-01-11 19:42:43 +00:00
void Client::updateTransientFor()
2003-01-05 01:40:38 +00:00
{
Window t = 0;
2003-01-11 19:42:43 +00:00
Client *c = 0;
2003-01-05 01:40:38 +00:00
2003-01-13 09:04:57 +00:00
if (XGetTransientForHint(**otk::display, _window, &t) &&
2003-01-05 01:40:38 +00:00
t != _window) { // cant be transient to itself!
c = openbox->findClient(t);
2003-01-05 01:40:38 +00:00
assert(c != this); // if this happens then we need to check for it
if (!c /*XXX: && _group*/) {
// not transient to a client, see if it is transient for a group
if (//t == _group->leader() ||
t == None ||
2003-01-13 09:04:57 +00:00
t == otk::display->screenInfo(_screen)->rootWindow()) {
2003-01-05 01:40:38 +00:00
// window is a transient for its group!
// XXX: for now this is treated as non-transient.
// this needs to be fixed!
}
}
}
// if anything has changed...
if (c != _transient_for) {
if (_transient_for)
_transient_for->_transients.remove(this); // remove from old parent
_transient_for = c;
if (_transient_for)
_transient_for->_transients.push_back(this); // add to new parent
}
}
2003-01-11 19:42:43 +00:00
void Client::propertyHandler(const XPropertyEvent &e)
2002-11-06 07:01:34 +00:00
{
2003-01-11 19:42:43 +00:00
otk::EventHandler::propertyHandler(e);
// validate cuz we query stuff off the client here
if (!validate()) return;
2002-12-02 22:32:38 +00:00
// compress changes to a single property into a single change
XEvent ce;
2003-01-13 09:04:57 +00:00
while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
2002-12-02 22:32:38 +00:00
// XXX: it would be nice to compress ALL changes to a property, not just
// changes in a row without other props between.
if (ce.xproperty.atom != e.atom) {
2003-01-13 09:04:57 +00:00
XPutBackEvent(**otk::display, &ce);
2002-12-02 22:32:38 +00:00
break;
}
}
if (e.atom == XA_WM_NORMAL_HINTS) {
2002-11-06 07:01:34 +00:00
updateNormalHints();
setupDecorAndFunctions(); // normal hints can make a window non-resizable
} else if (e.atom == XA_WM_HINTS)
2002-11-06 07:01:34 +00:00
updateWMHints();
else if (e.atom == XA_WM_TRANSIENT_FOR) {
2003-01-05 01:40:38 +00:00
updateTransientFor();
getType();
calcLayer(); // type may have changed, so update the layer
setupDecorAndFunctions();
}
2003-01-16 08:44:52 +00:00
else if (e.atom == otk::Property::atoms.net_wm_name ||
e.atom == otk::Property::atoms.wm_name)
2002-11-06 07:01:34 +00:00
updateTitle();
2003-01-16 08:44:52 +00:00
else if (e.atom == otk::Property::atoms.net_wm_icon_name ||
e.atom == otk::Property::atoms.wm_icon_name)
2002-11-07 08:22:27 +00:00
updateIconTitle();
2003-01-16 08:44:52 +00:00
else if (e.atom == otk::Property::atoms.wm_class)
2002-11-06 07:01:34 +00:00
updateClass();
else if (e.atom == otk::Property::atoms.wm_protocols) {
2002-11-06 10:05:56 +00:00
updateProtocols();
setupDecorAndFunctions();
}
2003-01-16 08:44:52 +00:00
else if (e.atom == otk::Property::atoms.net_wm_strut)
updateStrut();
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::setWMState(long state)
2002-11-06 07:01:34 +00:00
{
if (state == _wmstate) return; // no change
2002-11-06 07:01:34 +00:00
switch (state) {
2002-11-06 07:01:34 +00:00
case IconicState:
iconify(true);
2002-11-06 07:01:34 +00:00
break;
case NormalState:
iconify(false);
2002-11-06 07:01:34 +00:00
break;
}
}
void Client::setDesktop(unsigned int target)
2002-11-06 07:01:34 +00:00
{
if (target == _desktop) return;
printf("Setting desktop %u\n", target);
if (!(target < openbox->screen(_screen)->numDesktops() ||
target == 0xffffffff))
return;
2003-02-10 06:50:19 +00:00
2002-11-06 07:01:34 +00:00
_desktop = target;
2003-01-07 01:54:26 +00:00
// set the desktop hint
otk::Property::set(_window, otk::Property::atoms.net_wm_desktop,
otk::Property::atoms.cardinal, _desktop);
2003-01-07 01:54:26 +00:00
// 'move' the window to the new desktop
showhide();
openbox->screen(_screen)->updateStruts();
}
void Client::showhide()
{
if (!_iconic &&
(_desktop == openbox->screen(_screen)->desktop() ||
_desktop == 0xffffffff))
frame->show();
else
frame->hide();
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::setState(StateAction action, long data1, long data2)
2002-11-06 07:01:34 +00:00
{
bool shadestate = _shaded;
bool fsstate = _fullscreen;
2003-02-01 05:26:06 +00:00
bool maxh = _max_horz;
bool maxv = _max_vert;
2002-11-06 07:01:34 +00:00
if (!(action == State_Add || action == State_Remove ||
action == State_Toggle))
return; // an invalid action was passed to the client message, ignore it
for (int i = 0; i < 2; ++i) {
Atom state = i == 0 ? data1 : data2;
if (! state) continue;
// if toggling, then pick whether we're adding or removing
if (action == State_Toggle) {
2003-01-16 08:44:52 +00:00
if (state == otk::Property::atoms.net_wm_state_modal)
2002-11-06 07:01:34 +00:00
action = _modal ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_maximized_vert)
2002-11-06 07:01:34 +00:00
action = _max_vert ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_maximized_horz)
2002-11-06 07:01:34 +00:00
action = _max_horz ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_shaded)
2002-11-06 07:01:34 +00:00
action = _shaded ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_skip_taskbar)
2003-01-03 23:51:06 +00:00
action = _skip_taskbar ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_skip_pager)
2003-01-03 23:51:06 +00:00
action = _skip_pager ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_fullscreen)
2002-11-06 07:01:34 +00:00
action = _fullscreen ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_above)
2003-01-03 18:21:28 +00:00
action = _above ? State_Remove : State_Add;
2003-01-16 08:44:52 +00:00
else if (state == otk::Property::atoms.net_wm_state_below)
2003-01-03 18:21:28 +00:00
action = _below ? State_Remove : State_Add;
2002-11-06 07:01:34 +00:00
}
if (action == State_Add) {
2003-01-16 08:44:52 +00:00
if (state == otk::Property::atoms.net_wm_state_modal) {
2002-11-06 07:01:34 +00:00
if (_modal) continue;
2003-02-10 03:47:54 +00:00
_modal = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
2003-02-01 05:26:06 +00:00
maxv = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
2002-11-06 07:01:34 +00:00
if (_max_horz) continue;
2003-02-01 05:26:06 +00:00
maxh = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_shaded) {
2003-01-04 02:49:43 +00:00
shadestate = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
2003-01-03 23:51:06 +00:00
_skip_taskbar = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
2003-01-03 23:51:06 +00:00
_skip_pager = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
fsstate = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_above) {
2003-01-03 18:21:28 +00:00
if (_above) continue;
_above = true;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_below) {
2003-01-03 18:21:28 +00:00
if (_below) continue;
_below = true;
2002-11-06 07:01:34 +00:00
}
} else { // action == State_Remove
2003-01-16 08:44:52 +00:00
if (state == otk::Property::atoms.net_wm_state_modal) {
2002-11-06 07:01:34 +00:00
if (!_modal) continue;
2003-02-10 03:47:54 +00:00
_modal = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_maximized_vert) {
2003-02-01 05:26:06 +00:00
maxv = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_maximized_horz) {
2003-02-01 05:26:06 +00:00
maxh = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_shaded) {
2003-01-04 02:49:43 +00:00
shadestate = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) {
2003-01-03 23:51:06 +00:00
_skip_taskbar = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_skip_pager) {
2003-01-03 23:51:06 +00:00
_skip_pager = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_fullscreen) {
fsstate = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_above) {
2003-01-03 18:21:28 +00:00
if (!_above) continue;
_above = false;
2003-01-16 08:44:52 +00:00
} else if (state == otk::Property::atoms.net_wm_state_below) {
2003-01-03 18:21:28 +00:00
if (!_below) continue;
_below = false;
2002-11-06 07:01:34 +00:00
}
}
}
2003-02-01 05:26:06 +00:00
if (maxh != _max_horz || maxv != _max_vert) {
if (maxh != _max_horz && maxv != _max_vert) { // toggling both
if (maxh == maxv) { // both going the same way
maximize(maxh, 0, true);
} else {
maximize(maxh, 1, true);
maximize(maxv, 2, true);
}
} else { // toggling one
if (maxh != _max_horz)
maximize(maxh, 1, true);
else
maximize(maxv, 2, true);
2003-02-01 05:26:06 +00:00
}
}
// change fullscreen state before shading, as it will affect if the window
// can shade or not
if (fsstate != _fullscreen)
2003-02-01 05:26:06 +00:00
fullscreen(fsstate, true);
2003-01-04 02:49:43 +00:00
if (shadestate != _shaded)
shade(shadestate);
calcLayer();
2003-01-30 22:20:25 +00:00
changeState(); // change the hint to relect these changes
2002-11-06 07:01:34 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::toggleClientBorder(bool addborder)
{
// adjust our idea of where the client is, based on its border. When the
// border is removed, the client should now be considered to be in a
// different position.
// when re-adding the border to the client, the same operation needs to be
// reversed.
int oldx = _area.x(), oldy = _area.y();
int x = oldx, y = oldy;
switch(_gravity) {
default:
case NorthWestGravity:
case WestGravity:
case SouthWestGravity:
break;
case NorthEastGravity:
case EastGravity:
case SouthEastGravity:
if (addborder) x -= _border_width * 2;
else x += _border_width * 2;
break;
case NorthGravity:
case SouthGravity:
case CenterGravity:
case ForgetGravity:
case StaticGravity:
if (addborder) x -= _border_width;
else x += _border_width;
break;
}
switch(_gravity) {
default:
case NorthWestGravity:
case NorthGravity:
case NorthEastGravity:
break;
case SouthWestGravity:
case SouthGravity:
case SouthEastGravity:
if (addborder) y -= _border_width * 2;
else y += _border_width * 2;
break;
case WestGravity:
case EastGravity:
case CenterGravity:
case ForgetGravity:
case StaticGravity:
if (addborder) y -= _border_width;
else y += _border_width;
break;
}
2003-02-07 09:37:20 +00:00
_area = otk::Rect(otk::Point(x, y), _area.size());
if (addborder) {
2003-01-13 09:04:57 +00:00
XSetWindowBorderWidth(**otk::display, _window, _border_width);
// move the client so it is back it the right spot _with_ its border!
if (x != oldx || y != oldy)
XMoveWindow(**otk::display, _window, x, y);
} else
2003-01-13 09:04:57 +00:00
XSetWindowBorderWidth(**otk::display, _window, 0);
}
2003-01-11 19:42:43 +00:00
void Client::clientMessageHandler(const XClientMessageEvent &e)
2002-11-06 07:01:34 +00:00
{
2003-01-11 19:42:43 +00:00
otk::EventHandler::clientMessageHandler(e);
// validate cuz we query stuff off the client here
if (!validate()) return;
2002-11-06 07:01:34 +00:00
if (e.format != 32) return;
2003-01-16 08:44:52 +00:00
if (e.message_type == otk::Property::atoms.wm_change_state) {
// compress changes into a single change
bool compress = false;
XEvent ce;
2003-01-13 09:04:57 +00:00
while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
2002-12-02 22:36:43 +00:00
// XXX: it would be nice to compress ALL messages of a type, not just
// messages in a row without other message types between.
if (ce.xclient.message_type != e.message_type) {
2003-01-13 09:04:57 +00:00
XPutBackEvent(**otk::display, &ce);
2002-12-02 22:36:43 +00:00
break;
}
compress = true;
2002-12-02 22:36:43 +00:00
}
if (compress)
2002-12-02 22:36:43 +00:00
setWMState(ce.xclient.data.l[0]); // use the found event
else
setWMState(e.data.l[0]); // use the original event
2003-01-16 08:44:52 +00:00
} else if (e.message_type == otk::Property::atoms.net_wm_desktop) {
// compress changes into a single change
bool compress = false;
XEvent ce;
2003-01-13 09:04:57 +00:00
while (XCheckTypedEvent(**otk::display, e.type, &ce)) {
2002-12-02 22:36:43 +00:00
// XXX: it would be nice to compress ALL messages of a type, not just
// messages in a row without other message types between.
if (ce.xclient.message_type != e.message_type) {
2003-01-13 09:04:57 +00:00
XPutBackEvent(**otk::display, &ce);
2002-12-02 22:36:43 +00:00
break;
}
compress = true;
2002-12-02 22:36:43 +00:00
}
if (compress)
setDesktop(e.data.l[0]); // use the found event
else
setDesktop(e.data.l[0]); // use the original event
2003-01-16 08:44:52 +00:00
} else if (e.message_type == otk::Property::atoms.net_wm_state) {
// can't compress these
#ifdef DEBUG
printf("net_wm_state %s %ld %ld for 0x%lx\n",
(e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
e.data.l[0] == 2 ? "Toggle" : "INVALID"),
e.data.l[1], e.data.l[2], _window);
#endif
2002-11-06 07:01:34 +00:00
setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
2003-01-16 08:44:52 +00:00
} else if (e.message_type == otk::Property::atoms.net_close_window) {
#ifdef DEBUG
printf("net_close_window for 0x%lx\n", _window);
#endif
close();
2003-01-16 08:44:52 +00:00
} else if (e.message_type == otk::Property::atoms.net_active_window) {
#ifdef DEBUG
printf("net_active_window for 0x%lx\n", _window);
#endif
if (_iconic)
iconify(false);
if (_shaded)
shade(false);
focus();
openbox->screen(_screen)->raiseWindow(this);
} else if (e.message_type == otk::Property::atoms.openbox_active_window) {
if (_iconic)
iconify(false);
if (e.data.l[0] && _shaded)
shade(false);
focus();
if (e.data.l[1])
openbox->screen(_screen)->raiseWindow(this);
}
2002-11-06 07:01:34 +00:00
}
#if defined(SHAPE)
2003-01-11 19:42:43 +00:00
void Client::shapeHandler(const XShapeEvent &e)
2002-11-10 05:54:12 +00:00
{
2003-01-11 19:42:43 +00:00
otk::EventHandler::shapeHandler(e);
if (e.kind == ShapeBounding) {
_shaped = e.shaped;
frame->adjustShape();
}
2002-11-10 05:54:12 +00:00
}
#endif
void Client::resize(Corner anchor, int w, int h)
{
if (!(_functions & Func_Resize)) return;
internal_resize(anchor, w, h);
}
void Client::internal_resize(Corner anchor, int w, int h,
2003-02-07 09:37:20 +00:00
bool user, int x, int y)
2002-12-04 00:26:45 +00:00
{
w -= _base_size.width();
h -= _base_size.height();
2002-12-04 00:26:45 +00:00
if (user) {
// for interactive resizing. have to move half an increment in each
// direction.
int mw = w % _size_inc.width(); // how far we are towards the next size inc
int mh = h % _size_inc.height();
int aw = _size_inc.width() / 2; // amount to add
int ah = _size_inc.height() / 2;
// don't let us move into a new size increment
2003-02-07 09:37:20 +00:00
if (mw + aw >= _size_inc.width()) aw = _size_inc.width() - mw - 1;
if (mh + ah >= _size_inc.height()) ah = _size_inc.height() - mh - 1;
w += aw;
h += ah;
// if this is a user-requested resize, then check against min/max sizes
// and aspect ratios
2002-12-04 00:26:45 +00:00
// smaller than min size or bigger than max size?
2003-02-10 00:21:09 +00:00
if (w > _max_size.width()) w = _max_size.width();
2003-02-07 09:37:20 +00:00
if (w < _min_size.width()) w = _min_size.width();
2003-02-10 00:21:09 +00:00
if (h > _max_size.height()) h = _max_size.height();
2003-02-07 09:37:20 +00:00
if (h < _min_size.height()) h = _min_size.height();
// adjust the height ot match the width for the aspect ratios
if (_min_ratio)
if (h * _min_ratio > w) h = static_cast<int>(w / _min_ratio);
if (_max_ratio)
if (h * _max_ratio < w) h = static_cast<int>(w / _max_ratio);
2002-12-04 00:26:45 +00:00
}
// keep to the increments
2003-02-07 09:37:20 +00:00
w /= _size_inc.width();
h /= _size_inc.height();
2002-12-04 00:26:45 +00:00
// you cannot resize to nothing
if (w < 1) w = 1;
if (h < 1) h = 1;
2002-12-04 00:26:45 +00:00
// store the logical size
2003-02-07 09:37:20 +00:00
_logical_size = otk::Size(w, h);
2002-12-04 00:26:45 +00:00
2003-02-07 09:37:20 +00:00
w *= _size_inc.width();
h *= _size_inc.height();
2002-12-04 00:26:45 +00:00
2003-02-07 09:37:20 +00:00
w += _base_size.width();
h += _base_size.height();
2003-01-04 07:24:40 +00:00
if (x == INT_MIN || y == INT_MIN) {
x = _area.x();
y = _area.y();
switch (anchor) {
case TopLeft:
break;
case TopRight:
x -= w - _area.width();
break;
case BottomLeft:
y -= h - _area.height();
break;
case BottomRight:
x -= w - _area.width();
y -= h - _area.height();
break;
}
2002-12-04 00:26:45 +00:00
}
2003-02-07 09:37:20 +00:00
_area = otk::Rect(_area.position(), otk::Size(w, h));
2003-01-04 07:24:40 +00:00
2003-01-13 09:04:57 +00:00
XResizeWindow(**otk::display, _window, w, h);
2002-12-04 00:26:45 +00:00
// resize the frame to match the request
frame->adjustSize();
internal_move(x, y);
2002-12-04 00:26:45 +00:00
}
void Client::move(int x, int y)
{
if (!(_functions & Func_Move)) return;
frame->frameGravity(x, y); // get the client's position based on x,y for the
// frame
internal_move(x, y);
}
void Client::internal_move(int x, int y)
{
2003-02-07 09:37:20 +00:00
_area = otk::Rect(otk::Point(x, y), _area.size());
2003-01-04 07:24:40 +00:00
2002-12-04 00:26:45 +00:00
// move the frame to be in the requested position
if (frame) { // this can be called while mapping, before frame exists
frame->adjustPosition();
// send synthetic configure notify (we don't need to if we aren't mapped
// yet)
XEvent event;
event.type = ConfigureNotify;
2003-01-13 09:04:57 +00:00
event.xconfigure.display = **otk::display;
event.xconfigure.event = _window;
event.xconfigure.window = _window;
// root window coords with border in mind
event.xconfigure.x = x - _border_width + frame->size().left;
event.xconfigure.y = y - _border_width + frame->size().top;
event.xconfigure.width = _area.width();
event.xconfigure.height = _area.height();
event.xconfigure.border_width = _border_width;
event.xconfigure.above = frame->plate();
event.xconfigure.override_redirect = False;
XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
StructureNotifyMask, &event);
#if 0//def DEBUG
printf("Sent synthetic ConfigureNotify %d,%d %d,%d to 0x%lx\n",
event.xconfigure.x, event.xconfigure.y, event.xconfigure.width,
event.xconfigure.height, event.xconfigure.window);
#endif
}
}
2002-12-04 00:26:45 +00:00
2003-01-11 19:42:43 +00:00
void Client::close()
{
XEvent ce;
if (!(_functions & Func_Close)) return;
// XXX: itd be cool to do timeouts and shit here for killing the client's
// process off
2003-01-04 07:26:57 +00:00
// like... if the window is around after 5 seconds, then the close button
// turns a nice red, and if this function is called again, the client is
// explicitly killed.
ce.xclient.type = ClientMessage;
2003-01-16 08:44:52 +00:00
ce.xclient.message_type = otk::Property::atoms.wm_protocols;
2003-01-13 09:04:57 +00:00
ce.xclient.display = **otk::display;
ce.xclient.window = _window;
ce.xclient.format = 32;
2003-01-16 08:44:52 +00:00
ce.xclient.data.l[0] = otk::Property::atoms.wm_delete_window;
ce.xclient.data.l[1] = CurrentTime;
ce.xclient.data.l[2] = 0l;
ce.xclient.data.l[3] = 0l;
ce.xclient.data.l[4] = 0l;
2003-01-13 09:04:57 +00:00
XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
}
2003-01-11 19:42:43 +00:00
void Client::changeState()
2003-01-03 23:51:06 +00:00
{
unsigned long state[2];
state[0] = _wmstate;
state[1] = None;
2003-01-16 08:44:52 +00:00
otk::Property::set(_window, otk::Property::atoms.wm_state,
otk::Property::atoms.wm_state, state, 2);
2003-01-03 23:51:06 +00:00
Atom netstate[10];
int num = 0;
if (_modal)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_modal;
2003-01-03 23:51:06 +00:00
if (_shaded)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_shaded;
2003-01-03 23:51:06 +00:00
if (_iconic)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_hidden;
2003-01-03 23:51:06 +00:00
if (_skip_taskbar)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_skip_taskbar;
2003-01-03 23:51:06 +00:00
if (_skip_pager)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_skip_pager;
2003-01-03 23:51:06 +00:00
if (_fullscreen)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_fullscreen;
2003-01-03 23:51:06 +00:00
if (_max_vert)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_maximized_vert;
2003-01-03 23:51:06 +00:00
if (_max_horz)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_maximized_horz;
2003-01-03 23:51:06 +00:00
if (_above)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_above;
2003-01-03 23:51:06 +00:00
if (_below)
2003-01-16 08:44:52 +00:00
netstate[num++] = otk::Property::atoms.net_wm_state_below;
otk::Property::set(_window, otk::Property::atoms.net_wm_state,
otk::Property::atoms.atom, netstate, num);
calcLayer();
if (frame)
frame->adjustState();
2003-01-03 23:51:06 +00:00
}
2003-01-04 08:41:42 +00:00
void Client::changeAllowedActions(void)
{
Atom actions[9];
int num = 0;
actions[num++] = otk::Property::atoms.net_wm_action_change_desktop;
if (_functions & Func_Shade)
actions[num++] = otk::Property::atoms.net_wm_action_shade;
if (_functions & Func_Close)
actions[num++] = otk::Property::atoms.net_wm_action_close;
if (_functions & Func_Move)
actions[num++] = otk::Property::atoms.net_wm_action_move;
if (_functions & Func_Iconify)
actions[num++] = otk::Property::atoms.net_wm_action_minimize;
if (_functions & Func_Resize)
actions[num++] = otk::Property::atoms.net_wm_action_resize;
if (_functions & Func_Fullscreen)
actions[num++] = otk::Property::atoms.net_wm_action_fullscreen;
if (_functions & Func_Maximize) {
actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz;
actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert;
}
otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions,
otk::Property::atoms.atom, actions, num);
// make sure the window isn't breaking any rules now
if (!(_functions & Func_Shade) && _shaded)
if (frame) shade(false);
else _shaded = false;
if (!(_functions & Func_Iconify) && _iconic)
if (frame) setDesktop(openbox->screen(_screen)->desktop());
else _iconic = false;
if (!(_functions & Func_Fullscreen) && _fullscreen)
if (frame) fullscreen(false);
else _fullscreen = false;
if (!(_functions & Func_Maximize) && (_max_horz || _max_vert))
if (frame) maximize(false, 0);
else _max_vert = _max_horz = false;
}
void Client::remaximize()
{
int dir;
if (_max_horz && _max_vert)
dir = 0;
else if (_max_horz)
dir = 1;
else if (_max_vert)
dir = 2;
else
return; // not maximized
_max_horz = _max_vert = false;
maximize(true, dir, false);
}
void Client::applyStartupState()
{
// these are in a carefully crafted order..
if (_iconic) {
_iconic = false;
iconify(true);
}
if (_fullscreen) {
_fullscreen = false;
2003-02-01 05:26:06 +00:00
fullscreen(true, false);
}
if (_shaded) {
_shaded = false;
shade(true);
}
if (_urgent)
fireUrgent();
2003-02-01 05:26:06 +00:00
if (_max_vert && _max_horz) {
_max_vert = _max_horz = false;
maximize(true, 0, false);
} else if (_max_vert) {
_max_vert = false;
maximize(true, 2, false);
} else if (_max_horz) {
_max_horz = false;
maximize(true, 1, false);
}
if (_skip_taskbar); // nothing to do for this
if (_skip_pager); // nothing to do for this
if (_modal); // nothing to do for this
if (_above); // nothing to do for this
if (_below); // nothing to do for this
}
void Client::fireUrgent()
{
2003-01-24 04:19:50 +00:00
// call the python UrgentWindow callbacks
2003-01-25 16:36:55 +00:00
EventData data(_screen, this, EventAction::UrgentWindow, 0);
openbox->bindings()->fireEvent(&data);
}
2003-01-11 19:42:43 +00:00
void Client::shade(bool shade)
2003-01-03 23:51:06 +00:00
{
if (!(_functions & Func_Shade) || // can't
_shaded == shade) return; // already done
2003-01-03 23:51:06 +00:00
// when we're iconic, don't change the wmstate
if (!_iconic)
_wmstate = shade ? IconicState : NormalState;
2003-01-03 23:51:06 +00:00
_shaded = shade;
changeState();
frame->adjustSize();
}
2003-02-01 05:26:06 +00:00
void Client::maximize(bool max, int dir, bool savearea)
{
assert(dir == 0 || dir == 1 || dir == 2);
if (!(_functions & Func_Maximize)) return; // can't
// check if already done
if (max) {
if (dir == 0 && _max_horz && _max_vert) return;
if (dir == 1 && _max_horz) return;
if (dir == 2 && _max_vert) return;
} else {
if (dir == 0 && !_max_horz && !_max_vert) return;
if (dir == 1 && !_max_horz) return;
if (dir == 2 && !_max_vert) return;
}
const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
2003-02-07 09:37:20 +00:00
int x = frame->area().x(), y = frame->area().y(),
2003-02-03 08:19:48 +00:00
w = _area.width(), h = _area.height();
2003-02-01 05:26:06 +00:00
if (max) {
if (savearea) {
long dimensions[4];
long *readdim;
unsigned long n = 4;
2003-02-01 05:26:06 +00:00
dimensions[0] = x;
dimensions[1] = y;
dimensions[2] = w;
dimensions[3] = h;
// get the property off the window and use it for the dimentions we are
// already maxed on
if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
otk::Property::atoms.cardinal, &n,
(long unsigned**) &readdim)) {
if (n >= 4) {
if (_max_horz) {
dimensions[0] = readdim[0];
dimensions[2] = readdim[2];
}
if (_max_vert) {
dimensions[1] = readdim[1];
dimensions[3] = readdim[3];
}
}
delete readdim;
}
2003-02-01 05:26:06 +00:00
otk::Property::set(_window, otk::Property::atoms.openbox_premax,
otk::Property::atoms.cardinal,
(long unsigned*)dimensions, 4);
}
if (dir == 0 || dir == 1) { // horz
x = a.x();
w = a.width();
}
if (dir == 0 || dir == 2) { // vert
2003-02-03 08:19:48 +00:00
y = a.y();
2003-02-01 05:26:06 +00:00
h = a.height() - frame->size().top - frame->size().bottom;
}
} else {
long *dimensions;
long unsigned n = 4;
if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
otk::Property::atoms.cardinal, &n,
(long unsigned**) &dimensions)) {
if (n >= 4) {
if (dir == 0 || dir == 1) { // horz
x = (signed int)dimensions[0];
w = (signed int)dimensions[2];
}
if (dir == 0 || dir == 2) { // vert
y = (signed int)dimensions[1];
h = (signed int)dimensions[3];
}
}
delete dimensions;
} else {
// pick some fallbacks...
if (dir == 0 || dir == 1) { // horz
x = a.x() + a.width() / 4;
w = a.width() / 2;
}
if (dir == 0 || dir == 2) { // vert
y = a.y() + a.height() / 4;
h = a.height() / 2;
}
}
}
if (dir == 0 || dir == 1) // horz
_max_horz = max;
if (dir == 0 || dir == 2) // vert
_max_vert = max;
2003-02-03 08:19:48 +00:00
if (!_max_horz && !_max_vert)
otk::Property::erase(_window, otk::Property::atoms.openbox_premax);
2003-02-01 05:26:06 +00:00
changeState(); // change the state hints on the client
2003-02-03 08:19:48 +00:00
frame->frameGravity(x, y); // figure out where the client should be going
2003-02-01 05:26:06 +00:00
internal_resize(TopLeft, w, h, true, x, y);
}
void Client::fullscreen(bool fs, bool savearea)
{
static FunctionFlags saved_func;
static DecorationFlags saved_decor;
if (!(_functions & Func_Fullscreen) || // can't
_fullscreen == fs) return; // already done
_fullscreen = fs;
changeState(); // change the state hints on the client
2003-02-01 05:26:06 +00:00
int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height();
if (fs) {
// save the functions and remove them
saved_func = _functions;
_functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify);
// save the decorations and remove them
saved_decor = _decorations;
_decorations = 0;
2003-02-01 05:26:06 +00:00
if (savearea) {
long dimensions[4];
dimensions[0] = _area.x();
dimensions[1] = _area.y();
dimensions[2] = _area.width();
dimensions[3] = _area.height();
otk::Property::set(_window, otk::Property::atoms.openbox_premax,
otk::Property::atoms.cardinal,
(long unsigned*)dimensions, 4);
}
const otk::ScreenInfo *info = otk::display->screenInfo(_screen);
2003-02-01 05:26:06 +00:00
x = 0;
y = 0;
2003-02-07 09:37:20 +00:00
w = info->size().width();
h = info->size().height();
} else {
_functions = saved_func;
_decorations = saved_decor;
2003-02-01 05:26:06 +00:00
long *dimensions;
long unsigned n = 4;
if (otk::Property::get(_window, otk::Property::atoms.openbox_premax,
otk::Property::atoms.cardinal, &n,
(long unsigned**) &dimensions)) {
if (n >= 4) {
x = dimensions[0];
y = dimensions[1];
w = dimensions[2];
h = dimensions[3];
}
delete dimensions;
} else {
// pick some fallbacks...
const otk::Rect &a = openbox->screen(_screen)->area(_desktop);
2003-02-01 05:26:06 +00:00
x = a.x() + a.width() / 4;
y = a.y() + a.height() / 4;
w = a.width() / 2;
h = a.height() / 2;
}
}
changeAllowedActions(); // based on the new _functions
2003-02-01 05:26:06 +00:00
// when fullscreening, don't obey things like increments, fill the screen
internal_resize(TopLeft, w, h, !fs, x, y);
// raise (back) into our stacking layer
openbox->screen(_screen)->raiseWindow(this);
// try focus us when we go into fullscreen mode
if (fs) focus();
}
void Client::iconify(bool iconic, bool curdesk)
{
if (_iconic == iconic) return; // nothing to do
_iconic = iconic;
if (_iconic) {
_wmstate = IconicState;
ignore_unmaps++;
// we unmap the client itself so that we can get MapRequest events, and
// because the ICCCM tells us to!
XUnmapWindow(**otk::display, _window);
} else {
if (curdesk)
setDesktop(openbox->screen(_screen)->desktop());
_wmstate = NormalState;
XMapWindow(**otk::display, _window);
}
changeState();
showhide();
openbox->screen(_screen)->updateStruts();
}
void Client::disableDecorations(DecorationFlags flags)
{
_disabled_decorations = flags;
setupDecorAndFunctions();
}
void Client::installColormap(bool install) const
{
XWindowAttributes wa;
if (XGetWindowAttributes(**otk::display, _window, &wa)) {
if (install)
XInstallColormap(**otk::display, wa.colormap);
else
XUninstallColormap(**otk::display, wa.colormap);
}
}
2003-02-10 03:47:54 +00:00
// recursively searches the client 'tree' for a modal client, always skips the
// topmost node (the window you're starting with)
Client *Client::searchModalTree(Client *node, Client *skip)
{
List::const_iterator it, end = node->_transients.end();
Client *ret;
for (it = node->_transients.begin(); it != end; ++it) {
if (*it == skip) continue; // circular?
if ((ret = searchModalTree(*it, skip))) return ret; // got one
2003-02-10 03:50:47 +00:00
if ((*it)->_modal) return *it; // got one
2003-02-10 03:47:54 +00:00
}
return 0;
}
Client *Client::findModalChild()
{
return searchModalTree(this, this);
}
bool Client::focus()
{
// if we have a modal child, then focus it, not us
2003-02-10 03:47:54 +00:00
Client *c = findModalChild();
if (c) return c->focus();
// won't try focus if the client doesn't want it, or if the window isn't
// visible on the screen
2003-02-07 09:37:20 +00:00
if (!(frame->visible() && (_can_focus || _focus_notify))) return false;
if (_focused) return true;
// do a check to see if the window has already been unmapped or destroyed
// do this intelligently while watching out for unmaps we've generated
// (ignore_unmaps > 0)
XEvent ev;
if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) {
XPutBackEvent(**otk::display, &ev);
return false;
}
while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) {
if (ignore_unmaps) {
2003-01-24 00:02:24 +00:00
unmapHandler(ev.xunmap);
} else {
XPutBackEvent(**otk::display, &ev);
return false;
}
}
if (_can_focus)
2003-01-13 09:04:57 +00:00
XSetInputFocus(**otk::display, _window,
RevertToNone, CurrentTime);
if (_focus_notify) {
XEvent ce;
ce.xclient.type = ClientMessage;
2003-01-16 08:44:52 +00:00
ce.xclient.message_type = otk::Property::atoms.wm_protocols;
2003-01-13 09:04:57 +00:00
ce.xclient.display = **otk::display;
ce.xclient.window = _window;
ce.xclient.format = 32;
2003-01-16 08:44:52 +00:00
ce.xclient.data.l[0] = otk::Property::atoms.wm_take_focus;
ce.xclient.data.l[1] = openbox->lastTime();
ce.xclient.data.l[2] = 0l;
ce.xclient.data.l[3] = 0l;
ce.xclient.data.l[4] = 0l;
2003-01-13 09:04:57 +00:00
XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
}
2003-01-29 00:45:28 +00:00
XSync(**otk::display, False);
return true;
}
2003-01-11 19:42:43 +00:00
void Client::unfocus() const
2002-12-26 18:15:23 +00:00
{
if (!_focused) return;
assert(openbox->focusedClient() == this);
openbox->setFocusedClient(0);
2002-12-26 18:15:23 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::focusHandler(const XFocusChangeEvent &e)
{
#ifdef DEBUG
// printf("FocusIn for 0x%lx\n", e.window);
#endif // DEBUG
2003-01-11 19:42:43 +00:00
otk::EventHandler::focusHandler(e);
_focused = true;
frame->adjustFocus();
openbox->setFocusedClient(this);
}
2003-01-11 19:42:43 +00:00
void Client::unfocusHandler(const XFocusChangeEvent &e)
{
#ifdef DEBUG
// printf("FocusOut for 0x%lx\n", e.window);
#endif // DEBUG
2003-01-11 19:42:43 +00:00
otk::EventHandler::unfocusHandler(e);
_focused = false;
frame->adjustFocus();
if (openbox->focusedClient() == this)
openbox->setFocusedClient(0);
}
void Client::configureRequestHandler(const XConfigureRequestEvent &ec)
2002-12-04 00:26:45 +00:00
{
#ifdef DEBUG
printf("ConfigureRequest for 0x%lx\n", ec.window);
#endif // DEBUG
otk::EventHandler::configureRequestHandler(ec);
2003-02-07 09:37:20 +00:00
// compress these
XConfigureRequestEvent e = ec;
2003-02-07 09:37:20 +00:00
XEvent ev;
while (XCheckTypedWindowEvent(**otk::display, window(), ConfigureRequest,
&ev)) {
// XXX if this causes bad things.. we can compress config req's with the
// same mask.
2003-02-07 09:37:20 +00:00
e.value_mask |= ev.xconfigurerequest.value_mask;
if (ev.xconfigurerequest.value_mask & CWX)
e.x = ev.xconfigurerequest.x;
if (ev.xconfigurerequest.value_mask & CWY)
e.y = ev.xconfigurerequest.y;
if (ev.xconfigurerequest.value_mask & CWWidth)
e.width = ev.xconfigurerequest.width;
if (ev.xconfigurerequest.value_mask & CWHeight)
e.height = ev.xconfigurerequest.height;
if (ev.xconfigurerequest.value_mask & CWBorderWidth)
e.border_width = ev.xconfigurerequest.border_width;
if (ev.xconfigurerequest.value_mask & CWStackMode)
e.detail = ev.xconfigurerequest.detail;
}
// if we are iconic (or shaded (fvwm does this)) ignore the event
if (_iconic || _shaded) return;
2002-12-04 00:26:45 +00:00
if (e.value_mask & CWBorderWidth)
_border_width = e.border_width;
2003-01-03 18:21:28 +00:00
// resize, then move, as specified in the EWMH section 7.7
2002-12-04 00:26:45 +00:00
if (e.value_mask & (CWWidth | CWHeight)) {
int w = (e.value_mask & CWWidth) ? e.width : _area.width();
int h = (e.value_mask & CWHeight) ? e.height : _area.height();
Corner corner;
switch (_gravity) {
case NorthEastGravity:
case EastGravity:
corner = TopRight;
break;
case SouthWestGravity:
case SouthGravity:
corner = BottomLeft;
break;
case SouthEastGravity:
corner = BottomRight;
break;
default: // NorthWest, Static, etc
corner = TopLeft;
}
2003-01-04 07:24:40 +00:00
// if moving AND resizing ...
if (e.value_mask & (CWX | CWY)) {
int x = (e.value_mask & CWX) ? e.x : _area.x();
int y = (e.value_mask & CWY) ? e.y : _area.y();
internal_resize(corner, w, h, false, x, y);
2003-01-04 07:24:40 +00:00
} else // if JUST resizing...
internal_resize(corner, w, h, false);
2003-01-04 07:24:40 +00:00
} else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
2002-12-04 00:26:45 +00:00
int x = (e.value_mask & CWX) ? e.x : _area.x();
int y = (e.value_mask & CWY) ? e.y : _area.y();
internal_move(x, y);
2002-12-04 00:26:45 +00:00
}
if (e.value_mask & CWStackMode) {
switch (e.detail) {
case Below:
case BottomIf:
openbox->screen(_screen)->lowerWindow(this);
2002-12-04 00:26:45 +00:00
break;
case Above:
case TopIf:
default:
openbox->screen(_screen)->raiseWindow(this);
2002-12-04 00:26:45 +00:00
break;
}
}
}
2003-01-11 19:42:43 +00:00
void Client::unmapHandler(const XUnmapEvent &e)
2002-12-04 01:04:31 +00:00
{
if (ignore_unmaps) {
2002-12-04 01:04:31 +00:00
#ifdef DEBUG
// printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
2002-12-04 01:04:31 +00:00
#endif // DEBUG
2002-12-04 01:29:01 +00:00
ignore_unmaps--;
return;
}
#ifdef DEBUG
printf("UnmapNotify for 0x%lx\n", e.window);
#endif // DEBUG
2003-01-11 19:42:43 +00:00
otk::EventHandler::unmapHandler(e);
2002-12-04 01:04:31 +00:00
// this deletes us etc
openbox->screen(_screen)->unmanageWindow(this);
2002-12-04 01:04:31 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::destroyHandler(const XDestroyWindowEvent &e)
2002-12-04 01:04:31 +00:00
{
#ifdef DEBUG
printf("DestroyNotify for 0x%lx\n", e.window);
#endif // DEBUG
2003-01-11 19:42:43 +00:00
otk::EventHandler::destroyHandler(e);
2002-12-04 01:04:31 +00:00
// this deletes us etc
openbox->screen(_screen)->unmanageWindow(this);
2002-12-04 01:04:31 +00:00
}
2003-01-11 19:42:43 +00:00
void Client::reparentHandler(const XReparentEvent &e)
2002-12-27 08:52:16 +00:00
{
2002-12-27 10:07:57 +00:00
// this is when the client is first taken captive in the frame
2003-01-06 04:58:07 +00:00
if (e.parent == frame->plate()) return;
2002-12-27 10:07:57 +00:00
2002-12-27 08:52:16 +00:00
#ifdef DEBUG
printf("ReparentNotify for 0x%lx\n", e.window);
#endif // DEBUG
2003-01-11 19:42:43 +00:00
otk::EventHandler::reparentHandler(e);
2002-12-27 08:52:16 +00:00
/*
This event is quite rare and is usually handled in unmapHandler.
However, if the window is unmapped when the reparent event occurs,
the window manager never sees it because an unmap event is not sent
to an already unmapped window.
*/
// we don't want the reparent event, put it back on the stack for the X
// server to deal with after we unmanage the window
XEvent ev;
ev.xreparent = e;
2003-01-13 09:04:57 +00:00
XPutBackEvent(**otk::display, &ev);
2002-12-27 08:52:16 +00:00
// this deletes us etc
openbox->screen(_screen)->unmanageWindow(this);
2002-12-27 08:52:16 +00:00
}
void Client::mapRequestHandler(const XMapRequestEvent &e)
{
#ifdef DEBUG
printf("MapRequest for already managed 0x%lx\n", e.window);
#endif // DEBUG
assert(_iconic); // we shouldn't be able to get this unless we're iconic
// move to the current desktop (uniconify)
iconify(false);
// XXX: should we focus/raise the window? (basically a net_wm_active_window)
}
2002-11-06 07:01:34 +00:00
}