X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=473c37f8f98fe8e33885059de69b45330abe4bb5;hb=ab2968b3a5d61e2dc6b0a64153a7abb0078ee442;hp=27d89c077e7f0be41268cf0c579d94843d7d8696;hpb=28b8f67562bb7eb15134f2bf7a8394f0a009b9ba;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index 27d89c07..473c37f8 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" @@ -36,16 +37,16 @@ Client::Client(int screen, Window window) // update EVERYTHING the first time!! - // the state is kinda assumed to be normal. is this right? XXX - _wmstate = NormalState; _iconic = false; - // no default decors or functions, each has to be enabled - _decorations = _functions = 0; + // we default to NormalState, visible + _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; getArea(); getDesktop(); @@ -54,20 +55,35 @@ Client::Client(int screen, Window window) getType(); getMwmHints(); - setupDecorAndFunctions(); - getState(); getShaped(); updateProtocols(); - getGravity(); // get the attribute gravity + + // got the type, the mwmhints, and the protocols, so we're ready to set up + // the decorations/functions + setupDecorAndFunctions(); + + getGravity(); // get the attribute gravity updateNormalHints(); // this may override the attribute gravity - updateWMHints(); + // 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(); + // 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(); } @@ -88,6 +104,11 @@ Client::~Client() // these values should not be persisted across a window unmapping/mapping 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); } } @@ -108,13 +129,9 @@ 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); - } + otk::Property::get(_window, otk::Property::atoms.net_wm_desktop, + otk::Property::atoms.cardinal, + (long unsigned*)&_desktop); } @@ -173,6 +190,10 @@ void Client::setupDecorAndFunctions() 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; + } switch (_type) { case Type_Normal: @@ -282,7 +303,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; @@ -294,6 +315,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) @@ -367,16 +390,13 @@ void Client::updateProtocols() int num_return = 0; _focus_notify = false; - _decorations &= ~Decor_Close; - _functions &= ~Func_Close; + _delete_window = false; if (XGetWMProtocols(**otk::display, _window, &proto, &num_return)) { for (int i = 0; i < num_return; ++i) { if (proto[i] == otk::Property::atoms.wm_delete_window) { - _decorations |= Decor_Close; - _functions |= Func_Close; - if (frame) - frame->adjustSize(); // update the decorations + // this means we can request the window to close + _delete_window = true; } else if (proto[i] == otk::Property::atoms.wm_take_focus) // if this protocol is requested, then the window will be notified // by the window manager whenever it receives focus @@ -434,20 +454,24 @@ void Client::updateNormalHints() } -void Client::updateWMHints() +void Client::updateWMHints(bool initstate) { XWMHints *hints; // 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) _can_focus = hints->input; + // only do this when initstate is true! + if (initstate && (hints->flags & StateHint)) + _iconic = hints->initial_state == IconicState; + if (hints->flags & XUrgencyHint) - _urgent = true; + ur = true; if (hints->flags & WindowGroupHint) { if (hints->window_group != _group) { @@ -460,6 +484,14 @@ void Client::updateWMHints() XFree(hints); } + + if (ur != _urgent) { + _urgent = ur; + // fire the urgent callback if we're mapped, otherwise, wait until after + // we're mapped + if (_urgent && frame) + fireUrgent(); + } } @@ -613,8 +645,11 @@ void Client::propertyHandler(const XPropertyEvent &e) updateIconTitle(); else if (e.atom == otk::Property::atoms.wm_class) updateClass(); - else if (e.atom == otk::Property::atoms.wm_protocols) + 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(); } @@ -624,13 +659,12 @@ void Client::setWMState(long state) { if (state == _wmstate) return; // no change - _wmstate = state; - switch (_wmstate) { + switch (state) { case IconicState: - // XXX: cause it to iconify + setDesktop(ICONIC_DESKTOP); break; case NormalState: - // XXX: cause it to uniconify + setDesktop(openbox->screen(_screen)->desktop()); break; } } @@ -642,12 +676,16 @@ void Client::setDesktop(long target) printf("Setting desktop %ld\n", target); - if (!(target >= 0 || target == (signed)0xffffffff)) return; + if (!(target >= 0 || target == (signed)0xffffffff || + target == ICONIC_DESKTOP)) + return; _desktop = target; - 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() || @@ -656,6 +694,25 @@ void Client::setDesktop(long target) else frame->hide(); + // Handle Iconic state. Iconic state is maintained by the client being a + // member of the ICONIC_DESKTOP, so this is where we make iconifying and + // uniconifying happen. + bool i = _desktop == ICONIC_DESKTOP; + if (i != _iconic) { // has the state changed? + _iconic = i; + 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 { + _wmstate = NormalState; + XMapWindow(**otk::display, _window); + } + changeState(); + } + frame->adjustState(); } @@ -884,6 +941,8 @@ void Client::clientMessageHandler(const XClientMessageEvent &e) #ifdef DEBUG printf("net_active_window for 0x%lx\n", _window); #endif + if (_iconic) + setDesktop(openbox->screen(_screen)->desktop()); if (_shaded) shade(false); // XXX: deiconify @@ -1112,7 +1171,12 @@ void Client::changeAllowedActions(void) void Client::applyStartupState() { // these are in a carefully crafted order.. - + + if (_iconic) { + printf("MAP ICONIC\n"); + _iconic = false; + setDesktop(ICONIC_DESKTOP); + } if (_fullscreen) { _fullscreen = false; fullscreen(true); @@ -1121,6 +1185,8 @@ void Client::applyStartupState() _shaded = false; shade(true); } + if (_urgent) + fireUrgent(); if (_max_vert); // XXX: incomplete if (_max_horz); // XXX: incomplete @@ -1133,12 +1199,22 @@ void Client::applyStartupState() } +void Client::fireUrgent() +{ + // call the python UrgentNotify callbacks + EventData data(_screen, this, EventUrgentNotify, 0); + openbox->bindings()->fireEvent(&data); +} + + void Client::shade(bool shade) { if (!(_functions & Func_Shade) || // can't _shaded == shade) return; // already done - _wmstate = shade ? IconicState : NormalState; + // when we're iconic, don't change the wmstate + if (!_iconic) + _wmstate = shade ? IconicState : NormalState; _shaded = shade; changeState(); frame->adjustSize(); @@ -1193,7 +1269,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 @@ -1202,12 +1278,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, @@ -1280,7 +1365,8 @@ void Client::configureRequestHandler(const XConfigureRequestEvent &e) otk::EventHandler::configureRequestHandler(e); - // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event + // if we are iconic (or shaded (fvwm does this)) ignore the event + if (_iconic || _shaded) return; if (e.value_mask & CWBorderWidth) _border_width = e.border_width; @@ -1399,4 +1485,17 @@ void Client::reparentHandler(const XReparentEvent &e) openbox->screen(_screen)->unmanageWindow(this); } +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) + setDesktop(openbox->screen(_screen)->desktop()); + // XXX: should we focus/raise the window? (basically a net_wm_active_window) +} + }