X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=91d61f4bd20392904744e19f06e675ebfde4b4c9;hb=ff98438acca3ae7ca0b2be66cb39cc29643ecaba;hp=d005a0bdaad5acf032c5c04ea9bec24170bfa427;hpb=2b0897234fe041bac66c28f4a3b75d67d55991d7;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index d005a0bd..91d61f4b 100644 --- a/src/client.cc +++ b/src/client.cc @@ -36,10 +36,8 @@ 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 @@ -47,7 +45,6 @@ Client::Client(int screen, Window window) // pick a layer to start from _layer = Layer_Normal; - getGravity(); getArea(); getDesktop(); @@ -55,18 +52,35 @@ Client::Client(int screen, Window window) getType(); getMwmHints(); - setupDecorAndFunctions(); - getState(); getShaped(); updateProtocols(); - updateWMHints(); + + // 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 + // 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(); } @@ -87,27 +101,23 @@ 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); } } void Client::getGravity() { - XSizeHints size; XWindowAttributes wattrib; Status ret; - long junk; - if (XGetWMNormalHints(**otk::display, _window, &size, &junk) && - size.flags & PWinGravity) { - // first try the normal hints - _gravity = size.win_gravity; - } else { - // then fall back to the attribute - ret = XGetWindowAttributes(**otk::display, _window, &wattrib); - assert(ret != BadWindow); - _gravity = wattrib.win_gravity; - } + ret = XGetWindowAttributes(**otk::display, _window, &wattrib); + assert(ret != BadWindow); + _gravity = wattrib.win_gravity; } @@ -116,13 +126,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); } @@ -176,15 +182,21 @@ void Client::getType() void Client::setupDecorAndFunctions() { - // start with everything + // start with everything (cept fullscreen) _decorations = Decor_Titlebar | Decor_Handle | Decor_Border | - Decor_Iconify | Decor_Maximize; - _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; + 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: // normal windows retain all of the possible decorations and - // functionality + // functionality, and are the only windows that you can fullscreen + _functions |= Func_Fullscreen; case Type_Dialog: // dialogs cannot be maximized @@ -217,8 +229,11 @@ void Client::setupDecorAndFunctions() _decorations &= ~Decor_Border; if (! (_mwmhints.decorations & MwmDecor_Handle)) _decorations &= ~Decor_Handle; - if (! (_mwmhints.decorations & MwmDecor_Title)) + if (! (_mwmhints.decorations & MwmDecor_Title)) { _decorations &= ~Decor_Titlebar; + // if we don't have a titlebar, then we cannot shade! + _functions &= ~Func_Shade; + } if (! (_mwmhints.decorations & MwmDecor_Iconify)) _decorations &= ~Decor_Iconify; if (! (_mwmhints.decorations & MwmDecor_Maximize)) @@ -242,7 +257,7 @@ void Client::setupDecorAndFunctions() } } - // XXX: changeAllowedActions(); + changeAllowedActions(); } @@ -285,7 +300,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; @@ -295,10 +310,11 @@ void Client::getState() for (unsigned long i = 0; i < num; ++i) { if (state[i] == otk::Property::atoms.net_wm_state_modal) _modal = true; - else if (state[i] == otk::Property::atoms.net_wm_state_shaded) { + else if (state[i] == otk::Property::atoms.net_wm_state_shaded) _shaded = true; - _wmstate = IconicState; - } else if (state[i] == otk::Property::atoms.net_wm_state_skip_taskbar) + 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) _skip_pager = true; @@ -371,16 +387,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 @@ -415,7 +428,7 @@ void Client::updateNormalHints() // if the client has a frame, i.e. has already been mapped and is // changing its gravity - if (_gravity != oldgravity) { + if (frame && _gravity != oldgravity) { // move our idea of the client's position based on its new gravity int x, y; frame->frameGravity(x, y); @@ -438,7 +451,7 @@ void Client::updateNormalHints() } -void Client::updateWMHints() +void Client::updateWMHints(bool initstate) { XWMHints *hints; @@ -450,6 +463,10 @@ void Client::updateWMHints() 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; @@ -617,8 +634,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(); } @@ -628,13 +648,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; } } @@ -646,12 +665,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() || @@ -659,12 +682,34 @@ void Client::setDesktop(long target) frame->show(); 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(); } void Client::setState(StateAction action, long data1, long data2) { bool shadestate = _shaded; + bool fsstate = _fullscreen; if (!(action == State_Add || action == State_Remove || action == State_Toggle)) @@ -711,16 +756,13 @@ void Client::setState(StateAction action, long data1, long data2) _max_horz = true; // XXX: resize the window etc } else if (state == otk::Property::atoms.net_wm_state_shaded) { - if (_shaded) continue; - // shade when we're all thru here shadestate = true; } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) { _skip_taskbar = true; } else if (state == otk::Property::atoms.net_wm_state_skip_pager) { _skip_pager = true; } else if (state == otk::Property::atoms.net_wm_state_fullscreen) { - if (_fullscreen) continue; - _fullscreen = true; + fsstate = true; } else if (state == otk::Property::atoms.net_wm_state_above) { if (_above) continue; _above = true; @@ -742,16 +784,13 @@ void Client::setState(StateAction action, long data1, long data2) _max_horz = false; // XXX: resize the window etc } else if (state == otk::Property::atoms.net_wm_state_shaded) { - if (!_shaded) continue; - // unshade when we're all thru here shadestate = false; } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) { _skip_taskbar = false; } else if (state == otk::Property::atoms.net_wm_state_skip_pager) { _skip_pager = false; } else if (state == otk::Property::atoms.net_wm_state_fullscreen) { - if (!_fullscreen) continue; - _fullscreen = false; + fsstate = false; } else if (state == otk::Property::atoms.net_wm_state_above) { if (!_above) continue; _above = false; @@ -761,6 +800,10 @@ void Client::setState(StateAction action, long data1, long data2) } } } + // change fullscreen state before shading, as it will affect if the window + // can shade or not + if (fsstate != _fullscreen) + fullscreen(fsstate); if (shadestate != _shaded) shade(shadestate); calcLayer(); @@ -887,6 +930,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 @@ -909,7 +954,14 @@ void Client::shapeHandler(const XShapeEvent &e) #endif -void Client::resize(Corner anchor, int w, int h, int x, int y) +void Client::resize(Corner anchor, int w, int h) +{ + if (!(_functions & Func_Resize)) return; + internal_resize(anchor, w, h); +} + + +void Client::internal_resize(Corner anchor, int w, int h, int x, int y) { w -= _base_size.x(); h -= _base_size.y(); @@ -971,11 +1023,18 @@ void Client::resize(Corner anchor, int w, int h, int x, int y) // resize the frame to match the request frame->adjustSize(); - move(x, y); + internal_move(x, y); } void Client::move(int x, int y) +{ + if (!(_functions & Func_Move)) return; + internal_move(x, y); +} + + +void Client::internal_move(int x, int y) { _area.setPos(x, y); @@ -1063,21 +1122,133 @@ void Client::changeState() otk::Property::atoms.atom, netstate, num); calcLayer(); + + if (frame) + frame->adjustState(); +} + + +void Client::changeAllowedActions(void) +{ + Atom actions[9]; + int num = 0; + + actions[num++] = otk::Property::atoms.net_wm_action_change_desktop; + + if (_functions & Func_Shade) + actions[num++] = otk::Property::atoms.net_wm_action_shade; + if (_functions & Func_Close) + actions[num++] = otk::Property::atoms.net_wm_action_close; + if (_functions & Func_Move) + actions[num++] = otk::Property::atoms.net_wm_action_move; + if (_functions & Func_Iconify) + actions[num++] = otk::Property::atoms.net_wm_action_minimize; + if (_functions & Func_Resize) + actions[num++] = otk::Property::atoms.net_wm_action_resize; + if (_functions & Func_Fullscreen) + actions[num++] = otk::Property::atoms.net_wm_action_fullscreen; + if (_functions & Func_Maximize) { + actions[num++] = otk::Property::atoms.net_wm_action_maximize_horz; + actions[num++] = otk::Property::atoms.net_wm_action_maximize_vert; + } + + otk::Property::set(_window, otk::Property::atoms.net_wm_allowed_actions, + otk::Property::atoms.atom, actions, num); +} + + +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); + } + if (_shaded) { + _shaded = false; + shade(true); + } + + if (_max_vert); // XXX: incomplete + if (_max_horz); // XXX: incomplete + + if (_skip_taskbar); // nothing to do for this + if (_skip_pager); // nothing to do for this + if (_modal); // nothing to do for this + if (_above); // nothing to do for this + if (_below); // nothing to do for this } void Client::shade(bool shade) { - if (shade == _shaded) return; // already done + 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(); } -bool Client::focus() const +void Client::fullscreen(bool fs) +{ + static FunctionFlags saved_func; + static DecorationFlags saved_decor; + static otk::Rect saved_area; + static otk::Point saved_logical_size; + + if (!(_functions & Func_Fullscreen) || // can't + _fullscreen == fs) return; // already done + + _fullscreen = fs; + changeState(); // change the state hints on the client + + if (fs) { + // save the functions and remove them + saved_func = _functions; + _functions = _functions & (Func_Close | Func_Fullscreen | Func_Iconify); + // save the decorations and remove them + saved_decor = _decorations; + _decorations = 0; + // save the area and adjust it (we don't call internal resize here for + // constraints on the size, etc, we just make it fullscreen). + saved_area = _area; + const otk::ScreenInfo *info = otk::display->screenInfo(_screen); + _area.setRect(0, 0, info->width(), info->height()); + saved_logical_size = _logical_size; + _logical_size.setPoint((info->width() - _base_size.x()) / _size_inc.x(), + (info->height() - _base_size.y()) / _size_inc.y()); + } else { + _functions = saved_func; + _decorations = saved_decor; + _area = saved_area; + _logical_size = saved_logical_size; + } + + changeAllowedActions(); // based on the new _functions + + frame->adjustSize(); // drop/replace the decor's and resize + frame->adjustPosition(); // get (back) in position! + + // raise (back) into our stacking layer + openbox->screen(_screen)->raiseWindow(this); + + // try focus us when we go into fullscreen mode + if (fs) focus(); +} + + +bool Client::focus() { // won't try focus if the client doesn't want it, or if the window isn't // visible on the screen @@ -1085,6 +1256,23 @@ 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, DestroyNotify, &ev)) { + XPutBackEvent(**otk::display, &ev); + return false; + } + while (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev)) { + if (ignore_unmaps) { + --ignore_unmaps; + } else { + XPutBackEvent(**otk::display, &ev); + return false; + } + } + if (_can_focus) XSetInputFocus(**otk::display, _window, RevertToNone, CurrentTime); @@ -1156,7 +1344,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; @@ -1187,13 +1376,13 @@ 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(); - resize(corner, w, h, x, y); + internal_resize(corner, w, h, x, y); } else // if JUST resizing... - resize(corner, w, h); + internal_resize(corner, w, h); } 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(); - move(x, y); + internal_move(x, y); } if (e.value_mask & CWStackMode) { @@ -1217,7 +1406,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; @@ -1275,4 +1464,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) +} + }