X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fclient.cc;h=f60a8da62e33882c38631cd62f3d9751ae5f6e0f;hb=d73d4247e7b98df679ca81b349da26dd1e2fabc1;hp=e9136df4d0324c3294869e8af44d6cee9ef56af1;hpb=44d653e3e8d042bf3f1d68108a8197a895888da8;p=chaz%2Fopenbox diff --git a/src/client.cc b/src/client.cc index e9136df4..f60a8da6 100644 --- a/src/client.cc +++ b/src/client.cc @@ -51,6 +51,8 @@ Client::Client(int screen, Window window) _urgent = false; // not positioned unless specified _positioned = false; + // nothing is disabled unless specified + _disabled_decorations = 0; getArea(); getDesktop(); @@ -64,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 @@ -218,8 +222,8 @@ void Client::setupDecorAndFunctions() _functions |= Func_Close; } - if (_min_size.x() > _max_size.x() || _min_size.y() > _max_size.y()) { - _decorations &= ~Decor_Maximize; + if (!(_min_size.x() < _max_size.x() || _min_size.y() < _max_size.y())) { + _decorations &= ~(Decor_Maximize | Decor_Handle); _functions &= ~(Func_Resize | Func_Maximize); } @@ -228,6 +232,7 @@ void Client::setupDecorAndFunctions() // 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 @@ -305,7 +310,17 @@ void Client::setupDecorAndFunctions() 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 + } } @@ -624,8 +639,11 @@ void Client::updateStrut() _strut.right = data[1]; _strut.top = data[2]; _strut.bottom = data[3]; - - openbox->screen(_screen)->updateStrut(); + + // 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; @@ -695,7 +713,6 @@ void Client::propertyHandler(const XPropertyEvent &e) 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) @@ -708,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(); @@ -781,6 +797,8 @@ void Client::setState(StateAction action, long data1, long data2) { bool shadestate = _shaded; bool fsstate = _fullscreen; + bool maxh = _max_horz; + bool maxv = _max_vert; if (!(action == State_Add || action == State_Remove || action == State_Toggle)) @@ -819,12 +837,10 @@ void Client::setState(StateAction action, long data1, long data2) _modal = true; // XXX: give it focus if another window has focus that shouldnt now } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) { - if (_max_vert) continue; - _max_vert = true; - // XXX: resize the window etc + maxv = true; } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) { if (_max_horz) continue; - _max_horz = true; + maxh = true; // XXX: resize the window etc } else if (state == otk::Property::atoms.net_wm_state_shaded) { shadestate = true; @@ -847,13 +863,9 @@ void Client::setState(StateAction action, long data1, long data2) if (!_modal) continue; _modal = false; } else if (state == otk::Property::atoms.net_wm_state_maximized_vert) { - if (!_max_vert) continue; - _max_vert = false; - // XXX: resize the window etc + maxv = false; } else if (state == otk::Property::atoms.net_wm_state_maximized_horz) { - if (!_max_horz) continue; - _max_horz = false; - // XXX: resize the window etc + maxh = false; } else if (state == otk::Property::atoms.net_wm_state_shaded) { shadestate = false; } else if (state == otk::Property::atoms.net_wm_state_skip_taskbar) { @@ -871,13 +883,29 @@ void Client::setState(StateAction action, long data1, long data2) } } } + if (maxh != _max_horz || maxv != _max_vert) { + if (maxh != _max_horz && maxv != _max_vert) { // toggling both + if (maxh == maxv) { // both going the same way + maximize(maxh, 0, true); + } else { + maximize(maxh, 1, true); + maximize(maxv, 2, true); + } + } else { // toggling one + if (maxh != _max_horz) + maximize(maxh, 1, true); + else + maximize(maxv, 2, true); + } + } // change fullscreen state before shading, as it will affect if the window // can shade or not if (fsstate != _fullscreen) - fullscreen(fsstate); + fullscreen(fsstate, true); if (shadestate != _shaded) shade(shadestate); calcLayer(); + changeState(); // change the hint to relect these changes } @@ -888,7 +916,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: @@ -937,7 +966,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); } @@ -1041,12 +1071,19 @@ void Client::internal_resize(Corner anchor, int w, int h, bool user, w -= _base_size.x(); h -= _base_size.y(); - // for interactive resizing. have to move half an increment in each - // direction. - w += _size_inc.x() / 2; - h += _size_inc.y() / 2; - if (user) { + // for interactive resizing. have to move half an increment in each + // direction. + int mw = w % _size_inc.x(); // how far we are towards the next size inc + int mh = h % _size_inc.y(); + int aw = _size_inc.x() / 2; // amount to add + int ah = _size_inc.y() / 2; + // don't let us move into a new size increment + if (mw + aw >= _size_inc.x()) aw = _size_inc.x() - mw - 1; + if (mh + ah >= _size_inc.y()) ah = _size_inc.y() - mh - 1; + w += aw; + h += ah; + // if this is a user-requested resize, then check against min/max sizes // and aspect ratios @@ -1131,15 +1168,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 } } @@ -1177,7 +1222,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) @@ -1239,6 +1284,22 @@ void Client::changeAllowedActions(void) } +void Client::remaximize() +{ + int dir; + if (_max_horz && _max_vert) + dir = 0; + else if (_max_horz) + dir = 1; + else if (_max_vert) + dir = 2; + else + return; // not maximized + _max_horz = _max_vert = false; + maximize(true, dir, false); +} + + void Client::applyStartupState() { // these are in a carefully crafted order.. @@ -1249,7 +1310,7 @@ void Client::applyStartupState() } if (_fullscreen) { _fullscreen = false; - fullscreen(true); + fullscreen(true, false); } if (_shaded) { _shaded = false; @@ -1258,8 +1319,16 @@ void Client::applyStartupState() if (_urgent) fireUrgent(); - if (_max_vert); // XXX: incomplete - if (_max_horz); // XXX: incomplete + if (_max_vert && _max_horz) { + _max_vert = _max_horz = false; + maximize(true, 0, false); + } else if (_max_vert) { + _max_vert = false; + maximize(true, 2, false); + } else if (_max_horz) { + _max_horz = false; + maximize(true, 1, false); + } if (_skip_taskbar); // nothing to do for this if (_skip_pager); // nothing to do for this @@ -1291,12 +1360,128 @@ void Client::shade(bool shade) } -void Client::fullscreen(bool fs) +void Client::maximize(bool max, int dir, bool savearea) +{ + assert(dir == 0 || dir == 1 || dir == 2); + if (!(_functions & Func_Maximize)) return; // can't + + // check if already done + if (max) { + if (dir == 0 && _max_horz && _max_vert) return; + if (dir == 1 && _max_horz) return; + if (dir == 2 && _max_vert) return; + } else { + if (dir == 0 && !_max_horz && !_max_vert) return; + if (dir == 1 && !_max_horz) return; + if (dir == 2 && !_max_vert) return; + } + + int g = _gravity; + + const otk::Rect &a = openbox->screen(_screen)->area(); + int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height(); + + if (max) { + // when maximizing, put the client where we want, NOT the frame! + _gravity = StaticGravity; + // adjust our idea of position based on StaticGravity, so we stay put + // unless asked + frame->frameGravity(x, y); + + if (savearea) { + long dimensions[4]; + long *readdim; + unsigned long n = 4; + + dimensions[0] = x; + dimensions[1] = y; + dimensions[2] = w; + dimensions[3] = h; + + // get the property off the window and use it for the dimentions we are + // already maxed on + if (otk::Property::get(_window, otk::Property::atoms.openbox_premax, + otk::Property::atoms.cardinal, &n, + (long unsigned**) &readdim)) { + if (n >= 4) { + if (_max_horz) { + dimensions[0] = readdim[0]; + dimensions[2] = readdim[2]; + } + if (_max_vert) { + dimensions[1] = readdim[1]; + dimensions[3] = readdim[3]; + } + } + delete readdim; + } + + otk::Property::set(_window, otk::Property::atoms.openbox_premax, + otk::Property::atoms.cardinal, + (long unsigned*)dimensions, 4); + } + if (dir == 0 || dir == 1) { // horz + x = a.x(); + w = a.width(); + } + if (dir == 0 || dir == 2) { // vert + y = a.y() + frame->size().top; + h = a.height() - frame->size().top - frame->size().bottom; + } + } else { + long *dimensions; + long unsigned n = 4; + + if (otk::Property::get(_window, otk::Property::atoms.openbox_premax, + otk::Property::atoms.cardinal, &n, + (long unsigned**) &dimensions)) { + if (n >= 4) { + if (dir == 0 || dir == 1) { // horz + x = (signed int)dimensions[0]; + w = (signed int)dimensions[2]; + } + if (dir == 0 || dir == 2) { // vert + y = (signed int)dimensions[1]; + h = (signed int)dimensions[3]; + } + } + delete dimensions; + } else { + // pick some fallbacks... + if (dir == 0 || dir == 1) { // horz + x = a.x() + a.width() / 4; + w = a.width() / 2; + } + if (dir == 0 || dir == 2) { // vert + y = a.y() + a.height() / 4; + h = a.height() / 2; + } + } + otk::Property::erase(_window, otk::Property::atoms.openbox_premax); + } + + if (dir == 0 || dir == 1) // horz + _max_horz = max; + if (dir == 0 || dir == 2) // vert + _max_vert = max; + changeState(); // change the state hints on the client + + internal_resize(TopLeft, w, h, true, x, y); + _gravity = g; + if (max) { + // because of my little gravity trick in here, we have to set the position + // of the client to what it really is + int x, y; + frame->frameGravity(x, y); + _area.setPos(x, y); + } +} + + +void Client::fullscreen(bool fs, bool savearea) { 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 @@ -1304,6 +1489,8 @@ void Client::fullscreen(bool fs) _fullscreen = fs; changeState(); // change the state hints on the client + int x = _area.x(), y = _area.y(), w = _area.width(), h = _area.height(); + if (fs) { // save the functions and remove them saved_func = _functions; @@ -1311,25 +1498,52 @@ void Client::fullscreen(bool fs) // 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; + if (savearea) { + long dimensions[4]; + dimensions[0] = _area.x(); + dimensions[1] = _area.y(); + dimensions[2] = _area.width(); + dimensions[3] = _area.height(); + otk::Property::set(_window, otk::Property::atoms.openbox_premax, + otk::Property::atoms.cardinal, + (long unsigned*)dimensions, 4); + } 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()); + x = 0; + y = 0; + w = info->width(); + h = info->height(); } else { _functions = saved_func; _decorations = saved_decor; - _area = saved_area; - _logical_size = saved_logical_size; + + long *dimensions; + long unsigned n = 4; + + if (otk::Property::get(_window, otk::Property::atoms.openbox_premax, + otk::Property::atoms.cardinal, &n, + (long unsigned**) &dimensions)) { + if (n >= 4) { + x = dimensions[0]; + y = dimensions[1]; + w = dimensions[2]; + h = dimensions[3]; + } + delete dimensions; + } else { + // pick some fallbacks... + const otk::Rect &a = openbox->screen(_screen)->area(); + x = a.x() + a.width() / 4; + y = a.y() + a.height() / 4; + w = a.width() / 2; + h = a.height() / 2; + } } changeAllowedActions(); // based on the new _functions - - frame->adjustSize(); // drop/replace the decor's and resize - frame->adjustPosition(); // get (back) in position! + + // when fullscreening, don't obey things like increments, fill the screen + internal_resize(TopLeft, w, h, !fs, x, y); // raise (back) into our stacking layer openbox->screen(_screen)->raiseWindow(this); @@ -1343,8 +1557,6 @@ void Client::disableDecorations(DecorationFlags flags) { _disabled_decorations = flags; setupDecorAndFunctions(); - if (frame) - frame->adjustSize(); // change the decors on the frame }