X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2FWindow.cc;h=3005674b900bdac5cb62585b5a84ccb2d0dec629;hb=c2afc72a41a4093f1fdfdf3245d5d70ef8bf9ad2;hp=201bea66d7f28bc4c931788d92f0c3ef4a96f847;hpb=35818cc2f103844953c7afb77ab2c5c8fd69b442;p=chaz%2Fopenbox diff --git a/src/Window.cc b/src/Window.cc index 201bea66..3005674b 100644 --- a/src/Window.cc +++ b/src/Window.cc @@ -62,11 +62,6 @@ extern "C" { using std::string; using std::abs; -// change this to change what modifier keys openbox uses for mouse bindings -// for example: Mod1Mask | ControlMask -// or: ControlMask| ShiftMask -const unsigned int ModMask = Mod1Mask; - /* * Initializes the class with default values/the window's set initial values. */ @@ -126,14 +121,15 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { blackbox_attrib.workspace = window_number = BSENTINEL; - blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack - = blackbox_attrib.decoration = 0l; + blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l; + blackbox_attrib.decoration = DecorNormal; blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0; blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0; frame.border_w = 1; frame.window = frame.plate = frame.title = frame.handle = None; - frame.close_button = frame.iconify_button = frame.maximize_button = None; + frame.close_button = frame.iconify_button = frame.maximize_button = + frame.stick_button = None; frame.right_grip = frame.left_grip = None; frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel = @@ -147,7 +143,7 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | - Decor_Iconify | Decor_Maximize | Decor_Close; + Decor_Iconify | Decor_Maximize; client.normal_hint_flags = 0; client.window_group = None; @@ -158,7 +154,7 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { windowmenu = 0; /* - get the initial size and location of client window (relative to the + set the initial size and location of client window (relative to the _root window_). This position is the reference point used with the window's gravity to find the window's initial position. */ @@ -173,9 +169,8 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { // get size, aspect, minimum/maximum size and other hints set by the // client - if (! getBlackboxHints()) { + if (! getBlackboxHints()) getNetWMHints(); - } getWMProtocols(); getWMHints(); @@ -185,7 +180,7 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { blackbox->saveWindowSearch(frame.window, this); - frame.plate = createChildWindow(frame.window); + frame.plate = createChildWindow(frame.window, ExposureMask); blackbox->saveWindowSearch(frame.plate, this); // determine if this is a transient window @@ -218,7 +213,8 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { break; case Type_Normal: - // normal windows retain all of the possible decorations and functionality + // normal windows retain all of the possible decorations and + // functionality break; } } else { @@ -235,7 +231,7 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { setAllowedActions(); - enableDecor(True); + setupDecor(); if (decorations & Decor_Titlebar) createTitlebar(); @@ -407,15 +403,33 @@ BlackboxWindow::~BlackboxWindow(void) { void BlackboxWindow::enableDecor(bool enable) { - if (enable) { + blackbox_attrib.flags |= AttribDecoration; + blackbox_attrib.decoration = enable ? DecorNormal : DecorNone; + setupDecor(); + + // we can not be shaded if we lack a titlebar + if (! (decorations & Decor_Titlebar) && flags.shaded) + shade(); + + if (flags.visible && frame.window) { + XMapSubwindows(blackbox->getXDisplay(), frame.window); + XMapWindow(blackbox->getXDisplay(), frame.window); + } + + reconfigure(); + setState(current_state); +} + + +void BlackboxWindow::setupDecor() { + if (blackbox_attrib.decoration != DecorNone) { // start with everything on - decorations = + decorations = Decor_Close | (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) | (mwm_decorations & Decor_Border ? Decor_Border : 0) | (mwm_decorations & Decor_Handle ? Decor_Handle : 0) | (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) | - (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0) | - (mwm_decorations & Decor_Close ? Decor_Close : 0); + (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0); if (! (functions & Func_Close)) decorations &= ~Decor_Close; if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize; @@ -461,9 +475,13 @@ Window BlackboxWindow::createToplevelWindow(void) { attrib_create.background_pixmap = None; attrib_create.colormap = screen->getColormap(); attrib_create.override_redirect = True; - attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | - EnterWindowMask | LeaveWindowMask; + attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | + ButtonPress; + /* + We catch button presses because other wise they get passed down to the + root window, which will then cause root menus to show when you click the + window's frame. + */ return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(), 0, 0, 1, 1, frame.border_w, screen->getDepth(), @@ -476,14 +494,15 @@ Window BlackboxWindow::createToplevelWindow(void) { * Creates a child window, and optionally associates a given cursor with * the new window. */ -Window BlackboxWindow::createChildWindow(Window parent, Cursor cursor) { +Window BlackboxWindow::createChildWindow(Window parent, + unsigned long event_mask, + Cursor cursor) { XSetWindowAttributes attrib_create; unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask; attrib_create.background_pixmap = None; - attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask | ExposureMask; + attrib_create.event_mask = event_mask; if (cursor) { create_mask |= CWCursor; @@ -578,13 +597,8 @@ void BlackboxWindow::decorate(void) { } if (decorations & Decor_Border) { - frame.fborder_pixel = screen->getWindowStyle()->f_focus.pixel(); - frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.pixel(); - blackbox_attrib.flags |= AttribDecoration; - blackbox_attrib.decoration = DecorNormal; - } else { - blackbox_attrib.flags |= AttribDecoration; - blackbox_attrib.decoration = DecorNone; + frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel(); + frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel(); } if (decorations & Decor_Handle) { @@ -639,15 +653,23 @@ void BlackboxWindow::decorateLabel(void) { void BlackboxWindow::createHandle(void) { - frame.handle = createChildWindow(frame.window); + frame.handle = createChildWindow(frame.window, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask); blackbox->saveWindowSearch(frame.handle, this); frame.left_grip = - createChildWindow(frame.handle, blackbox->getLowerLeftAngleCursor()); + createChildWindow(frame.handle, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask, + blackbox->getLowerLeftAngleCursor()); blackbox->saveWindowSearch(frame.left_grip, this); frame.right_grip = - createChildWindow(frame.handle, blackbox->getLowerRightAngleCursor()); + createChildWindow(frame.handle, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask, + blackbox->getLowerRightAngleCursor()); blackbox->saveWindowSearch(frame.right_grip, this); } @@ -679,8 +701,12 @@ void BlackboxWindow::destroyHandle(void) { void BlackboxWindow::createTitlebar(void) { - frame.title = createChildWindow(frame.window); - frame.label = createChildWindow(frame.title); + frame.title = createChildWindow(frame.window, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + frame.label = createChildWindow(frame.title, + ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask | ExposureMask); blackbox->saveWindowSearch(frame.title, this); blackbox->saveWindowSearch(frame.label, this); @@ -700,6 +726,9 @@ void BlackboxWindow::destroyTitlebar(void) { if (frame.maximize_button) destroyMaximizeButton(); + if (frame.stick_button) + destroyStickyButton(); + if (frame.ftitle) screen->getImageControl()->removeImage(frame.ftitle); @@ -732,7 +761,10 @@ void BlackboxWindow::destroyTitlebar(void) { void BlackboxWindow::createCloseButton(void) { if (frame.title != None) { - frame.close_button = createChildWindow(frame.title); + frame.close_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); blackbox->saveWindowSearch(frame.close_button, this); } } @@ -747,7 +779,10 @@ void BlackboxWindow::destroyCloseButton(void) { void BlackboxWindow::createIconifyButton(void) { if (frame.title != None) { - frame.iconify_button = createChildWindow(frame.title); + frame.iconify_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); blackbox->saveWindowSearch(frame.iconify_button, this); } } @@ -762,7 +797,10 @@ void BlackboxWindow::destroyIconifyButton(void) { void BlackboxWindow::createMaximizeButton(void) { if (frame.title != None) { - frame.maximize_button = createChildWindow(frame.title); + frame.maximize_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); blackbox->saveWindowSearch(frame.maximize_button, this); } } @@ -774,13 +812,28 @@ void BlackboxWindow::destroyMaximizeButton(void) { frame.maximize_button = None; } +void BlackboxWindow::createStickyButton(void) { + if (frame.title != None) { + frame.stick_button = createChildWindow(frame.title, + ButtonPressMask | + ButtonReleaseMask | + ButtonMotionMask | ExposureMask); + blackbox->saveWindowSearch(frame.stick_button, this); + } +} + +void BlackboxWindow::destroyStickyButton(void) { + blackbox->removeWindowSearch(frame.stick_button); + XDestroyWindow(blackbox->getXDisplay(), frame.stick_button); + frame.stick_button = None; +} void BlackboxWindow::positionButtons(bool redecorate_label) { string layout = blackbox->getTitlebarLayout(); string parsed; - bool hasclose, hasiconify, hasmaximize, haslabel; - hasclose = hasiconify = hasmaximize = haslabel = false; + bool hasclose, hasiconify, hasmaximize, haslabel, hasstick; + hasclose = hasiconify = hasmaximize = haslabel = hasstick = false; string::const_iterator it, end; for (it = layout.begin(), end = layout.end(); it != end; ++it) { @@ -797,6 +850,12 @@ void BlackboxWindow::positionButtons(bool redecorate_label) { parsed += *it; } break; + case 'S': + if (!hasstick) { + hasstick = true; + parsed += *it; + } + break; case 'M': if (! hasmaximize && (decorations & Decor_Maximize)) { hasmaximize = true; @@ -808,14 +867,18 @@ void BlackboxWindow::positionButtons(bool redecorate_label) { haslabel = true; parsed += *it; } + break; } } + if (! hasclose && frame.close_button) destroyCloseButton(); if (! hasiconify && frame.iconify_button) destroyIconifyButton(); if (! hasmaximize && frame.maximize_button) destroyMaximizeButton(); + if (! hasstick && frame.stick_button) + destroyStickyButton(); if (! haslabel) parsed += 'L'; // require that the label be in the layout @@ -841,6 +904,12 @@ void BlackboxWindow::positionButtons(bool redecorate_label) { frame.button_w, frame.button_w); x += frame.button_w + bsep; break; + case 'S': + if (! frame.stick_button) createStickyButton(); + XMoveResizeWindow(blackbox->getXDisplay(), frame.stick_button, x, by, + frame.button_w, frame.button_w); + x += frame.button_w + bsep; + break; case 'M': if (! frame.maximize_button) createMaximizeButton(); XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by, @@ -880,6 +949,8 @@ void BlackboxWindow::reconfigure(void) { void BlackboxWindow::grabButtons(void) { + mod_mask = blackbox->getMouseModMask(); + if (! screen->isSloppyFocus() || screen->doClickRaise()) // grab button 1 for changing focus/raising blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask, @@ -887,28 +958,27 @@ void BlackboxWindow::grabButtons(void) { screen->allowScrollLock()); if (functions & Func_Move) - blackbox->grabButton(Button1, ModMask, frame.window, True, + blackbox->grabButton(Button1, mod_mask, frame.window, True, ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, frame.window, None, screen->allowScrollLock()); if (functions & Func_Resize) - blackbox->grabButton(Button3, ModMask, frame.window, True, + blackbox->grabButton(Button3, mod_mask, frame.window, True, ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, frame.window, None, screen->allowScrollLock()); // alt+middle lowers the window - blackbox->grabButton(Button2, ModMask, frame.window, True, + blackbox->grabButton(Button2, mod_mask, frame.window, True, ButtonReleaseMask, GrabModeAsync, GrabModeAsync, - frame.window, None, - screen->allowScrollLock()); + frame.window, None, screen->allowScrollLock()); } void BlackboxWindow::ungrabButtons(void) { blackbox->ungrabButton(Button1, 0, frame.plate); - blackbox->ungrabButton(Button1, ModMask, frame.window); - blackbox->ungrabButton(Button2, ModMask, frame.window); - blackbox->ungrabButton(Button3, ModMask, frame.window); + blackbox->ungrabButton(Button1, mod_mask, frame.window); + blackbox->ungrabButton(Button2, mod_mask, frame.window); + blackbox->ungrabButton(Button3, mod_mask, frame.window); } @@ -996,39 +1066,50 @@ void BlackboxWindow::updateStrut(void) { bool BlackboxWindow::getWindowType(void) { - unsigned long val; + window_type = (WindowType) -1; + + unsigned long *val; + unsigned long num = (unsigned) -1; if (xatom->getValue(client.window, XAtom::net_wm_window_type, XAtom::atom, - val)) { - if (val == xatom->getAtom(XAtom::net_wm_window_type_desktop)) - window_type = Type_Desktop; - else if (val == xatom->getAtom(XAtom::net_wm_window_type_dock)) - window_type = Type_Dock; - else if (val == xatom->getAtom(XAtom::net_wm_window_type_toolbar)) - window_type = Type_Toolbar; - else if (val == xatom->getAtom(XAtom::net_wm_window_type_menu)) - window_type = Type_Menu; - else if (val == xatom->getAtom(XAtom::net_wm_window_type_utility)) - window_type = Type_Utility; - else if (val == xatom->getAtom(XAtom::net_wm_window_type_splash)) - window_type = Type_Splash; - else if (val == xatom->getAtom(XAtom::net_wm_window_type_dialog)) + num, &val)) { + for (unsigned long i = 0; i < num; ++i) { + if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_desktop)) + window_type = Type_Desktop; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dock)) + window_type = Type_Dock; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_toolbar)) + window_type = Type_Toolbar; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_menu)) + window_type = Type_Menu; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_utility)) + window_type = Type_Utility; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_splash)) + window_type = Type_Splash; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_dialog)) + window_type = Type_Dialog; + else if (val[i] == xatom->getAtom(XAtom::net_wm_window_type_normal)) + window_type = Type_Normal; + else if (val[i] == + xatom->getAtom(XAtom::kde_net_wm_window_type_override)) + mwm_decorations = 0; // prevent this window from getting any decor + } + delete val; + } + + if (window_type == (WindowType) -1) { + /* + * the window type hint was not set, which means we either classify ourself + * as a normal window or a dialog, depending on if we are a transient. + */ + if (isTransient()) window_type = Type_Dialog; - else //if (val[0] == xatom->getAtom(XAtom::net_wm_window_type_normal)) + else window_type = Type_Normal; - return True; + return False; } - /* - * the window type hint was not set, which means we either classify ourself - * as a normal window or a dialog, depending on if we are a transient. - */ - if (isTransient()) - window_type = Type_Dialog; - - window_type = Type_Normal; - - return False; + return True; } @@ -1049,6 +1130,14 @@ void BlackboxWindow::getWMName(void) { client.title = i18n(WindowSet, WindowUnnamed, "Unnamed"); xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8, client.title); + +#ifdef DEBUG_WITH_ID + // the 16 is the 8 chars of the debug text plus the number + char *tmp = new char[client.title.length() + 16]; + sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window); + client.title = tmp; + delete tmp; +#endif } @@ -1169,14 +1258,11 @@ void BlackboxWindow::getWMNormalHints(void) { client.max_aspect_x = client.max_aspect_y = 1; #endif - /* - use the full screen, not the strut modified size. otherwise when the - availableArea changes max_width/height will be incorrect and lead to odd - rendering bugs. - */ - const Rect& screen_area = screen->getRect(); - client.max_width = screen_area.width(); - client.max_height = screen_area.height(); + // don't limit the size of a window, the default max width is the biggest + // possible + client.max_width = (unsigned) -1; + client.max_height = (unsigned) -1; + if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window, &sizehint, &icccm_mask)) @@ -1301,7 +1387,7 @@ void BlackboxWindow::getMWMHints(void) { if (mwm_hint->flags & MwmHintsDecorations) { if (mwm_hint->decorations & MwmDecorAll) { mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | - Decor_Iconify | Decor_Maximize | Decor_Close; + Decor_Iconify | Decor_Maximize; } else { mwm_decorations = 0; @@ -1387,18 +1473,16 @@ bool BlackboxWindow::getBlackboxHints(void) { if (blackbox_hint->flags & AttribDecoration) { switch (blackbox_hint->decoration) { case DecorNone: - enableDecor(False); + blackbox_attrib.decoration = DecorNone; break; case DecorTiny: case DecorTool: case DecorNormal: default: - enableDecor(True); + // blackbox_attrib.decoration defaults to DecorNormal break; } - - reconfigure(); } delete [] blackbox_hint; @@ -1415,7 +1499,7 @@ void BlackboxWindow::getTransientInfo(void) { } // we have no transient_for until we find a new one - client.transient_for = 0; + client.transient_for = (BlackboxWindow *) 0; Window trans_for; if (! XGetTransientForHint(blackbox->getXDisplay(), client.window, @@ -1587,6 +1671,14 @@ void BlackboxWindow::configureShape(void) { ShapeBounding, 0, 0, xrect, num, ShapeUnion, Unsorted); } + + +void BlackboxWindow::clearShape(void) { + XShapeCombineMask(blackbox->getXDisplay(), frame.window, ShapeBounding, + frame.margin.left - frame.border_w, + frame.margin.top - frame.border_w, + None, ShapeSet); +} #endif // SHAPE @@ -1650,7 +1742,7 @@ bool BlackboxWindow::setInputFocus(void) { void BlackboxWindow::iconify(void) { - if (flags.iconic) return; + if (flags.iconic || ! (functions & Func_Iconify)) return; // We don't need to worry about resizing because resizing always grabs the X // server. This should only ever happen if using opaque moving. @@ -1754,6 +1846,8 @@ void BlackboxWindow::deiconify(bool reassoc, bool raise) { void BlackboxWindow::close(void) { + if (! (functions & Func_Close)) return; + XEvent ce; ce.xclient.type = ClientMessage; ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols); @@ -1799,6 +1893,8 @@ void BlackboxWindow::withdraw(void) { void BlackboxWindow::maximize(unsigned int button) { + if (! (functions & Func_Maximize)) return; + // We don't need to worry about resizing because resizing always grabs the X // server. This should only ever happen if using opaque moving. if (flags.moving) @@ -2021,6 +2117,9 @@ void BlackboxWindow::stick(void) { setState(current_state); } + + redrawAllButtons(); + // go up the chain if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && client.transient_for->isStuck() != flags.stuck) @@ -2320,18 +2419,6 @@ void BlackboxWindow::restoreAttributes(void) { enableDecor(True); break; } - - // we can not be shaded if we lack a titlebar - if (! (decorations & Decor_Titlebar) && flags.shaded) - shade(); - - if (flags.visible && frame.window) { - XMapSubwindows(blackbox->getXDisplay(), frame.window); - XMapWindow(blackbox->getXDisplay(), frame.window); - } - - reconfigure(); - setState(current_state); } // with the state set it will then be the map event's job to read the @@ -2498,6 +2585,7 @@ void BlackboxWindow::redrawAllButtons(void) const { if (frame.iconify_button) redrawIconifyButton(False); if (frame.maximize_button) redrawMaximizeButton(flags.maximized); if (frame.close_button) redrawCloseButton(False); + if (frame.stick_button) redrawStickyButton(flags.stuck); } @@ -2526,12 +2614,31 @@ void BlackboxWindow::redrawIconifyButton(bool pressed) const { XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button, frame.pbutton_pixel); } - XClearWindow(blackbox->getXDisplay(), frame.iconify_button); + XClearWindow(blackbox->getXDisplay(), frame.iconify_button); BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : - screen->getWindowStyle()->b_pic_unfocus); - XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(), - 2, (frame.button_w - 5), (frame.button_w - 5), 2); + screen->getWindowStyle()->b_pic_unfocus); +#ifdef BITMAPBUTTONS + PixmapMask pm = screen->getWindowStyle()->icon_button; + + if (screen->getWindowStyle()->icon_button.mask != None) { + XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask); + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + XSetClipMask(blackbox->getXDisplay(), pen.gc(), None); + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0); + } else { +#endif // BITMAPBUTTONS + XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(), + 2, (frame.button_w - 5), (frame.button_w - 5), 2); +#ifdef BITMAPBUTTONS + } +#endif // BITMAPBUTTONS } @@ -2564,10 +2671,30 @@ void BlackboxWindow::redrawMaximizeButton(bool pressed) const { BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : screen->getWindowStyle()->b_pic_unfocus); - XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), - 2, 2, (frame.button_w - 5), (frame.button_w - 5)); - XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), - 2, 3, (frame.button_w - 3), 3); + +#ifdef BITMAPBUTTONS + PixmapMask pm = screen->getWindowStyle()->max_button; + + if (pm.mask != None) { + XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask); + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 ); + XSetClipMask( blackbox->getXDisplay(), pen.gc(), None ); + } else { +#endif // BITMAPBUTTONS + XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), + 2, 2, (frame.button_w - 5), (frame.button_w - 5)); + XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(), + 2, 3, (frame.button_w - 3), 3); +#ifdef BITMAPBUTTONS + } +#endif // BITMAPBUTTONS } @@ -2575,8 +2702,8 @@ void BlackboxWindow::redrawCloseButton(bool pressed) const { if (! pressed) { if (flags.focused) { if (frame.fbutton) - XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button, - frame.fbutton); + XSetWindowBackgroundPixmap(blackbox->getXDisplay(), + frame.close_button, frame.fbutton); else XSetWindowBackground(blackbox->getXDisplay(), frame.close_button, frame.fbutton_pixel); @@ -2600,12 +2727,86 @@ void BlackboxWindow::redrawCloseButton(bool pressed) const { BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : screen->getWindowStyle()->b_pic_unfocus); - XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(), - 2, 2, (frame.button_w - 3), (frame.button_w - 3)); - XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(), - 2, (frame.button_w - 3), (frame.button_w - 3), 2); + +#ifdef BITMAPBUTTONS + PixmapMask pm = screen->getWindowStyle()->close_button; + + if (pm.mask != None) { + XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask); + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(blackbox->getXDisplay(), frame.close_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 ); + XSetClipMask( blackbox->getXDisplay(), pen.gc(), None ); + } else { +#endif // BITMAPBUTTONS + XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(), + 2, 2, (frame.button_w - 3), (frame.button_w - 3)); + XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(), + 2, (frame.button_w - 3), (frame.button_w - 3), 2); +#ifdef BITMAPBUTTONS + } +#endif // BITMAPBUTTONS } +void BlackboxWindow::redrawStickyButton(bool pressed) const { + if (! pressed) { + if (flags.focused) { + if (frame.fbutton) + XSetWindowBackgroundPixmap(blackbox->getXDisplay(), + frame.stick_button, frame.fbutton); + else + XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button, + frame.fbutton_pixel); + } else { + if (frame.ubutton) + XSetWindowBackgroundPixmap(blackbox->getXDisplay(), + frame.stick_button, frame.ubutton); + else + XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button, + frame.ubutton_pixel); + } + } else { + if (frame.pbutton) + XSetWindowBackgroundPixmap(blackbox->getXDisplay(), + frame.stick_button, frame.pbutton); + else + XSetWindowBackground(blackbox->getXDisplay(), frame.stick_button, + frame.pbutton_pixel); + } + XClearWindow(blackbox->getXDisplay(), frame.stick_button); + + BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus : + screen->getWindowStyle()->b_pic_unfocus); + +#ifdef BITMAPBUTTONS + PixmapMask pm = screen->getWindowStyle()->stick_button; + + if (pm.mask != None) { + XSetClipMask(blackbox->getXDisplay(), pen.gc(), pm.mask); + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2); + + XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(), + (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2, + (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2); + + + XSetClipOrigin(blackbox->getXDisplay(), pen.gc(), 0, 0 ); + XSetClipMask( blackbox->getXDisplay(), pen.gc(), None ); + } else { +#endif // BITMAPBUTTONS + XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(), + frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 ); +#ifdef BITMAPBUTTONS + } +#endif +} void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { if (re->window != client.window) @@ -2616,6 +2817,15 @@ void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { client.window); #endif // DEBUG + /* + Even though the window wants to be shown, if it is not on the current + workspace, then it isn't going to be shown right now. + */ + if (! flags.stuck && + blackbox_attrib.workspace != screen->getCurrentWorkspaceID() && + blackbox_attrib.workspace < screen->getWorkspaceCount()) + if (current_state == NormalState) current_state = WithdrawnState; + switch (current_state) { case IconicState: iconify(); @@ -2634,8 +2844,8 @@ void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { if (isNormal()) { if (! blackbox->isStartup()) { XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped - if (screen->doFocusNew()|| (isTransient() && getTransientFor() && - getTransientFor()->isFocused())) { + if (screen->doFocusNew() || (isTransient() && getTransientFor() && + getTransientFor()->isFocused())) { setInputFocus(); } if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) { @@ -2696,10 +2906,10 @@ void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) { void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { - if (pe->state == PropertyDelete) + if (pe->state == PropertyDelete || ! validateClient()) return; -#ifdef DEBUG +#if 0 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n", client.window); #endif @@ -2711,14 +2921,18 @@ void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { break; case XA_WM_TRANSIENT_FOR: { + bool s = flags.stuck; + // determine if this is a transient window getTransientInfo(); + if (flags.stuck != s) stick(); + // adjust the window decorations based on transience if (isTransient()) { functions &= ~Func_Maximize; setAllowedActions(); - enableDecor(True); + setupDecor(); } reconfigure(); @@ -2762,7 +2976,7 @@ void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { } grabButtons(); setAllowedActions(); - enableDecor(True); + setupDecor(); } Rect old_rect = frame.rect; @@ -2797,7 +3011,7 @@ void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { void BlackboxWindow::exposeEvent(const XExposeEvent *ee) { -#ifdef DEBUG +#if 0 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window); #endif @@ -2809,6 +3023,8 @@ void BlackboxWindow::exposeEvent(const XExposeEvent *ee) { redrawMaximizeButton(flags.maximized); else if (frame.iconify_button == ee->window) redrawIconifyButton(False); + else if (frame.stick_button == ee->window) + redrawStickyButton(flags.stuck); } @@ -2820,7 +3036,7 @@ void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) { client.old_bw = cr->border_width; if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) { - Rect req = frame.rect; + frame.changing = frame.rect; if (cr->value_mask & (CWX | CWY)) { if (cr->value_mask & CWX) @@ -2828,16 +3044,45 @@ void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) { if (cr->value_mask & CWY) client.rect.setY(cr->y); - applyGravity(req); + applyGravity(frame.changing); } - if (cr->value_mask & CWWidth) - req.setWidth(cr->width + frame.margin.left + frame.margin.right); + if (cr->value_mask & (CWWidth | CWHeight)) { + if (cr->value_mask & CWWidth) + frame.changing.setWidth(cr->width + + frame.margin.left + frame.margin.right); + + if (cr->value_mask & CWHeight) + frame.changing.setHeight(cr->height + + frame.margin.top + frame.margin.bottom); - if (cr->value_mask & CWHeight) - req.setHeight(cr->height + frame.margin.top + frame.margin.bottom); + /* + if a position change has been specified, then that position will be + used instead of determining a position based on the window's gravity. + */ + if (! (cr->value_mask & (CWX | CWY))) { + Corner corner; + switch (client.win_gravity) { + case NorthEastGravity: + case EastGravity: + corner = TopRight; + break; + case SouthWestGravity: + case SouthGravity: + corner = BottomLeft; + break; + case SouthEastGravity: + corner = BottomRight; + break; + default: // NorthWest, Static, etc + corner = TopLeft; + } + constrain(corner); + } + } - configure(req.x(), req.y(), req.width(), req.height()); + configure(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); } if (cr->value_mask & CWStackMode && !isDesktop()) { @@ -2865,7 +3110,7 @@ void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { if (frame.maximize_button == be->window && be->button <= 3) { redrawMaximizeButton(True); - } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) { + } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) { if (! flags.focused) setInputFocus(); @@ -2873,6 +3118,8 @@ void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { redrawIconifyButton(True); } else if (frame.close_button == be->window) { redrawCloseButton(True); + } else if (frame.stick_button == be->window) { + redrawStickyButton(True); } else if (frame.plate == be->window) { if (windowmenu && windowmenu->isVisible()) windowmenu->hide(); @@ -2896,7 +3143,8 @@ void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this); } } else if (be->button == 2 && (be->window != frame.iconify_button) && - (be->window != frame.close_button)) { + (be->window != frame.close_button) && + (be->window != frame.stick_button)) { screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this); } else if (windowmenu && be->button == 3 && (frame.title == be->window || frame.label == be->window || @@ -2944,7 +3192,8 @@ void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { be->window == frame.title || be->window == frame.maximize_button || be->window == frame.iconify_button || - be->window == frame.close_button) && + be->window == frame.close_button || + be->window == frame.stick_button) && ! flags.shaded) shade(); // mouse wheel down @@ -2953,7 +3202,8 @@ void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) { be->window == frame.title || be->window == frame.maximize_button || be->window == frame.iconify_button || - be->window == frame.close_button) && + be->window == frame.close_button || + be->window == frame.stick_button) && flags.shaded) shade(); } @@ -2981,6 +3231,13 @@ void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) { } else { redrawIconifyButton(False); } + } else if (re->window == frame.stick_button && re->button == 1) { + if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && + (re->y >= 0 && re->y <= static_cast(frame.button_w))) { + stick(); + } else { + redrawStickyButton(False); + } } else if (re->window == frame.close_button & re->button == 1) { if ((re->x >= 0 && re->x <= static_cast(frame.button_w)) && (re->y >= 0 && re->y <= static_cast(frame.button_w))) @@ -2991,7 +3248,7 @@ void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) { } else if (flags.resizing) { endResize(); } else if (re->window == frame.window) { - if (re->button == 2 && re->state == ModMask) + if (re->button == 2 && re->state == mod_mask) XUngrabPointer(blackbox->getXDisplay(), CurrentTime); } } @@ -2999,6 +3256,8 @@ void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) { void BlackboxWindow::beginMove(int x_root, int y_root) { + if (! (functions & Func_Move)) return; + assert(! (flags.resizing || flags.moving)); /* @@ -3051,13 +3310,12 @@ void BlackboxWindow::doMove(int x_root, int y_root) { dx -= frame.border_w; dy -= frame.border_w; - if (screen->doWorkspaceWarping()) - if (doWorkspaceWarping(x_root, y_root, dx, dy)) - return; - doWindowSnapping(dx, dy); if (screen->doOpaqueMove()) { + if (screen->doWorkspaceWarping()) + doWorkspaceWarping(x_root, y_root, dx); + configure(dx, dy, frame.rect.width(), frame.rect.height()); } else { XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), @@ -3067,6 +3325,9 @@ void BlackboxWindow::doMove(int x_root, int y_root) { frame.changing.width() - 1, frame.changing.height() - 1); + if (screen->doWorkspaceWarping()) + doWorkspaceWarping(x_root, y_root, dx); + frame.changing.setPos(dx, dy); XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), @@ -3081,8 +3342,7 @@ void BlackboxWindow::doMove(int x_root, int y_root) { } -bool BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, - int dx, int dy) { +void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) { // workspace warping bool warp = False; unsigned int dest = screen->getCurrentWorkspaceID(); @@ -3099,40 +3359,41 @@ bool BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, else dest = 0; } if (! warp) - return false; + return; - endMove(); bool focus = flags.focused; // had focus while moving? + + int dest_x = x_root; + if (x_root < 0) { + dest_x += screen->getRect().width() - 1; + dx += screen->getRect().width() - 1; + } else { + dest_x -= screen->getRect().width() - 1; + dx -= screen->getRect().width() - 1; + } + if (! flags.stuck) screen->reassociateWindow(this, dest, False); screen->changeWorkspaceID(dest); - if (focus) - setInputFocus(); - /* - We grab the X server here because we are moving the window and then the - mouse cursor. When one moves, it could end up putting the mouse cursor - over another window for a moment. This can cause the warp to iniate a - move on another window. - */ - XGrabServer(blackbox->getXDisplay()); - int dest_x; - if (x_root <= 0) { - dest_x = screen->getRect().right() - 1; - configure(dx + (screen->getRect().width() - 1), dy, - frame.rect.width(), frame.rect.height()); - } else { - dest_x = 0; - configure(dx - (screen->getRect().width() - 1), dy, - frame.rect.width(), frame.rect.height()); - } + if (screen->doOpaqueMove()) + XGrabServer(blackbox->getXDisplay()); + + XUngrabPointer(blackbox->getXDisplay(), CurrentTime); XWarpPointer(blackbox->getXDisplay(), None, screen->getRootWindow(), 0, 0, 0, 0, dest_x, y_root); - XUngrabServer(blackbox->getXDisplay()); + XGrabPointer(blackbox->getXDisplay(), frame.window, False, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, + None, blackbox->getMoveCursor(), CurrentTime); + + if (screen->doOpaqueMove()) + XUngrabServer(blackbox->getXDisplay()); + + if (focus) + setInputFocus(); - beginMove(dest_x, y_root); - return true; } @@ -3458,11 +3719,14 @@ void BlackboxWindow::endMove(void) { void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) { + if (! (functions & Func_Resize)) return; + assert(! (flags.resizing || flags.moving)); /* - Only one window can be moved/resized at a time. If another window is already - being moved or resized, then stop it before whating to work with this one. + Only one window can be moved/resized at a time. If another window is + already being moved or resized, then stop it before whating to work with + this one. */ BlackboxWindow *changing = blackbox->getChangingWindow(); if (changing && changing != this) { @@ -3537,34 +3801,42 @@ void BlackboxWindow::doResize(int x_root, int y_root) { unsigned int gw, gh; Corner anchor; + int dx, dy; // the amount of change in the size of the window switch (resize_dir) { case BottomLeft: anchor = TopRight; - frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x), - frame.rect.height() + (y_root - frame.grab_y)); + dx = - (x_root - frame.grab_x); + dy = + (y_root - frame.grab_y); break; case BottomRight: anchor = TopLeft; - frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x), - frame.rect.height() + (y_root - frame.grab_y)); + dx = + (x_root - frame.grab_x); + dy = + (y_root - frame.grab_y); break; case TopLeft: anchor = BottomRight; - frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x), - frame.rect.height() - (y_root - frame.grab_y)); + dx = - (x_root - frame.grab_x); + dy = - (y_root - frame.grab_y); break; case TopRight: anchor = BottomLeft; - frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x), - frame.rect.height() - (y_root - frame.grab_y)); + dx = + (x_root - frame.grab_x); + dy = - (y_root - frame.grab_y); break; default: assert(false); // unhandled Corner return; // unreachable, for the compiler } - + + // make sure the user cant resize the window smaller than 0, which makes it + // wrap around and become huge + if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width(); + if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height(); + + frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy); + constrain(anchor, &gw, &gh); XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), @@ -3606,7 +3878,7 @@ void BlackboxWindow::endResize(void) { void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) { -#ifdef DEBUG +#if 0 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n", client.window); #endif @@ -3616,15 +3888,20 @@ void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) { } else if (flags.resizing) { doResize(me->x_root, me->y_root); } else { - if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) && + if ((functions & Func_Move) && + (me->state & Button1Mask) && (frame.title == me->window || frame.label == me->window || frame.handle == me->window || frame.window == me->window)) { beginMove(me->x_root, me->y_root); } else if ((functions & Func_Resize) && - (me->state & Button1Mask && (me->window == frame.right_grip || - me->window == frame.left_grip)) || - (me->state & Button3Mask && me->state & ModMask && - me->window == frame.window)) { + ((me->state & Button1Mask) && + (me->window == frame.right_grip || + me->window == frame.left_grip)) || + ((me->state & Button3Mask) && (me->state & mod_mask) && + (frame.title == me->window || frame.label == me->window || + frame.handle == me->window || frame.window == me->window || + frame.right_grip == me->window || + frame.left_grip == me->window))) { unsigned int zones = screen->getResizeZones(); Corner corner; @@ -3666,14 +3943,21 @@ void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) { } } - if ((! leave || inferior) && ! isFocused()) { - bool success = setInputFocus(); - if (success) // if focus succeeded install the colormap - installColormap(True); // XXX: shouldnt we honour no install? - } + if (! leave || inferior) { + if (! isFocused()) { + bool success = setInputFocus(); + if (success) // if focus succeeded install the colormap + installColormap(True); // XXX: shouldnt we honour no install? - if (screen->doAutoRaise()) - timer->start(); + /* + We only auto-raise when the window wasn't focused because otherwise + we run into problems with gtk+ drop-down lists. The window ends up + raising over the list. + */ + if (screen->doAutoRaise()) + timer->start(); + } + } } @@ -3689,9 +3973,15 @@ void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) { #ifdef SHAPE -void BlackboxWindow::shapeEvent(XShapeEvent *) { - if (blackbox->hasShapeExtensions() && flags.shaped) { - configureShape(); +void BlackboxWindow::shapeEvent(XShapeEvent *e) { + if (blackbox->hasShapeExtensions()) { + if (! e->shaped && flags.shaped) { + clearShape(); + flags.shaped = False; + } else if (e->shaped) { + configureShape(); + flags.shaped = True; + } } } #endif // SHAPE @@ -3723,6 +4013,12 @@ void BlackboxWindow::restore(bool remap) { if (flags.shaded && ! flags.iconic) setState(NormalState); + // erase the netwm stuff that we read when a window maps, so that it + // doesn't persist between mappings. + // (these are the ones read in getNetWMFlags().) + xatom->eraseValue(client.window, XAtom::net_wm_desktop); + xatom->eraseValue(client.window, XAtom::net_wm_state); + restoreGravity(client.rect); XUnmapWindow(blackbox->getXDisplay(), frame.window); @@ -3808,18 +4104,6 @@ void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) { enableDecor(True); break; } - - // we can not be shaded if we lack a titlebar - if (flags.shaded && ! (decorations & Decor_Titlebar)) - shade(); - - if (flags.visible && frame.window) { - XMapSubwindows(blackbox->getXDisplay(), frame.window); - XMapWindow(blackbox->getXDisplay(), frame.window); - } - - reconfigure(); - setState(current_state); } } @@ -3929,11 +4213,17 @@ void BlackboxWindow::constrain(Corner anchor, base_height = (client.base_height) ? client.base_height : client.min_height; - // constrain - if (dw < client.min_width) dw = client.min_width; - if (dh < client.min_height) dh = client.min_height; - if (dw > client.max_width) dw = client.max_width; - if (dh > client.max_height) dh = client.max_height; + // constrain, but only if the min/max are being used. if they aren't, then + // this resize is going to be from a ConfigureRequest because the window + // isn't allowed to be resized by the user. And in that case, we don't want + // to limit what the app can do + if (client.max_width > client.min_width || + client.max_height > client.min_height) { + if (dw < client.min_width) dw = client.min_width; + if (dh < client.min_height) dh = client.min_height; + if (dw > client.max_width) dw = client.max_width; + if (dh > client.max_height) dh = client.max_height; + } assert(dw >= base_width && dh >= base_height);