X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=8a6c1b11df7fd328c11f8458335fb1e3cb42e97b;hb=9e0ae7ecee94a0cc467d90926428fdc84f9a0339;hp=4a99b0bc58eecbb27ca21870ae57c743c0a79f80;hpb=8b041e2f5c2edc6d295697d129cbc65b9bb2c6b7;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index 4a99b0bc..8a6c1b11 100644 --- a/src/client.cc +++ b/src/client.cc @@ -1,7 +1,13 @@ // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifdef HAVE_CONFIG_H +# include "../config.h" +#endif + #include "client.hh" +#include "frame.hh" #include "screen.hh" +#include "bbscreen.hh" #include "openbox.hh" #include "otk/display.hh" #include "otk/property.hh" @@ -18,11 +24,16 @@ extern "C" { namespace ob { -OBClient::OBClient(Window window) - : _window(window) +OBClient::OBClient(int screen, Window window) + : otk::OtkEventHandler(), + OBWidget(OBWidget::Type_Client), + frame(0), _screen(screen), _window(window) { + assert(screen >= 0); assert(window); + ignore_unmaps = 0; + // update EVERYTHING the first time!! // the state is kinda assumed to be normal. is this right? XXX @@ -78,6 +89,7 @@ OBClient::OBClient(Window window) updateIconTitle(); updateClass(); +/* #ifdef DEBUG printf("Mapped window: 0x%lx\n" " title: \t%s\t icon title: \t%s\n" @@ -123,6 +135,7 @@ OBClient::OBClient(Window window) _floating ? "yes" : "no", _positioned ? "yes" : "no"); #endif +*/ } @@ -266,7 +279,10 @@ void OBClient::getMwmHints() void OBClient::getArea() { XWindowAttributes wattrib; - assert(XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib)); + Status ret; + + ret = XGetWindowAttributes(otk::OBDisplay::display, _window, &wattrib); + assert(ret != BadWindow); _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height); _border_width = wattrib.border_width; @@ -313,9 +329,13 @@ void OBClient::getShaped() if (otk::OBDisplay::shape()) { int foo; unsigned int ufoo; + int s; + + XShapeSelectInput(otk::OBDisplay::display, _window, ShapeNotifyMask); - XShapeQueryExtents(otk::OBDisplay::display, client.window, &_shaped, &foo, + XShapeQueryExtents(otk::OBDisplay::display, _window, &s, &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo); + _shaped = (s != 0); } #endif // SHAPE } @@ -352,13 +372,14 @@ void OBClient::updateNormalHints() { XSizeHints size; long ret; + int oldgravity = _gravity; // defaults _gravity = NorthWestGravity; - _inc_x = _inc_y = 1; - _base_x = _base_y = 0; - _min_x = _min_y = 0; - _max_x = _max_y = INT_MAX; + _size_inc.setPoint(1, 1); + _base_size.setPoint(0, 0); + _min_size.setPoint(0, 0); + _max_size.setPoint(INT_MAX, INT_MAX); // XXX: might want to cancel any interactive resizing of the window at this // point.. @@ -369,26 +390,27 @@ void OBClient::updateNormalHints() if (size.flags & PWinGravity) _gravity = size.win_gravity; + + if (size.flags & PMinSize) + _min_size.setPoint(size.min_width, size.min_height); - if (size.flags & PMinSize) { - _min_x = size.min_width; - _min_y = size.min_height; - } - - if (size.flags & PMaxSize) { - _max_x = size.max_width; - _max_y = size.max_height; - } + if (size.flags & PMaxSize) + _max_size.setPoint(size.max_width, size.max_height); - if (size.flags & PBaseSize) { - _base_x = size.base_width; - _base_y = size.base_height; - } + if (size.flags & PBaseSize) + _base_size.setPoint(size.base_width, size.base_height); - if (size.flags & PResizeInc) { - _inc_x = size.width_inc; - _inc_y = size.height_inc; - } + if (size.flags & PResizeInc) + _size_inc.setPoint(size.width_inc, size.height_inc); + } + + // 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); } } @@ -479,10 +501,23 @@ void OBClient::updateClass() } -void OBClient::update(const XPropertyEvent &e) +void OBClient::propertyHandler(const XPropertyEvent &e) { + otk::OtkEventHandler::propertyHandler(e); + const otk::OBProperty *property = Openbox::instance->property(); + // compress changes to a single property into a single change + XEvent ce; + while (XCheckTypedEvent(otk::OBDisplay::display, e.type, &ce)) { + // 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; + } + } + if (e.atom == XA_WM_NORMAL_HINTS) updateNormalHints(); else if (e.atom == XA_WM_HINTS) @@ -627,25 +662,269 @@ void OBClient::setState(StateAction action, long data1, long data2) } -void OBClient::update(const XClientMessageEvent &e) +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: + if (addborder) x += _border_width; + else x -= _border_width; + 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: + if (addborder) y += _border_width; + else y -= _border_width; + 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) { + otk::OtkEventHandler::clientMessageHandler(e); + if (e.format != 32) return; const otk::OBProperty *property = Openbox::instance->property(); - if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) - setWMState(e.data.l[0]); - else if (e.message_type == - property->atom(otk::OBProperty::net_wm_desktop)) - setDesktop(e.data.l[0]); + if (e.message_type == property->atom(otk::OBProperty::wm_change_state)) { + // compress changes into a single change + bool compress = false; + XEvent ce; + 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; + } + if (compress) + 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; + 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; + } + if (compress) + setDesktop(e.data.l[0]); // use the found event + else + setDesktop(e.data.l[0]); // use the original event + } else if (e.message_type == property->atom(otk::OBProperty::net_wm_state)) + // can't compress these setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]); } -void OBClient::setArea(const otk::Rect &area) +#if defined(SHAPE) || defined(DOXYGEN_IGNORE) +void OBClient::shapeHandler(const XShapeEvent &e) { - _area = area; + otk::OtkEventHandler::shapeHandler(e); + + _shaped = e.shaped; } +#endif + + +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; + + // 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(); + 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; + } + + _area.setSize(w, h); + XResizeWindow(otk::OBDisplay::display, _window, w, h); + + // resize the frame to match the request + frame->adjustSize(); + move(x, y); +} + + +void OBClient::move(int x, int y) +{ + _area.setPos(x, y); + // move the frame to be in the requested position + frame->adjustPosition(); +} + + +void OBClient::configureRequestHandler(const XConfigureRequestEvent &e) +{ + OtkEventHandler::configureRequestHandler(e); + + // 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; + } + } +} + + +void OBClient::unmapHandler(const XUnmapEvent &e) +{ +#ifdef DEBUG + printf("UnmapNotify for 0x%lx\n", e.window); +#endif // DEBUG + + if (ignore_unmaps) { + ignore_unmaps--; + return; + } + + 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); +} + }