openbox/src/client.cc

1033 lines
28 KiB
C++
Raw Normal View History

2002-11-06 07:01:34 +00:00
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif
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-10 10:22:47 +00:00
#include "bbscreen.hh"
2002-11-06 07:01:34 +00:00
#include "openbox.hh"
#include "otk/display.hh"
#include "otk/property.hh"
extern "C" {
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <assert.h>
#include "gettext.h"
#define _(str) gettext(str)
}
namespace ob {
OBClient::OBClient(int screen, Window window)
: otk::OtkEventHandler(),
2002-12-18 11:34:29 +00:00
OBWidget(OBWidget::Type_Client),
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!!
// the state is kinda assumed to be normal. is this right? XXX
_wmstate = NormalState;
2002-11-06 11:31:50 +00:00
// no default decors or functions, each has to be enabled
_decorations = _functions = 0;
// start unfocused
_focused = false;
getArea();
getDesktop();
getType();
// set the decorations and functions
switch (_type) {
case Type_Normal:
// normal windows retain all of the possible decorations and
// functionality
_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
Decor_Iconify | Decor_Maximize;
_functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
case Type_Dialog:
// dialogs cannot be maximized
_decorations &= ~Decor_Maximize;
_functions &= ~Func_Maximize;
break;
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;
}
getMwmHints(); // this fucks (in good ways) with the decors and functions
getState();
getShaped();
2002-11-06 10:05:56 +00:00
updateProtocols();
updateNormalHints();
updateWMHints();
// XXX: updateTransientFor();
updateTitle();
2002-11-07 08:22:27 +00:00
updateIconTitle();
updateClass();
/*
#ifdef DEBUG
printf("Mapped window: 0x%lx\n"
2002-11-06 09:12:13 +00:00
" title: \t%s\t icon title: \t%s\n"
" app name: \t%s\t\t class: \t%s\n"
" position: \t%d, %d\t\t size: \t%d, %d\n"
" desktop: \t%lu\t\t group: \t0x%lx\n"
" type: \t%d\t\t min size \t%d, %d\n"
" base size \t%d, %d\t\t max size \t%d, %d\n"
" size incr \t%d, %d\t\t gravity \t%d\n"
" wm state \t%ld\t\t can be focused:\t%s\n"
" notify focus: \t%s\t\t urgent: \t%s\n"
" shaped: \t%s\t\t modal: \t%s\n"
" shaded: \t%s\t\t iconic: \t%s\n"
" vert maximized:\t%s\t\t horz maximized:\t%s\n"
" fullscreen: \t%s\t\t floating: \t%s\n"
" requested pos: \t%s\n",
_window,
_title.c_str(),
_icon_title.c_str(),
_app_name.c_str(),
_app_class.c_str(),
_area.x(), _area.y(),
_area.width(), _area.height(),
_desktop,
_group,
_type,
_min_x, _min_y,
_base_x, _base_y,
_max_x, _max_y,
_inc_x, _inc_y,
_gravity,
_wmstate,
_can_focus ? "yes" : "no",
_focus_notify ? "yes" : "no",
_urgent ? "yes" : "no",
_shaped ? "yes" : "no",
_modal ? "yes" : "no",
_shaded ? "yes" : "no",
_iconic ? "yes" : "no",
_max_vert ? "yes" : "no",
_max_horz ? "yes" : "no",
_fullscreen ? "yes" : "no",
_floating ? "yes" : "no",
_positioned ? "yes" : "no");
#endif
*/
2002-11-06 07:01:34 +00:00
}
2002-11-06 07:01:34 +00:00
OBClient::~OBClient()
{
const otk::OBProperty *property = Openbox::instance->property();
// these values should not be persisted across a window unmapping/mapping
property->erase(_window, otk::OBProperty::net_wm_desktop);
property->erase(_window, otk::OBProperty::net_wm_state);
}
void OBClient::getDesktop()
{
const otk::OBProperty *property = Openbox::instance->property();
// defaults to the current desktop
_desktop = 0; // XXX: change this to the current desktop!
property->get(_window, otk::OBProperty::net_wm_desktop,
otk::OBProperty::Atom_Cardinal,
&_desktop);
}
void OBClient::getType()
{
const otk::OBProperty *property = Openbox::instance->property();
_type = (WindowType) -1;
unsigned long *val;
unsigned long num = (unsigned) -1;
if (property->get(_window, otk::OBProperty::net_wm_window_type,
otk::OBProperty::Atom_Atom,
&num, &val)) {
// use the first value that we know about in the array
for (unsigned long i = 0; i < num; ++i) {
if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_desktop))
_type = Type_Desktop;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_dock))
_type = Type_Dock;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_toolbar))
_type = Type_Toolbar;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_menu))
_type = Type_Menu;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_utility))
_type = Type_Utility;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_splash))
_type = Type_Splash;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_dialog))
_type = Type_Dialog;
else if (val[i] ==
property->atom(otk::OBProperty::net_wm_window_type_normal))
_type = Type_Normal;
// else if (val[i] ==
// property->atom(otk::OBProperty::kde_net_wm_window_type_override))
// mwm_decorations = 0; // prevent this window from getting any decor
// XXX: make this work again
}
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.
*/
// XXX: make this code work!
//if (isTransient())
// _type = Type_Dialog;
//else
_type = Type_Normal;
}
2002-11-06 11:31:50 +00:00
}
void OBClient::getMwmHints()
2002-11-06 11:31:50 +00:00
{
const otk::OBProperty *property = Openbox::instance->property();
unsigned long num;
MwmHints *hints;
num = MwmHints::elements;
if (!property->get(_window, otk::OBProperty::motif_wm_hints,
otk::OBProperty::motif_wm_hints, &num,
(unsigned long **)&hints))
return;
if (num < MwmHints::elements) {
delete [] hints;
return;
}
// retrieved the hints
// Mwm Hints are applied subtractively to what has already been chosen for
// decor and functionality
if (hints->flags & MwmFlag_Decorations) {
2002-11-06 11:31:50 +00:00
if (! (hints->decorations & MwmDecor_All)) {
if (! (hints->decorations & MwmDecor_Border))
_decorations &= ~Decor_Border;
if (! (hints->decorations & MwmDecor_Handle))
_decorations &= ~Decor_Handle;
if (! (hints->decorations & MwmDecor_Title))
_decorations &= ~Decor_Titlebar;
if (! (hints->decorations & MwmDecor_Iconify))
_decorations &= ~Decor_Iconify;
if (! (hints->decorations & MwmDecor_Maximize))
_decorations &= ~Decor_Maximize;
}
}
if (hints->flags & MwmFlag_Functions) {
2002-11-06 11:31:50 +00:00
if (! (hints->functions & MwmFunc_All)) {
if (! (hints->functions & MwmFunc_Resize))
_functions &= ~Func_Resize;
2002-11-06 11:31:50 +00:00
if (! (hints->functions & MwmFunc_Move))
_functions &= ~Func_Move;
2002-11-06 11:31:50 +00:00
if (! (hints->functions & MwmFunc_Iconify))
_functions &= ~Func_Iconify;
2002-11-06 11:31:50 +00:00
if (! (hints->functions & MwmFunc_Maximize))
_functions &= ~Func_Maximize;
//if (! (hints->functions & MwmFunc_Close))
// _functions &= ~Func_Close;
2002-11-06 11:31:50 +00:00
}
}
delete [] hints;
}
void OBClient::getArea()
{
XWindowAttributes wattrib;
Status ret;
ret = XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib);
assert(ret != BadWindow);
_area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
2002-11-06 10:05:56 +00:00
_border_width = wattrib.border_width;
}
void OBClient::getState()
{
const otk::OBProperty *property = Openbox::instance->property();
_modal = _shaded = _max_horz = _max_vert = _fullscreen = _floating = false;
unsigned long *state;
unsigned long num = (unsigned) -1;
if (property->get(_window, otk::OBProperty::net_wm_state,
otk::OBProperty::Atom_Atom, &num, &state)) {
for (unsigned long i = 0; i < num; ++i) {
if (state[i] == property->atom(otk::OBProperty::net_wm_state_modal))
_modal = true;
else if (state[i] ==
property->atom(otk::OBProperty::net_wm_state_shaded))
_shaded = true;
else if (state[i] ==
property->atom(otk::OBProperty::net_wm_state_fullscreen))
_fullscreen = true;
else if (state[i] ==
property->atom(otk::OBProperty::net_wm_state_maximized_vert))
_max_vert = true;
else if (state[i] ==
property->atom(otk::OBProperty::net_wm_state_maximized_horz))
_max_horz = true;
}
delete [] state;
}
}
void OBClient::getShaped()
{
_shaped = false;
#ifdef SHAPE
if (otk::OBDisplay::shape()) {
int foo;
unsigned int ufoo;
2002-11-10 05:54:12 +00:00
int s;
2002-11-10 05:54:12 +00:00
XShapeSelectInput(otk::OBDisplay::display, _window, ShapeNotifyMask);
XShapeQueryExtents(otk::OBDisplay::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
}
2002-11-06 11:31:50 +00:00
void OBClient::updateProtocols()
{
2002-11-06 10:05:56 +00:00
const otk::OBProperty *property = Openbox::instance->property();
Atom *proto;
int num_return = 0;
_focus_notify = false;
2002-11-07 08:22:27 +00:00
_decorations &= ~Decor_Close;
_functions &= ~Func_Close;
2002-11-06 10:05:56 +00:00
if (XGetWMProtocols(otk::OBDisplay::display, _window, &proto, &num_return)) {
for (int i = 0; i < num_return; ++i) {
if (proto[i] == property->atom(otk::OBProperty::wm_delete_window)) {
_decorations |= Decor_Close;
_functions |= Func_Close;
// XXX: update the decor?
2002-11-06 10:05:56 +00:00
} else if (proto[i] == property->atom(otk::OBProperty::wm_take_focus))
// if this protocol is requested, then the window will be notified
// by the window manager whenever it receives focus
_focus_notify = true;
}
XFree(proto);
}
}
2002-11-06 07:01:34 +00:00
void OBClient::updateNormalHints()
{
XSizeHints size;
long ret;
int oldgravity = _gravity;
2002-11-06 07:01:34 +00:00
// defaults
_gravity = NorthWestGravity;
2002-12-04 00:26:45 +00:00
_size_inc.setPoint(1, 1);
_base_size.setPoint(0, 0);
_min_size.setPoint(0, 0);
_max_size.setPoint(INT_MAX, INT_MAX);
2002-11-06 07:01:34 +00:00
2002-11-06 11:31:50 +00:00
// XXX: might want to cancel any interactive resizing of the window at this
// point..
2002-11-06 07:01:34 +00:00
// get the hints from the window
if (XGetWMNormalHints(otk::OBDisplay::display, _window, &size, &ret)) {
_positioned = (size.flags & (PPosition|USPosition));
2002-11-06 07:01:34 +00:00
if (size.flags & PWinGravity)
_gravity = size.win_gravity;
2002-12-04 00:26:45 +00:00
if (size.flags & PMinSize)
_min_size.setPoint(size.min_width, size.min_height);
2002-12-04 00:26:45 +00:00
if (size.flags & PMaxSize)
_max_size.setPoint(size.max_width, size.max_height);
2002-12-04 00:26:45 +00:00
if (size.flags & PBaseSize)
_base_size.setPoint(size.base_width, size.base_height);
2002-12-04 00:26:45 +00:00
if (size.flags & PResizeInc)
_size_inc.setPoint(size.width_inc, size.height_inc);
2002-11-06 07:01:34 +00:00
}
// 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
int x, y;
frame->frameGravity(x, y);
_area.setPos(x, y);
}
2002-11-06 07:01:34 +00:00
}
void OBClient::updateWMHints()
{
XWMHints *hints;
// assume a window takes input if it doesnt specify
_can_focus = true;
_urgent = false;
if ((hints = XGetWMHints(otk::OBDisplay::display, _window)) != NULL) {
if (hints->flags & InputHint)
_can_focus = hints->input;
2002-11-06 07:01:34 +00:00
if (hints->flags & XUrgencyHint)
_urgent = 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);
}
}
void OBClient::updateTitle()
{
const otk::OBProperty *property = Openbox::instance->property();
_title = "";
// try netwm
if (! property->get(_window, otk::OBProperty::net_wm_name,
otk::OBProperty::utf8, &_title)) {
// try old x stuff
property->get(_window, otk::OBProperty::wm_name,
otk::OBProperty::ascii, &_title);
}
if (_title.empty())
_title = _("Unnamed Window");
if (frame)
frame->setTitle(_title);
2002-11-06 07:01:34 +00:00
}
2002-11-07 08:22:27 +00:00
void OBClient::updateIconTitle()
{
const otk::OBProperty *property = Openbox::instance->property();
_icon_title = "";
// try netwm
if (! property->get(_window, otk::OBProperty::net_wm_icon_name,
otk::OBProperty::utf8, &_icon_title)) {
// try old x stuff
property->get(_window, otk::OBProperty::wm_icon_name,
otk::OBProperty::ascii, &_icon_title);
}
if (_title.empty())
_icon_title = _("Unnamed Window");
}
2002-11-06 07:01:34 +00:00
void OBClient::updateClass()
{
const otk::OBProperty *property = Openbox::instance->property();
// set the defaults
_app_name = _app_class = "";
otk::OBProperty::StringVect v;
unsigned long num = 2;
if (! property->get(_window, otk::OBProperty::wm_class,
otk::OBProperty::ascii, &num, &v))
return;
if (num > 0) _app_name = v[0];
if (num > 1) _app_class = v[1];
}
void OBClient::propertyHandler(const XPropertyEvent &e)
2002-11-06 07:01:34 +00:00
{
otk::OtkEventHandler::propertyHandler(e);
2002-11-06 07:01:34 +00:00
const otk::OBProperty *property = Openbox::instance->property();
2002-12-02 22:32:38 +00:00
// compress changes to a single property into a single change
XEvent ce;
2002-12-02 22:36:43 +00:00
while (XCheckTypedEvent(otk::OBDisplay::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) {
XPutBackEvent(otk::OBDisplay::display, &ce);
break;
}
}
2002-11-06 07:01:34 +00:00
if (e.atom == XA_WM_NORMAL_HINTS)
updateNormalHints();
else if (e.atom == XA_WM_HINTS)
updateWMHints();
else if (e.atom == property->atom(otk::OBProperty::net_wm_name) ||
2002-11-07 08:22:27 +00:00
e.atom == property->atom(otk::OBProperty::wm_name))
2002-11-06 07:01:34 +00:00
updateTitle();
2002-11-07 08:22:27 +00:00
else if (e.atom == property->atom(otk::OBProperty::net_wm_icon_name) ||
e.atom == property->atom(otk::OBProperty::wm_icon_name))
updateIconTitle();
2002-11-06 07:01:34 +00:00
else if (e.atom == property->atom(otk::OBProperty::wm_class))
updateClass();
2002-11-06 10:05:56 +00:00
else if (e.atom == property->atom(otk::OBProperty::wm_protocols))
updateProtocols();
// XXX: transient for hint
2002-11-06 10:05:56 +00:00
// XXX: strut hint
2002-11-06 07:01:34 +00:00
}
void OBClient::setWMState(long state)
{
if (state == _wmstate) return; // no change
2002-11-06 07:01:34 +00:00
switch (state) {
case IconicState:
// XXX: cause it to iconify
break;
case NormalState:
// XXX: cause it to uniconify
break;
}
_wmstate = state;
2002-11-06 07:01:34 +00:00
}
void OBClient::setDesktop(long target)
{
assert(target >= 0);
//assert(target == 0xffffffff || target < MAX);
// XXX: move the window to the new desktop
_desktop = target;
}
void OBClient::setState(StateAction action, long data1, long data2)
{
const otk::OBProperty *property = Openbox::instance->property();
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) {
if (state == property->atom(otk::OBProperty::net_wm_state_modal))
action = _modal ? State_Remove : State_Add;
else if (state ==
property->atom(otk::OBProperty::net_wm_state_maximized_vert))
action = _max_vert ? State_Remove : State_Add;
else if (state ==
property->atom(otk::OBProperty::net_wm_state_maximized_horz))
action = _max_horz ? State_Remove : State_Add;
else if (state == property->atom(otk::OBProperty::net_wm_state_shaded))
action = _shaded ? State_Remove : State_Add;
else if (state ==
property->atom(otk::OBProperty::net_wm_state_fullscreen))
action = _fullscreen ? State_Remove : State_Add;
else if (state == property->atom(otk::OBProperty::net_wm_state_floating))
action = _floating ? State_Remove : State_Add;
}
if (action == State_Add) {
if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
if (_modal) continue;
_modal = true;
// XXX: give it focus if another window has focus that shouldnt now
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
if (_max_vert) continue;
_max_vert = true;
// XXX: resize the window etc
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
if (_max_horz) continue;
_max_horz = true;
// XXX: resize the window etc
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_shaded)) {
if (_shaded) continue;
_shaded = true;
// XXX: hide the client window
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
if (_fullscreen) continue;
_fullscreen = true;
// XXX: raise the window n shit
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_floating)) {
if (_floating) continue;
_floating = true;
// XXX: raise the window n shit
}
} else { // action == State_Remove
if (state == property->atom(otk::OBProperty::net_wm_state_modal)) {
if (!_modal) continue;
_modal = false;
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_maximized_vert)){
if (!_max_vert) continue;
_max_vert = false;
// XXX: resize the window etc
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_maximized_horz)){
if (!_max_horz) continue;
_max_horz = false;
// XXX: resize the window etc
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_shaded)) {
if (!_shaded) continue;
_shaded = false;
// XXX: show the client window
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_fullscreen)) {
if (!_fullscreen) continue;
_fullscreen = false;
// XXX: lower the window to its proper layer
} else if (state ==
property->atom(otk::OBProperty::net_wm_state_floating)) {
if (!_floating) continue;
_floating = false;
// XXX: lower the window to its proper layer
}
}
}
}
void OBClient::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 x = _area.x(), y = _area.y();
switch(_gravity) {
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;
}
switch(_gravity) {
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;
default:
// no change for StaticGravity etc.
break;
}
_area.setPos(x, y);
if (addborder) {
XSetWindowBorderWidth(otk::OBDisplay::display, _window, _border_width);
// move the client so it is back it the right spot _with_ its border!
XMoveWindow(otk::OBDisplay::display, _window, x, y);
} else
XSetWindowBorderWidth(otk::OBDisplay::display, _window, 0);
}
void OBClient::clientMessageHandler(const XClientMessageEvent &e)
2002-11-06 07:01:34 +00:00
{
otk::OtkEventHandler::clientMessageHandler(e);
2002-11-06 07:01:34 +00:00
if (e.format != 32) return;
const otk::OBProperty *property = Openbox::instance->property();
if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) {
// compress changes into a single change
bool compress = false;
XEvent ce;
2002-12-02 22:36:43 +00:00
while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
// 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) {
XPutBackEvent(otk::OBDisplay::display, &ce);
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
} else if (e.message_type ==
property->atom(otk::OBProperty::net_wm_desktop)) {
// compress changes into a single change
bool compress = false;
XEvent ce;
2002-12-02 22:36:43 +00:00
while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) {
// 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) {
XPutBackEvent(otk::OBDisplay::display, &ce);
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
}
2002-11-06 07:01:34 +00:00
else if (e.message_type == property->atom(otk::OBProperty::net_wm_state))
// can't compress these
2002-11-06 07:01:34 +00:00
setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
}
2002-11-10 05:54:12 +00:00
#if defined(SHAPE) || defined(DOXYGEN_IGNORE)
void OBClient::shapeHandler(const XShapeEvent &e)
2002-11-10 05:54:12 +00:00
{
otk::OtkEventHandler::shapeHandler(e);
2002-11-10 05:54:12 +00:00
_shaped = e.shaped;
}
#endif
2002-12-04 00:26:45 +00:00
void OBClient::resize(Corner anchor, int w, int h)
{
w -= _base_size.x();
h -= _base_size.y();
// for interactive resizing. have to move half an increment in each
// direction.
w += _size_inc.x() / 2;
h += _size_inc.y() / 2;
2002-12-04 00:26:45 +00:00
// is the window resizable? if it is not, then don't check its sizes, the
// client can do what it wants and the user can't change it anyhow
if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
// smaller than min size or bigger than max size?
if (w < _min_size.x()) w = _min_size.x();
else if (w > _max_size.x()) w = _max_size.x();
if (h < _min_size.y()) h = _min_size.y();
else if (h > _max_size.y()) h = _max_size.y();
}
// keep to the increments
w /= _size_inc.x();
h /= _size_inc.y();
// store the logical size
_logical_size.setPoint(w, h);
w *= _size_inc.x();
h *= _size_inc.y();
w += _base_size.x();
h += _base_size.y();
int x = _area.x(), y = _area.y();
2002-12-04 00:26:45 +00:00
switch (anchor) {
case TopLeft:
break;
case TopRight:
x -= w - _area.width();
2002-12-04 00:26:45 +00:00
break;
case BottomLeft:
y -= h - _area.height();
2002-12-04 00:26:45 +00:00
break;
case BottomRight:
x -= w - _area.width();
y -= h - _area.height();
2002-12-04 00:26:45 +00:00
break;
}
_area.setSize(w, h);
XResizeWindow(otk::OBDisplay::display, _window, w, h);
2002-12-04 00:26:45 +00:00
// resize the frame to match the request
frame->adjustSize();
move(x, y);
2002-12-04 00:26:45 +00:00
}
void OBClient::move(int x, int y)
{
2002-12-04 00:26:45 +00:00
_area.setPos(x, y);
// move the frame to be in the requested position
frame->adjustPosition();
}
2002-12-04 00:26:45 +00:00
void OBClient::close()
{
XEvent ce;
const otk::OBProperty *property = Openbox::instance->property();
if (!(_functions & Func_Close)) return;
// XXX: itd be cool to do timeouts and shit here for killing the client's
// process off
ce.xclient.type = ClientMessage;
ce.xclient.message_type = property->atom(otk::OBProperty::wm_protocols);
ce.xclient.display = otk::OBDisplay::display;
ce.xclient.window = _window;
ce.xclient.format = 32;
ce.xclient.data.l[0] = property->atom(otk::OBProperty::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;
XSendEvent(otk::OBDisplay::display, _window, False, NoEventMask, &ce);
}
bool OBClient::focus()
{
2002-12-26 18:15:23 +00:00
if (!_can_focus || _focused) return false;
XSetInputFocus(otk::OBDisplay::display, _window, RevertToNone, CurrentTime);
return true;
}
2002-12-26 18:15:23 +00:00
void OBClient::unfocus()
{
if (!_focused) return;
assert(Openbox::instance->focusedClient() == this);
Openbox::instance->setFocusedClient(0);
}
void OBClient::focusHandler(const XFocusChangeEvent &e)
{
#ifdef DEBUG
printf("FocusIn for 0x%lx\n", e.window);
#endif // DEBUG
OtkEventHandler::focusHandler(e);
frame->focus();
_focused = true;
Openbox::instance->setFocusedClient(this);
}
void OBClient::unfocusHandler(const XFocusChangeEvent &e)
{
#ifdef DEBUG
printf("FocusOut for 0x%lx\n", e.window);
#endif // DEBUG
OtkEventHandler::unfocusHandler(e);
frame->unfocus();
_focused = false;
if (Openbox::instance->focusedClient() == this) {
printf("UNFOCUSED!\n");
Openbox::instance->setFocusedClient(this);
}
}
2002-12-04 00:26:45 +00:00
void OBClient::configureRequestHandler(const XConfigureRequestEvent &e)
{
#ifdef DEBUG
printf("ConfigureRequest for 0x%lx\n", e.window);
#endif // DEBUG
2002-12-04 01:04:31 +00:00
OtkEventHandler::configureRequestHandler(e);
2002-12-04 00:26:45 +00:00
// XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
if (e.value_mask & CWBorderWidth)
_border_width = e.border_width;
// resize, then move, as specified in the EWMH section 7.7
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;
}
resize(corner, w, h);
}
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();
move(x, y);
}
if (e.value_mask & CWStackMode) {
switch (e.detail) {
case Below:
case BottomIf:
// XXX: lower the window
break;
case Above:
case TopIf:
default:
// XXX: raise the window
break;
}
}
}
2002-12-04 01:04:31 +00:00
void OBClient::unmapHandler(const XUnmapEvent &e)
{
#ifdef DEBUG
printf("UnmapNotify for 0x%lx\n", e.window);
#endif // DEBUG
2002-12-04 01:29:01 +00:00
if (ignore_unmaps) {
ignore_unmaps--;
return;
}
2002-12-04 01:04:31 +00:00
OtkEventHandler::unmapHandler(e);
// this deletes us etc
Openbox::instance->screen(_screen)->unmanageWindow(this);
}
void OBClient::destroyHandler(const XDestroyWindowEvent &e)
{
#ifdef DEBUG
printf("DestroyNotify for 0x%lx\n", e.window);
#endif // DEBUG
OtkEventHandler::destroyHandler(e);
// this deletes us etc
Openbox::instance->screen(_screen)->unmanageWindow(this);
}
2002-12-27 08:52:16 +00:00
void OBClient::reparentHandler(const XReparentEvent &e)
{
#ifdef DEBUG
printf("ReparentNotify for 0x%lx\n", e.window);
#endif // DEBUG
OtkEventHandler::reparentHandler(e);
// this is when the client is first taken captive in the frame
if (e.parent == frame->plate()) return;
/*
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.
*/
// this deletes us etc
Openbox::instance->screen(_screen)->unmanageWindow(this);
}
2002-11-06 07:01:34 +00:00
}