X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=8f6549085f772e2bf43f85bf01a9e70b3de781d4;hb=f92804ec20cb51d8656887ea9a78d68a06b1f02a;hp=91d61f4bd20392904744e19f06e675ebfde4b4c9;hpb=f4b87e04a2a1d285f7b6f93b3db08f040ba5c32c;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index 91d61f4b..8f654908 100644 --- a/src/client.cc +++ b/src/client.cc @@ -8,6 +8,7 @@ #include "frame.hh" #include "screen.hh" #include "openbox.hh" +#include "bindings.hh" #include "otk/display.hh" #include "otk/property.hh" @@ -22,6 +23,8 @@ extern "C" { #define _(str) gettext(str) } +#include + namespace ob { Client::Client(int screen, Window window) @@ -44,6 +47,12 @@ Client::Client(int screen, Window window) _transient_for = 0; // pick a layer to start from _layer = Layer_Normal; + // default to not urgent + _urgent = false; + // not positioned unless specified + _positioned = false; + // nothing is disabled unless specified + _disabled_decorations = 0; getArea(); getDesktop(); @@ -57,12 +66,14 @@ Client::Client(int screen, Window window) updateProtocols(); - // got the type, the mwmhints, and the protocols, so we're ready to set up + 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(); - getGravity(); // get the attribute gravity - updateNormalHints(); // this may override the attribute gravity // 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 @@ -73,7 +84,7 @@ Client::Client(int screen, Window window) updateStrut(); // this makes sure that these windows appear on all desktops - if (_type == Type_Dock || _type == Type_Desktop) + if (/*_type == Type_Dock ||*/ _type == Type_Desktop) _desktop = 0xffffffff; // set the desktop hint, to make sure that it always exists, and to reflect @@ -110,6 +121,21 @@ Client::~Client() } +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; @@ -126,9 +152,13 @@ void Client::getDesktop() // defaults to the current desktop _desktop = openbox->screen(_screen)->desktop(); - otk::Property::get(_window, otk::Property::atoms.net_wm_desktop, - otk::Property::atoms.cardinal, - (long unsigned*)&_desktop); + if (otk::Property::get(_window, otk::Property::atoms.net_wm_desktop, + otk::Property::atoms.cardinal, + (long unsigned*)&_desktop)) { +#ifdef DEBUG +// printf("Window requested desktop: %ld\n", _desktop); +#endif + } } @@ -191,12 +221,18 @@ void Client::setupDecorAndFunctions() _decorations |= Decor_Close; _functions |= Func_Close; } + + if (!(_min_size.x() < _max_size.x() || _min_size.y() < _max_size.y())) { + _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; + break; case Type_Dialog: // dialogs cannot be maximized @@ -257,7 +293,34 @@ void Client::setupDecorAndFunctions() } } + // 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; + + // You can't shade without a titlebar + 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 + } } @@ -411,6 +474,8 @@ void Client::updateNormalHints() int oldgravity = _gravity; // defaults + _min_ratio = 0.0; + _max_ratio = 0.0; _size_inc.setPoint(1, 1); _base_size.setPoint(0, 0); _min_size.setPoint(0, 0); @@ -436,6 +501,11 @@ void Client::updateNormalHints() } } + 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; + } + if (size.flags & PMinSize) _min_size.setPoint(size.min_width, size.min_height); @@ -457,7 +527,7 @@ void Client::updateWMHints(bool initstate) // assume a window takes input if it doesnt specify _can_focus = true; - _urgent = false; + bool ur = false; if ((hints = XGetWMHints(**otk::display, _window)) != NULL) { if (hints->flags & InputHint) @@ -468,7 +538,7 @@ void Client::updateWMHints(bool initstate) _iconic = hints->initial_state == IconicState; if (hints->flags & XUrgencyHint) - _urgent = true; + ur = true; if (hints->flags & WindowGroupHint) { if (hints->window_group != _group) { @@ -481,6 +551,18 @@ void Client::updateWMHints(bool initstate) 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(); + } } @@ -556,9 +638,12 @@ void Client::updateStrut() _strut.left = data[0]; _strut.right = data[1]; _strut.top = data[2]; - _strut.bottom = data[3]; - - openbox->screen(_screen)->updateStrut(); + _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) + openbox->screen(_screen)->updateStrut(); } delete [] data; @@ -603,6 +688,9 @@ void Client::updateTransientFor() void Client::propertyHandler(const XPropertyEvent &e) { otk::EventHandler::propertyHandler(e); + + // validate cuz we query stuff off the client here + if (!validate()) return; // compress changes to a single property into a single change XEvent ce; @@ -615,16 +703,16 @@ void Client::propertyHandler(const XPropertyEvent &e) } } - if (e.atom == XA_WM_NORMAL_HINTS) + if (e.atom == XA_WM_NORMAL_HINTS) { updateNormalHints(); - else if (e.atom == XA_WM_HINTS) + setupDecorAndFunctions(); // normal hints can make a window non-resizable + } else if (e.atom == XA_WM_HINTS) updateWMHints(); else if (e.atom == XA_WM_TRANSIENT_FOR) { updateTransientFor(); getType(); calcLayer(); // type may have changed, so update the layer setupDecorAndFunctions(); - frame->adjustSize(); // this updates the frame for any new decor settings } else if (e.atom == otk::Property::atoms.net_wm_name || e.atom == otk::Property::atoms.wm_name) @@ -637,7 +725,6 @@ void Client::propertyHandler(const XPropertyEvent &e) else if (e.atom == otk::Property::atoms.wm_protocols) { updateProtocols(); setupDecorAndFunctions(); - frame->adjustSize(); // update the decorations } else if (e.atom == otk::Property::atoms.net_wm_strut) updateStrut(); @@ -807,6 +894,7 @@ void Client::setState(StateAction action, long data1, long data2) if (shadestate != _shaded) shade(shadestate); calcLayer(); + changeState(); // change the hint to relect these changes } @@ -817,7 +905,8 @@ void Client::toggleClientBorder(bool addborder) // different position. // when re-adding the border to the client, the same operation needs to be // reversed. - int x = _area.x(), y = _area.y(); + int oldx = _area.x(), oldy = _area.y(); + int x = oldx, y = oldy; switch(_gravity) { default: case NorthWestGravity: @@ -866,7 +955,8 @@ void Client::toggleClientBorder(bool addborder) XSetWindowBorderWidth(**otk::display, _window, _border_width); // move the client so it is back it the right spot _with_ its border! - XMoveWindow(**otk::display, _window, x, y); + if (x != oldx || y != oldy) + XMoveWindow(**otk::display, _window, x, y); } else XSetWindowBorderWidth(**otk::display, _window, 0); } @@ -876,6 +966,9 @@ void Client::clientMessageHandler(const XClientMessageEvent &e) { otk::EventHandler::clientMessageHandler(e); + // validate cuz we query stuff off the client here + if (!validate()) return; + if (e.format != 32) return; if (e.message_type == otk::Property::atoms.wm_change_state) { @@ -961,7 +1054,8 @@ void Client::resize(Corner anchor, int w, int h) } -void Client::internal_resize(Corner anchor, int w, int h, int x, int y) +void Client::internal_resize(Corner anchor, int w, int h, bool user, + int x, int y) { w -= _base_size.x(); h -= _base_size.y(); @@ -971,14 +1065,21 @@ void Client::internal_resize(Corner anchor, int w, int h, int x, int y) 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()) { + if (user) { + // if this is a user-requested resize, then check against min/max sizes + // and aspect ratios + // 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(); + + // adjust the height ot match the width for the aspect ratios + if (_min_ratio) + if (h * _min_ratio > w) h = static_cast(w / _min_ratio); + if (_max_ratio) + if (h * _max_ratio < w) h = static_cast(w / _max_ratio); } // keep to the increments @@ -1049,15 +1150,23 @@ void Client::internal_move(int x, int y) event.xconfigure.display = **otk::display; event.xconfigure.event = _window; event.xconfigure.window = _window; - event.xconfigure.x = x; - event.xconfigure.y = y; + + // 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->window(); + 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 } } @@ -1095,7 +1204,7 @@ void Client::changeState() state[1] = None; otk::Property::set(_window, otk::Property::atoms.wm_state, otk::Property::atoms.wm_state, state, 2); - + Atom netstate[10]; int num = 0; if (_modal) @@ -1162,7 +1271,6 @@ void Client::applyStartupState() // these are in a carefully crafted order.. if (_iconic) { - printf("MAP ICONIC\n"); _iconic = false; setDesktop(ICONIC_DESKTOP); } @@ -1174,6 +1282,8 @@ void Client::applyStartupState() _shaded = false; shade(true); } + if (_urgent) + fireUrgent(); if (_max_vert); // XXX: incomplete if (_max_horz); // XXX: incomplete @@ -1186,6 +1296,14 @@ void Client::applyStartupState() } +void Client::fireUrgent() +{ + // call the python UrgentWindow callbacks + EventData data(_screen, this, EventAction::UrgentWindow, 0); + openbox->bindings()->fireEvent(&data); +} + + void Client::shade(bool shade) { if (!(_functions & Func_Shade) || // can't @@ -1248,6 +1366,13 @@ void Client::fullscreen(bool fs) } +void Client::disableDecorations(DecorationFlags flags) +{ + _disabled_decorations = flags; + setupDecorAndFunctions(); +} + + bool Client::focus() { // won't try focus if the client doesn't want it, or if the window isn't @@ -1266,7 +1391,7 @@ bool Client::focus() } while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) { if (ignore_unmaps) { - --ignore_unmaps; + unmapHandler(ev.xunmap); } else { XPutBackEvent(**otk::display, &ev); return false; @@ -1292,6 +1417,7 @@ bool Client::focus() XSendEvent(**otk::display, _window, False, NoEventMask, &ce); } + XSync(**otk::display, False); return true; } @@ -1376,9 +1502,9 @@ void Client::configureRequestHandler(const XConfigureRequestEvent &e) 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, x, y); + internal_resize(corner, w, h, false, x, y); } else // if JUST resizing... - internal_resize(corner, w, h); + internal_resize(corner, w, h, false); } else if (e.value_mask & (CWX | CWY)) { // if JUST moving... int x = (e.value_mask & CWX) ? e.x : _area.x(); int y = (e.value_mask & CWY) ? e.y : _area.y();