X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=3939b0c61ada1b63278e7d0691ccf392da3cf722;hb=ee356f4265d09a542d77474ac2f4b5057e7d04fe;hp=604a2e5d5309c55686fd14677e6f06990e4ea762;hpb=709c747a743be285b97d0ddfcaa28940394840b9;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index 604a2e5d..3939b0c6 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) @@ -37,15 +40,17 @@ Client::Client(int screen, Window window) // update EVERYTHING the first time!! // we default to NormalState, visible - _wmstate = NormalState; _iconic = false; - // no default decors or functions, each has to be enabled - _decorations = _functions = 0; + _wmstate = NormalState; // start unfocused _focused = false; // not a transient by default of course _transient_for = 0; // pick a layer to start from _layer = Layer_Normal; + // default to not urgent + _urgent = false; + // not positioned unless specified + _positioned = false; getArea(); getDesktop(); @@ -54,8 +59,7 @@ Client::Client(int screen, Window window) getType(); getMwmHints(); - getState(); // gets all the states except for iconic, which is found from - // the desktop == ICONIC_DESKTOP + getState(); getShaped(); updateProtocols(); @@ -66,15 +70,23 @@ Client::Client(int screen, Window window) getGravity(); // get the attribute gravity updateNormalHints(); // this may override the attribute gravity - updateWMHints(true); // also get the initial_state and set _iconic + // 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(); updateIconTitle(); updateClass(); updateStrut(); - // restores iconic state when we restart. - // this will override the initial_state if that was set - if (_desktop == ICONIC_DESKTOP) _iconic = true; + // 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(); } @@ -105,6 +117,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; @@ -121,12 +148,12 @@ 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)) { - // make sure the hint exists - otk::Property::set(_window, otk::Property::atoms.net_wm_desktop, - otk::Property::atoms.cardinal, (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 } } @@ -190,6 +217,11 @@ 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; + _functions &= ~(Func_Resize | Func_Maximize); + } switch (_type) { case Type_Normal: @@ -299,7 +331,7 @@ void Client::getArea() void Client::getState() { _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below = - _skip_taskbar = _skip_pager = false; + _iconic = _skip_taskbar = _skip_pager = false; unsigned long *state; unsigned long num = (unsigned) -1; @@ -311,6 +343,8 @@ void Client::getState() _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) _skip_taskbar = true; else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager) @@ -408,6 +442,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); @@ -433,6 +469,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); @@ -454,7 +495,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) @@ -465,7 +506,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) { @@ -478,6 +519,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(); + } } @@ -553,8 +606,8 @@ void Client::updateStrut() _strut.left = data[0]; _strut.right = data[1]; _strut.top = data[2]; - _strut.bottom = data[3]; - + _strut.bottom = data[3]; + openbox->screen(_screen)->updateStrut(); } @@ -600,6 +653,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; @@ -612,9 +668,10 @@ 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(); @@ -668,9 +725,10 @@ void Client::setDesktop(long target) _desktop = target; - // set the desktop hint - otk::Property::set(_window, otk::Property::atoms.net_wm_desktop, - otk::Property::atoms.cardinal, (unsigned)_desktop); + // set the desktop hint, but not if we're iconifying + if (_desktop != ICONIC_DESKTOP) + otk::Property::set(_window, otk::Property::atoms.net_wm_desktop, + otk::Property::atoms.cardinal, (unsigned)_desktop); // 'move' the window to the new desktop if (_desktop == openbox->screen(_screen)->desktop() || @@ -872,6 +930,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) { @@ -957,7 +1018,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(); @@ -967,14 +1029,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 @@ -1159,7 +1228,6 @@ void Client::applyStartupState() if (_iconic) { _iconic = false; - _desktop = 0; // set some other source desktop so this goes through setDesktop(ICONIC_DESKTOP); } if (_fullscreen) { @@ -1170,6 +1238,8 @@ void Client::applyStartupState() _shaded = false; shade(true); } + if (_urgent) + fireUrgent(); if (_max_vert); // XXX: incomplete if (_max_horz); // XXX: incomplete @@ -1182,6 +1252,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 @@ -1244,7 +1322,7 @@ void Client::fullscreen(bool fs) } -bool Client::focus() const +bool Client::focus() { // won't try focus if the client doesn't want it, or if the window isn't // visible on the screen @@ -1253,12 +1331,21 @@ bool Client::focus() const 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, UnmapNotify, &ev) || - XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) { + if (XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) { XPutBackEvent(**otk::display, &ev); return false; } + while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) { + if (ignore_unmaps) { + unmapHandler(ev.xunmap); + } else { + XPutBackEvent(**otk::display, &ev); + return false; + } + } if (_can_focus) XSetInputFocus(**otk::display, _window, @@ -1279,6 +1366,7 @@ bool Client::focus() const XSendEvent(**otk::display, _window, False, NoEventMask, &ce); } + XSync(**otk::display, False); return true; } @@ -1363,9 +1451,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(); @@ -1393,7 +1481,7 @@ void Client::unmapHandler(const XUnmapEvent &e) { if (ignore_unmaps) { #ifdef DEBUG - printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event); +// printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event); #endif // DEBUG ignore_unmaps--; return;