X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=604a2e5d5309c55686fd14677e6f06990e4ea762;hb=709c747a743be285b97d0ddfcaa28940394840b9;hp=9667a6f941a383864d732a1a0d3e9cb3692cc6ae;hpb=af1ac846ccb5fab8a3c83d8688db3d3f96f5a98b;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index 9667a6f9..604a2e5d 100644 --- a/src/client.cc +++ b/src/client.cc @@ -36,7 +36,7 @@ Client::Client(int screen, Window window) // update EVERYTHING the first time!! - // the state is kinda assumed to be normal. is this right? XXX + // we default to NormalState, visible _wmstate = NormalState; _iconic = false; // no default decors or functions, each has to be enabled _decorations = _functions = 0; @@ -54,19 +54,28 @@ Client::Client(int screen, Window window) getType(); getMwmHints(); - setupDecorAndFunctions(); - - getState(); + getState(); // gets all the states except for iconic, which is found from + // the desktop == ICONIC_DESKTOP getShaped(); updateProtocols(); - updateNormalHints(); - 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 + updateWMHints(true); // also get the initial_state and set _iconic 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; + changeState(); } @@ -87,10 +96,26 @@ 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() +{ + XWindowAttributes wattrib; + Status ret; + + ret = XGetWindowAttributes(**otk::display, _window, &wattrib); + assert(ret != BadWindow); + _gravity = wattrib.win_gravity; +} + + void Client::getDesktop() { // defaults to the current desktop @@ -156,15 +181,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 @@ -197,8 +228,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)) @@ -222,7 +256,7 @@ void Client::setupDecorAndFunctions() } } - // XXX: changeAllowedActions(); + changeAllowedActions(); } @@ -275,10 +309,9 @@ 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_skip_taskbar) _skip_taskbar = true; else if (state[i] == otk::Property::atoms.net_wm_state_skip_pager) _skip_pager = true; @@ -351,16 +384,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 @@ -378,7 +408,6 @@ void Client::updateNormalHints() int oldgravity = _gravity; // defaults - _gravity = NorthWestGravity; _size_inc.setPoint(1, 1); _base_size.setPoint(0, 0); _min_size.setPoint(0, 0); @@ -391,8 +420,18 @@ void Client::updateNormalHints() if (XGetWMNormalHints(**otk::display, _window, &size, &ret)) { _positioned = (size.flags & (PPosition|USPosition)); - if (size.flags & PWinGravity) + if (size.flags & PWinGravity) { _gravity = size.win_gravity; + + // 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); + } + } if (size.flags & PMinSize) _min_size.setPoint(size.min_width, size.min_height); @@ -406,19 +445,10 @@ void Client::updateNormalHints() 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); - } } -void Client::updateWMHints() +void Client::updateWMHints(bool initstate) { XWMHints *hints; @@ -430,6 +460,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; @@ -597,8 +631,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(); } @@ -608,13 +645,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; } } @@ -626,10 +662,13 @@ 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; + // set the desktop hint otk::Property::set(_window, otk::Property::atoms.net_wm_desktop, otk::Property::atoms.cardinal, (unsigned)_desktop); @@ -639,12 +678,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)) @@ -691,16 +752,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; @@ -722,16 +780,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; @@ -741,6 +796,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(); @@ -756,6 +815,7 @@ void Client::toggleClientBorder(bool addborder) // reversed. int x = _area.x(), y = _area.y(); switch(_gravity) { + default: case NorthWestGravity: case WestGravity: case SouthWestGravity: @@ -766,8 +826,17 @@ void Client::toggleClientBorder(bool addborder) if (addborder) x -= _border_width * 2; else x += _border_width * 2; break; + case NorthGravity: + case SouthGravity: + case CenterGravity: + case ForgetGravity: + case StaticGravity: + if (addborder) x -= _border_width; + else x += _border_width; + break; } switch(_gravity) { + default: case NorthWestGravity: case NorthGravity: case NorthEastGravity: @@ -778,8 +847,13 @@ void Client::toggleClientBorder(bool addborder) if (addborder) y -= _border_width * 2; else y += _border_width * 2; break; - default: - // no change for StaticGravity etc. + case WestGravity: + case EastGravity: + case CenterGravity: + case ForgetGravity: + case StaticGravity: + if (addborder) y -= _border_width; + else y += _border_width; break; } _area.setPos(x, y); @@ -852,6 +926,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 @@ -874,7 +950,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(); @@ -936,11 +1019,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); @@ -1028,20 +1118,132 @@ 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) { + _iconic = false; + _desktop = 0; // set some other source desktop so this goes through + 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(); } +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() const { // won't try focus if the client doesn't want it, or if the window isn't @@ -1050,6 +1252,14 @@ bool Client::focus() const if (_focused) return true; + // do a check to see if the window has already been unmapped or destroyed + XEvent ev; + if (XCheckTypedWindowEvent(**otk::display, _window, UnmapNotify, &ev) || + XCheckTypedWindowEvent(**otk::display, _window, DestroyNotify, &ev)) { + XPutBackEvent(**otk::display, &ev); + return false; + } + if (_can_focus) XSetInputFocus(**otk::display, _window, RevertToNone, CurrentTime); @@ -1121,7 +1331,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; @@ -1152,13 +1363,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) { @@ -1240,4 +1451,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) +} + }