X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2FWindow.cc;h=dcbb5f08afc55654e5c2f1806fd7d6cbaf33670a;hb=7f001973a8154b1fa9db110092c438b862f81c3b;hp=6131ec4917b21524ac73a5c1635aa40ed70a5e3e;hpb=5cb2020b9b9cd146d7a5dffa47b44110d8d963ff;p=chaz%2Fopenbox diff --git a/src/Window.cc b/src/Window.cc index 6131ec49..dcbb5f08 100644 --- a/src/Window.cc +++ b/src/Window.cc @@ -38,9 +38,11 @@ extern "C" { # include # endif // HAVE_STDIO_H #endif // DEBUG -} -#include +#ifdef HAVE_STDLIB_H + #include +#endif // HAVE_STDLIB_H +} #include "i18n.hh" #include "blackbox.hh" @@ -58,11 +60,7 @@ extern "C" { #include "Slit.hh" using std::string; - -// change this to change what modifier keys openbox uses for mouse bindings -// for example: Mod1Mask | ControlMask -// or: ControlMask| ShiftMask -const unsigned int ModMask = Mod1Mask; +using std::abs; /* * Initializes the class with default values/the window's set initial values. @@ -91,21 +89,11 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { return; } - // set the eventmask early in the game so that we make sure we get - // all the events we are interested in - XSetWindowAttributes attrib_set; - attrib_set.event_mask = PropertyChangeMask | FocusChangeMask | - StructureNotifyMask; - attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | - ButtonMotionMask; - XChangeWindowAttributes(blackbox->getXDisplay(), client.window, - CWEventMask|CWDontPropagate, &attrib_set); - // fetch client size and placement XWindowAttributes wattrib; - if ((! XGetWindowAttributes(blackbox->getXDisplay(), - client.window, &wattrib)) || - (! wattrib.screen) || wattrib.override_redirect) { + if (! XGetWindowAttributes(blackbox->getXDisplay(), + client.window, &wattrib) || + ! wattrib.screen || wattrib.override_redirect) { #ifdef DEBUG fprintf(stderr, "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n"); @@ -115,6 +103,16 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { return; } + // set the eventmask early in the game so that we make sure we get + // all the events we are interested in + XSetWindowAttributes attrib_set; + attrib_set.event_mask = PropertyChangeMask | FocusChangeMask | + StructureNotifyMask; + attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | + ButtonMotionMask; + XChangeWindowAttributes(blackbox->getXDisplay(), client.window, + CWEventMask|CWDontPropagate, &attrib_set); + flags.moving = flags.resizing = flags.shaded = flags.visible = flags.iconic = flags.focused = flags.stuck = flags.modal = flags.send_focus_message = flags.shaped = flags.skip_taskbar = @@ -123,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 = @@ -142,98 +141,108 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None; frame.pbutton = frame.ugrip = frame.fgrip = None; - decorations = Decor_Titlebar | Decor_Border | Decor_Handle | - Decor_Iconify | Decor_Maximize; functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize; + mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize; - client.wm_hint_flags = client.normal_hint_flags = 0; + client.normal_hint_flags = 0; client.window_group = None; client.transient_for = 0; + current_state = NormalState; + + 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. */ client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height); client.old_bw = wattrib.border_width; - windowmenu = 0; lastButtonPressTime = 0; timer = new BTimer(blackbox, this); timer->setTimeout(blackbox->getAutoRaiseDelay()); - if (! getBlackboxHints()) { - getMWMHints(); - getNetWMHints(); - } - // get size, aspect, minimum/maximum size and other hints set by the // client + + if (! getBlackboxHints()) + getNetWMHints(); + getWMProtocols(); getWMHints(); getWMNormalHints(); - if (client.initial_state == WithdrawnState) { - screen->getSlit()->addClient(client.window); - delete this; - return; - } - frame.window = createToplevelWindow(); - frame.plate = createChildWindow(frame.window); - associateClientWindow(); blackbox->saveWindowSearch(frame.window, this); + + frame.plate = createChildWindow(frame.window, ExposureMask); blackbox->saveWindowSearch(frame.plate, this); - blackbox->saveWindowSearch(client.window, this); // determine if this is a transient window getTransientInfo(); // determine the window's type, so we can decide its decorations and // functionality, or if we should not manage it at all - getWindowType(); - - // adjust the window decorations/behavior based on the window type - switch (window_type) { - case Type_Desktop: - case Type_Dock: - case Type_Menu: - case Type_Toolbar: - case Type_Utility: - case Type_Splash: - // none of these windows are decorated or manipulated by the window manager - decorations = 0; - functions = 0; - blackbox_attrib.workspace = 0; // we do need to belong to a workspace - flags.stuck = True; // we show up on all workspaces - break; + if (getWindowType()) { + // adjust the window decorations/behavior based on the window type + switch (window_type) { + case Type_Desktop: + case Type_Dock: + case Type_Menu: + blackbox_attrib.workspace = 0; // we do need to belong to a workspace + flags.stuck = True; // we show up on all workspaces + case Type_Splash: + // none of these windows are manipulated by the window manager + functions = 0; + break; - case Type_Dialog: - // dialogs cannot be maximized, and don't display a handle - decorations &= ~(Decor_Maximize | Decor_Handle); - functions &= ~Func_Maximize; - break; + case Type_Toolbar: + case Type_Utility: + // these windows get less functionality + functions &= ~(Func_Maximize | Func_Resize | Func_Iconify); + break; - case Type_Normal: - // normal windows retain all of the possible decorations and functionality - break; - } + case Type_Dialog: + // dialogs cannot be maximized + functions &= ~Func_Maximize; + break; + case Type_Normal: + // normal windows retain all of the possible decorations and + // functionality + break; + } + } else { + getMWMHints(); + } + // further adjeust the window's decorations/behavior based on window sizes if ((client.normal_hint_flags & PMinSize) && (client.normal_hint_flags & PMaxSize) && client.max_width <= client.min_width && client.max_height <= client.min_height) { - decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~(Func_Resize | Func_Maximize); } - upsize(); - + setAllowedActions(); + setupDecor(); + + if (decorations & Decor_Titlebar) + createTitlebar(); + + if (decorations & Decor_Handle) + createHandle(); + + // apply the size and gravity hint to the frame + + upsize(); + bool place_window = True; if (blackbox->isStartup() || isTransient() || client.normal_hint_flags & (PPosition|USPosition)) { @@ -247,19 +256,17 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { screen->addStrut(&client.strut); updateStrut(); - if (decorations & Decor_Titlebar) - createTitlebar(); + /* + the server needs to be grabbed here to prevent client's from sending + events while we are in the process of configuring their window. + We hold the grab until after we are done moving the window around. + */ - if (decorations & Decor_Handle) - createHandle(); + XGrabServer(blackbox->getXDisplay()); -#ifdef SHAPE - if (blackbox->hasShapeExtensions() && flags.shaped) { - configureShape(); - } -#endif // SHAPE + associateClientWindow(); - windowmenu = new Windowmenu(this); + blackbox->saveWindowSearch(client.window, this); if (blackbox_attrib.workspace >= screen->getWorkspaceCount()) screen->getCurrentWorkspace()->addWindow(this, place_window); @@ -272,32 +279,50 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { // place the window configure(frame.rect.x(), frame.rect.y(), frame.rect.width(), frame.rect.height()); + } + positionWindows(); + + XUngrabServer(blackbox->getXDisplay()); + +#ifdef SHAPE + if (blackbox->hasShapeExtensions() && flags.shaped) + configureShape(); +#endif // SHAPE + + // now that we know where to put the window and what it should look like + // we apply the decorations + decorate(); + + grabButtons(); + + XMapSubwindows(blackbox->getXDisplay(), frame.window); + + // this ensures the title, buttons, and other decor are properly displayed + redrawWindowFrame(); + // preserve the window's initial state on first map, and its current state // across a restart - if (! getState()) { - if (client.wm_hint_flags & StateHint) - current_state = client.initial_state; - else - current_state = NormalState; - } + unsigned long initial_state = current_state; + if (! getState()) + current_state = initial_state; // get sticky state from our parent window if we've got one if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && client.transient_for->isStuck() != flags.stuck) - stick(); + flags.stuck = True; if (flags.shaded) { flags.shaded = False; - unsigned long orig_state = current_state; + initial_state = current_state; shade(); /* At this point in the life of a window, current_state should only be set to IconicState if the window was an *icon*, not if it was shaded. */ - if (orig_state != IconicState) + if (initial_state != IconicState) current_state = NormalState; } @@ -306,27 +331,11 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) { stick(); } - if (flags.maximized && (functions & Func_Maximize)) { + if (flags.maximized && (functions & Func_Maximize)) remaximize(); - } - /* - When the window is mapped (and also when its attributes are restored), the - current_state that was set here will be used. - It is set to Normal if the window is to be mapped or it is set to Iconic - if the window is to be iconified. - *Note* that for sticky windows, the same rules apply here, they are in - fact never set to Iconic since there is no way for us to tell if a sticky - window was iconified previously. - */ - - positionWindows(); - decorate(); - grabButtons(); - - XMapSubwindows(blackbox->getXDisplay(), frame.window); - - redrawWindowFrame(); + // create this last so it only needs to be configured once + windowmenu = new Windowmenu(this); } @@ -339,6 +348,9 @@ BlackboxWindow::~BlackboxWindow(void) { if (! timer) // window not managed... return; + if (flags.moving) + endMove(); + screen->removeStrut(&client.strut); screen->updateAvailableArea(); @@ -358,18 +370,16 @@ BlackboxWindow::~BlackboxWindow(void) { // remove ourselves from our transient_for if (isTransient()) { - if (client.transient_for != (BlackboxWindow *) ~0ul) { + if (client.transient_for != (BlackboxWindow *) ~0ul) client.transient_for->client.transientList.remove(this); - } client.transient_for = (BlackboxWindow*) 0; } if (client.transientList.size() > 0) { // reset transient_for for all transients BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) { + for (it = client.transientList.begin(); it != end; ++it) (*it)->client.transient_for = (BlackboxWindow*) 0; - } } if (frame.title) @@ -392,6 +402,66 @@ BlackboxWindow::~BlackboxWindow(void) { } +void BlackboxWindow::enableDecor(bool 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 = 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); + + if (! (functions & Func_Close)) decorations &= ~Decor_Close; + if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize; + if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify; + if (! (functions & Func_Resize)) decorations &= ~Decor_Handle; + + switch (window_type) { + case Type_Desktop: + case Type_Dock: + case Type_Menu: + case Type_Splash: + // none of these windows are decorated by the window manager at all + decorations = 0; + break; + + case Type_Toolbar: + case Type_Utility: + decorations &= ~(Decor_Border); + break; + + case Type_Dialog: + decorations &= ~Decor_Handle; + break; + + case Type_Normal: + break; + } + } else { + decorations = 0; + } +} + /* * Creates a new top level window, with a given location, size, and border * width. @@ -405,8 +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; + 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(), @@ -419,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; @@ -448,8 +524,10 @@ void BlackboxWindow::associateClientWindow(void) { XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask); - XGrabServer(blackbox->getXDisplay()); - + /* + note we used to grab around this call to XReparentWindow however the + server is now grabbed before this method is called + */ unsigned long event_mask = PropertyChangeMask | FocusChangeMask | StructureNotifyMask; XSelectInput(blackbox->getXDisplay(), client.window, @@ -457,12 +535,9 @@ void BlackboxWindow::associateClientWindow(void) { XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0); XSelectInput(blackbox->getXDisplay(), client.window, event_mask); - XUngrabServer(blackbox->getXDisplay()); - XRaiseWindow(blackbox->getXDisplay(), frame.plate); XMapSubwindows(blackbox->getXDisplay(), frame.plate); - #ifdef SHAPE if (blackbox->hasShapeExtensions()) { XShapeSelectInput(blackbox->getXDisplay(), client.window, @@ -522,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) { @@ -583,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); } @@ -623,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); @@ -644,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); @@ -676,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); } } @@ -691,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); } } @@ -706,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); } } @@ -718,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) { @@ -741,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; @@ -752,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 @@ -785,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, @@ -824,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, @@ -831,30 +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) { - if ((! screen->isSloppyFocus()) || screen->doClickRaise()) - 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, 0, frame.plate); + blackbox->ungrabButton(Button1, mod_mask, frame.window); + blackbox->ungrabButton(Button2, mod_mask, frame.window); + blackbox->ungrabButton(Button3, mod_mask, frame.window); } @@ -941,37 +1065,51 @@ void BlackboxWindow::updateStrut(void) { } -void BlackboxWindow::getWindowType(void) { - unsigned long val; +bool BlackboxWindow::getWindowType(void) { + 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; - } - /* - * 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; + return False; + } - window_type = Type_Normal; + return True; } @@ -992,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 } @@ -1050,7 +1196,6 @@ void BlackboxWindow::getWMProtocols(void) { */ void BlackboxWindow::getWMHints(void) { focus_mode = F_Passive; - client.initial_state = NormalState; // remove from current window group if (client.window_group) { @@ -1077,7 +1222,7 @@ void BlackboxWindow::getWMHints(void) { } if (wmhint->flags & StateHint) - client.initial_state = wmhint->initial_state; + current_state = wmhint->initial_state; if (wmhint->flags & WindowGroupHint) { client.window_group = wmhint->window_group; @@ -1092,7 +1237,6 @@ void BlackboxWindow::getWMHints(void) { group->addWindow(this); } - client.wm_hint_flags = wmhint->flags; XFree(wmhint); } @@ -1114,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)) @@ -1245,21 +1386,21 @@ void BlackboxWindow::getMWMHints(void) { if (mwm_hint->flags & MwmHintsDecorations) { if (mwm_hint->decorations & MwmDecorAll) { - decorations = Decor_Titlebar | Decor_Handle | Decor_Border | - Decor_Iconify | Decor_Maximize | Decor_Close; + mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border | + Decor_Iconify | Decor_Maximize; } else { - decorations = 0; + mwm_decorations = 0; if (mwm_hint->decorations & MwmDecorBorder) - decorations |= Decor_Border; + mwm_decorations |= Decor_Border; if (mwm_hint->decorations & MwmDecorHandle) - decorations |= Decor_Handle; + mwm_decorations |= Decor_Handle; if (mwm_hint->decorations & MwmDecorTitle) - decorations |= Decor_Titlebar; + mwm_decorations |= Decor_Titlebar; if (mwm_hint->decorations & MwmDecorIconify) - decorations |= Decor_Iconify; + mwm_decorations |= Decor_Iconify; if (mwm_hint->decorations & MwmDecorMaximize) - decorations |= Decor_Maximize; + mwm_decorations |= Decor_Maximize; } } @@ -1332,39 +1473,16 @@ bool BlackboxWindow::getBlackboxHints(void) { if (blackbox_hint->flags & AttribDecoration) { switch (blackbox_hint->decoration) { case DecorNone: - // clear all decorations except close - decorations &= Decor_Close; - // clear all functions except close - functions &= Func_Close; - + blackbox_attrib.decoration = DecorNone; break; case DecorTiny: - decorations |= Decor_Titlebar | Decor_Iconify; - decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize); - functions |= Func_Move | Func_Iconify; - functions &= ~(Func_Resize | Func_Maximize); - - break; - case DecorTool: - decorations |= Decor_Titlebar; - decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle); - functions |= Func_Move; - functions &= ~(Func_Resize | Func_Maximize | Func_Iconify); - - break; - case DecorNormal: default: - decorations |= Decor_Titlebar | Decor_Border | Decor_Handle | - Decor_Iconify | Decor_Maximize; - functions |= Func_Resize | Func_Move | Func_Iconify | Func_Maximize; - + // blackbox_attrib.decoration defaults to DecorNormal break; } - - reconfigure(); } delete [] blackbox_hint; @@ -1376,13 +1494,12 @@ bool BlackboxWindow::getBlackboxHints(void) { void BlackboxWindow::getTransientInfo(void) { if (client.transient_for && client.transient_for != (BlackboxWindow *) ~0ul) { - // the transient for hint was removed, so we need to tell our - // previous transient_for that we are going away + // reset transient_for in preparation of looking for a new owner client.transient_for->client.transientList.remove(this); } // 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, @@ -1422,9 +1539,24 @@ void BlackboxWindow::getTransientInfo(void) { return; } - // register ourselves with our new transient_for - client.transient_for->client.transientList.push_back(this); - flags.stuck = client.transient_for->flags.stuck; + // Check for a circular transient state: this can lock up Blackbox + // when it tries to find the non-transient window for a transient. + BlackboxWindow *w = this; + while(w->client.transient_for && + w->client.transient_for != (BlackboxWindow *) ~0ul) { + if(w->client.transient_for == this) { + client.transient_for = (BlackboxWindow*) 0; + break; + } + w = w->client.transient_for; + } + + if (client.transient_for && + client.transient_for != (BlackboxWindow *) ~0ul) { + // register ourselves with our new transient_for + client.transient_for->client.transientList.push_back(this); + flags.stuck = client.transient_for->flags.stuck; + } } @@ -1539,24 +1671,23 @@ 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 bool BlackboxWindow::setInputFocus(void) { if (flags.focused) return True; - assert(! flags.iconic && - (flags.stuck || // window must be on the current workspace or sticky - blackbox_attrib.workspace == screen->getCurrentWorkspaceID())); -#if 0 - // if the window is not visible, mark the window as wanting focus rather - // than give it focus. - if (! flags.visible) { - Workspace *wkspc = screen->getWorkspace(blackbox_attrib.workspace); - wkspc->setLastFocusedWindow(this); - return True; - } -#endif + assert(flags.stuck || // window must be on the current workspace or sticky + blackbox_attrib.workspace == screen->getCurrentWorkspaceID()); + /* We only do this check for normal windows and dialogs because other windows do this on purpose, such as kde's kicker, and we don't want to go moving @@ -1573,17 +1704,14 @@ bool BlackboxWindow::setInputFocus(void) { if (client.transientList.size() > 0) { // transfer focus to any modal transients BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) { + for (it = client.transientList.begin(); it != end; ++it) if ((*it)->flags.modal) return (*it)->setInputFocus(); - } } bool ret = True; if (focus_mode == F_LocallyActive || focus_mode == F_Passive) { XSetInputFocus(blackbox->getXDisplay(), client.window, RevertToPointerRoot, CurrentTime); - - blackbox->setFocusedWindow(this); } else { /* we could set the focus to none, since the window doesn't accept focus, * but we shouldn't set focus to nothing since this would surely make @@ -1614,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. @@ -1646,6 +1774,11 @@ void BlackboxWindow::iconify(void) { setState(IconicState); screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this); + if (flags.stuck) { + for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) + if (i != blackbox_attrib.workspace) + screen->getWorkspace(i)->removeWindow(this, True); + } if (isTransient()) { if (client.transient_for != (BlackboxWindow *) ~0ul && @@ -1679,7 +1812,7 @@ void BlackboxWindow::show(void) { XMapSubwindows(blackbox->getXDisplay(), frame.window); XMapWindow(blackbox->getXDisplay(), frame.window); -#ifdef DEBUG +#if 0 int real_x, real_y; Window child; XTranslateCoordinates(blackbox->getXDisplay(), client.window, @@ -1703,9 +1836,8 @@ void BlackboxWindow::deiconify(bool reassoc, bool raise) { // reassociate and deiconify all transients if (reassoc && client.transientList.size() > 0) { BlackboxWindowList::iterator it, end = client.transientList.end(); - for (it = client.transientList.begin(); it != end; ++it) { + for (it = client.transientList.begin(); it != end; ++it) (*it)->deiconify(True, False); - } } if (raise) @@ -1714,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); @@ -1759,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) @@ -1857,6 +1993,27 @@ void BlackboxWindow::maximize(unsigned int button) { // re-maximizes the window to take into account availableArea changes void BlackboxWindow::remaximize(void) { + if (flags.shaded) { + // we only update the window's attributes otherwise we lose the shade bit + switch(flags.maximized) { + case 1: + blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert; + break; + + case 2: + blackbox_attrib.flags |= AttribMaxVert; + blackbox_attrib.attrib |= AttribMaxVert; + break; + + case 3: + blackbox_attrib.flags |= AttribMaxHoriz; + blackbox_attrib.attrib |= AttribMaxHoriz; + break; + } + return; + } + // save the original dimensions because maximize will wipe them out int premax_x = blackbox_attrib.premax_x, premax_y = blackbox_attrib.premax_y, @@ -1930,6 +2087,10 @@ void BlackboxWindow::stick(void) { blackbox_attrib.attrib ^= AttribOmnipresent; flags.stuck = False; + + for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) + if (i != blackbox_attrib.workspace) + screen->getWorkspace(i)->removeWindow(this, True); if (! flags.iconic) screen->reassociateWindow(this, BSENTINEL, True); @@ -1949,9 +2110,16 @@ void BlackboxWindow::stick(void) { // value than that contained in the class' data. xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, 0xffffffff); + + for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i) + if (i != blackbox_attrib.workspace) + screen->getWorkspace(i)->addWindow(this, False, True); setState(current_state); } + + redrawAllButtons(); + // go up the chain if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul && client.transient_for->isStuck() != flags.stuck) @@ -2046,23 +2214,24 @@ void BlackboxWindow::redrawWindowFrame(void) const { void BlackboxWindow::setFocusFlag(bool focus) { // only focus a window if it is visible - if (focus && !flags.visible) + if (focus && ! flags.visible) return; flags.focused = focus; redrawWindowFrame(); - if (screen->isSloppyFocus() && screen->doAutoRaise()) { - if (isFocused()) timer->start(); - else timer->stop(); - } - - if (isFocused()) + if (flags.focused) blackbox->setFocusedWindow(this); - - Clientmenu *menu = screen->getWorkspace(blackbox_attrib.workspace)->getMenu(); - menu->setItemSelected(window_number, isFocused()); + + if (! flags.iconic) { + // iconic windows arent in a workspace menu! + if (flags.stuck) + screen->getCurrentWorkspace()->setFocused(this, isFocused()); + else + screen->getWorkspace(blackbox_attrib.workspace)-> + setFocused(this, flags.focused); + } } @@ -2203,8 +2372,8 @@ void BlackboxWindow::restoreAttributes(void) { current_state = NormalState; } - if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) { - flags.stuck = False; + if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent && + ! flags.stuck) { stick(); // if the window was on another workspace, it was going to be hidden. this @@ -2234,6 +2403,24 @@ void BlackboxWindow::restoreAttributes(void) { blackbox_attrib.premax_h = h; } + if (net->flags & AttribDecoration) { + switch (net->decoration) { + case DecorNone: + enableDecor(False); + break; + + /* since tools only let you toggle this anyways, we'll just make that all + it supports for now. + */ + default: + case DecorNormal: + case DecorTiny: + case DecorTool: + enableDecor(True); + break; + } + } + // with the state set it will then be the map event's job to read the // window's state and behave accordingly @@ -2398,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); } @@ -2507,6 +2695,40 @@ void BlackboxWindow::redrawCloseButton(bool pressed) const { } +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); + + XFillRectangle(blackbox->getXDisplay(), frame.stick_button, pen.gc(), + frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 ); +} + void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) { if (re->window != client.window) return; @@ -2516,6 +2738,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(); @@ -2534,8 +2765,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) { @@ -2596,10 +2827,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 @@ -2611,14 +2842,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()) { - decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~Func_Maximize; setAllowedActions(); + setupDecor(); } reconfigure(); @@ -2654,17 +2889,15 @@ void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) { ungrabButtons(); if (client.max_width <= client.min_width && client.max_height <= client.min_height) { - decorations &= ~(Decor_Maximize | Decor_Handle); functions &= ~(Func_Resize | Func_Maximize); } else { - if (! isTransient()) { - decorations |= Decor_Maximize | Decor_Handle; + if (! isTransient()) functions |= Func_Maximize; - } functions |= Func_Resize; } grabButtons(); setAllowedActions(); + setupDecor(); } Rect old_rect = frame.rect; @@ -2699,7 +2932,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 @@ -2711,6 +2944,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); } @@ -2722,7 +2957,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) @@ -2730,16 +2965,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) - req.setHeight(cr->height + frame.margin.top + frame.margin.bottom); + if (cr->value_mask & CWHeight) + frame.changing.setHeight(cr->height + + frame.margin.top + frame.margin.bottom); - configure(req.x(), req.y(), req.width(), req.height()); + /* + 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(frame.changing.x(), frame.changing.y(), + frame.changing.width(), frame.changing.height()); } if (cr->value_mask & CWStackMode && !isDesktop()) { @@ -2767,7 +3031,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(); @@ -2775,6 +3039,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(); @@ -2798,7 +3064,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 || @@ -2846,7 +3113,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 @@ -2855,7 +3123,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(); } @@ -2883,6 +3152,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))) @@ -2893,7 +3169,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); } } @@ -2901,6 +3177,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)); /* @@ -2953,221 +3231,375 @@ void BlackboxWindow::doMove(int x_root, int y_root) { dx -= frame.border_w; dy -= frame.border_w; - if (screen->doWorkspaceWarping()) { - // workspace warping - bool warp = False; - unsigned int dest = screen->getCurrentWorkspaceID(); - if (x_root <= 0) { - warp = True; + doWindowSnapping(dx, dy); - if (dest > 0) dest--; - else dest = screen->getNumberOfWorkspaces() - 1; + if (screen->doOpaqueMove()) { + if (screen->doWorkspaceWarping()) + doWorkspaceWarping(x_root, y_root, dx); - } else if (x_root >= screen->getRect().right()) { - warp = True; + configure(dx, dy, frame.rect.width(), frame.rect.height()); + } else { + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); - if (dest < screen->getNumberOfWorkspaces() - 1) dest++; - else dest = 0; - } - if (warp) { - endMove(); - bool focus = flags.focused; // had focus while moving? - if (! flags.stuck) - screen->reassociateWindow(this, dest, False); - screen->changeWorkspaceID(dest); - if (focus) - setInputFocus(); + if (screen->doWorkspaceWarping()) + doWorkspaceWarping(x_root, y_root, dx); - /* - If the XWarpPointer is done after the configure, we can end up - grabbing another window, so made sure you do it first. - */ - int dest_x; - if (x_root <= 0) { - dest_x = screen->getRect().right() - 1; - XWarpPointer(blackbox->getXDisplay(), None, - screen->getRootWindow(), 0, 0, 0, 0, - dest_x, y_root); - - configure(dx + (screen->getRect().width() - 1), dy, - frame.rect.width(), frame.rect.height()); - } else { - dest_x = 0; - XWarpPointer(blackbox->getXDisplay(), None, - screen->getRootWindow(), 0, 0, 0, 0, - dest_x, y_root); + frame.changing.setPos(dx, dy); - configure(dx - (screen->getRect().width() - 1), dy, - frame.rect.width(), frame.rect.height()); - } + XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), + screen->getOpGC(), + frame.changing.x(), + frame.changing.y(), + frame.changing.width() - 1, + frame.changing.height() - 1); + } - beginMove(dest_x, y_root); - return; - } + screen->showPosition(dx, dy); +} + + +void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) { + // workspace warping + bool warp = False; + unsigned int dest = screen->getCurrentWorkspaceID(); + if (x_root <= 0) { + warp = True; + + if (dest > 0) dest--; + else dest = screen->getNumberOfWorkspaces() - 1; + + } else if (x_root >= screen->getRect().right()) { + warp = True; + + if (dest < screen->getNumberOfWorkspaces() - 1) dest++; + else dest = 0; } + if (! warp) + return; + + 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 (screen->doOpaqueMove()) + XGrabServer(blackbox->getXDisplay()); + + XUngrabPointer(blackbox->getXDisplay(), CurrentTime); + XWarpPointer(blackbox->getXDisplay(), None, + screen->getRootWindow(), 0, 0, 0, 0, + dest_x, y_root); + XGrabPointer(blackbox->getXDisplay(), frame.window, False, + PointerMotionMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, + None, blackbox->getMoveCursor(), CurrentTime); + + if (screen->doOpaqueMove()) + XUngrabServer(blackbox->getXDisplay()); + + if (focus) + setInputFocus(); + +} + + +void BlackboxWindow::doWindowSnapping(int &dx, int &dy) { + // how much resistance to edges to provide + const int resistance_size = screen->getResistanceSize(); + + // how far away to snap + const int snap_distance = screen->getSnapThreshold(); + + // how to snap windows + const int snap_to_windows = screen->getWindowToWindowSnap(); + const int snap_to_edges = screen->getWindowToEdgeSnap(); + // the amount of space away from the edge to provide resistance/snap + const int snap_offset = screen->getSnapOffset(); + + // find the geomeetery where the moving window currently is + const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing; + + // window corners + const int wleft = dx, + wright = dx + frame.rect.width() - 1, + wtop = dy, + wbottom = dy + frame.rect.height() - 1; + + if (snap_to_windows) { + RectList rectlist; + + Workspace *w = screen->getWorkspace(getWorkspaceNumber()); + assert(w); + + // add windows on the workspace to the rect list + const BlackboxWindowList& stack_list = w->getStackingList(); + BlackboxWindowList::const_iterator st_it, st_end = stack_list.end(); + for (st_it = stack_list.begin(); st_it != st_end; ++st_it) + if (*st_it != this) // don't snap to ourself + rectlist.push_back( (*st_it)->frameRect() ); + + // add the toolbar and the slit to the rect list. + // (only if they are not hidden) + Toolbar *tbar = screen->getToolbar(); + Slit *slit = screen->getSlit(); + Rect tbar_rect, slit_rect; + unsigned int bwidth = screen->getBorderWidth() * 2; + + if (! (screen->doHideToolbar() || tbar->isHidden())) { + tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth, + tbar->getHeight() + bwidth); + rectlist.push_back(tbar_rect); + } + + if (! slit->isHidden()) { + slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth, + slit->getHeight() + bwidth); + rectlist.push_back(slit_rect); + } - const int snap_distance = screen->getEdgeSnapThreshold(); + RectList::const_iterator it, end = rectlist.end(); + for (it = rectlist.begin(); it != end; ++it) { + bool snapped = False; + const Rect &winrect = *it; + Rect offsetrect; + offsetrect.setCoords(winrect.left() - snap_offset, + winrect.top() - snap_offset, + winrect.right() + snap_offset, + winrect.bottom() + snap_offset); - if (snap_distance) { - // window corners - const int wleft = dx, - wright = dx + frame.rect.width() - 1, - wtop = dy, - wbottom = dy + frame.rect.height() - 1; + if (snap_to_windows == BScreen::WindowResistance) + // if the window is already over top of this snap target, then + // resistance is futile, so just ignore it + if (winrect.intersects(moving)) + continue; - if (screen->getWindowToWindowSnap()) { - Workspace *w = screen->getWorkspace(getWorkspaceNumber()); - assert(w); + int dleft, dright, dtop, dbottom; - // try snap to another window - for (unsigned int i = 0, c = w->getCount(); i < c; ++i) { - BlackboxWindow *snapwin = w->getWindow(i); - if (snapwin == this) - continue; // don't snap to self + // if the windows are in the same plane vertically + if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) && + wtop < (signed)(winrect.y() + winrect.height() - 1)) { - bool snapped = False; - - const Rect &winrect = snapwin->frameRect(); - int dleft = std::abs(wright - winrect.left()), - dright = std::abs(wleft - winrect.right()), - dtop = std::abs(wbottom - winrect.top()), - dbottom = std::abs(wtop - winrect.bottom()); + if (snap_to_windows == BScreen::WindowResistance) { + dleft = wright - offsetrect.left(); + dright = offsetrect.right() - wleft; - if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) && - wtop < (signed)(winrect.y() + winrect.height() - 1)) { + // snap left of other window? + if (dleft >= 0 && dleft < resistance_size && + dleft < (wright - wleft)) { + dx = offsetrect.left() - frame.rect.width(); + snapped = True; + } + // snap right of other window? + else if (dright >= 0 && dright < resistance_size && + dright < (wright - wleft)) { + dx = offsetrect.right() + 1; + snapped = True; + } + } else { // BScreen::WindowSnap + dleft = abs(wright - offsetrect.left()); + dright = abs(wleft - offsetrect.right()); // snap left of other window? if (dleft < snap_distance && dleft <= dright) { - dx = winrect.left() - frame.rect.width(); + dx = offsetrect.left() - frame.rect.width(); snapped = True; } // snap right of other window? else if (dright < snap_distance) { - dx = winrect.right() + 1; + dx = offsetrect.right() + 1; snapped = True; - } + } + } - if (snapped) { - if (screen->getWindowCornerSnap()) { - // try corner-snap to its other sides - dtop = std::abs(wtop - winrect.top()); - dbottom = std::abs(wbottom - winrect.bottom()); + if (snapped) { + if (screen->getWindowCornerSnap()) { + // try corner-snap to its other sides + if (snap_to_windows == BScreen::WindowResistance) { + dtop = winrect.top() - wtop; + dbottom = wbottom - winrect.bottom(); + if (dtop > 0 && dtop < resistance_size) { + // if we're already past the top edge, then don't provide + // resistance + if (moving.top() >= winrect.top()) + dy = winrect.top(); + } else if (dbottom > 0 && dbottom < resistance_size) { + // if we're already past the bottom edge, then don't provide + // resistance + if (moving.bottom() <= winrect.bottom()) + dy = winrect.bottom() - frame.rect.height() + 1; + } + } else { // BScreen::WindowSnap + dtop = abs(wtop - winrect.top()); + dbottom = abs(wbottom - winrect.bottom()); if (dtop < snap_distance && dtop <= dbottom) dy = winrect.top(); else if (dbottom < snap_distance) dy = winrect.bottom() - frame.rect.height() + 1; } - - continue; } + + continue; } + } - if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) && - wleft < (signed)(winrect.x() + winrect.width() - 1)) { + // if the windows are on the same plane horizontally + if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) && + wleft < (signed)(winrect.x() + winrect.width() - 1)) { + + if (snap_to_windows == BScreen::WindowResistance) { + dtop = wbottom - offsetrect.top(); + dbottom = offsetrect.bottom() - wtop; + + // snap top of other window? + if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) { + dy = offsetrect.top() - frame.rect.height(); + snapped = True; + } + // snap bottom of other window? + else if (dbottom >= 0 && dbottom < resistance_size && + dbottom < (wbottom - wtop)) { + dy = offsetrect.bottom() + 1; + snapped = True; + } + } else { // BScreen::WindowSnap + dtop = abs(wbottom - offsetrect.top()); + dbottom = abs(wtop - offsetrect.bottom()); // snap top of other window? if (dtop < snap_distance && dtop <= dbottom) { - dy = winrect.top() - frame.rect.height(); + dy = offsetrect.top() - frame.rect.height(); snapped = True; } // snap bottom of other window? else if (dbottom < snap_distance) { - dy = winrect.bottom() + 1; + dy = offsetrect.bottom() + 1; snapped = True; } - if (snapped) { - if (screen->getWindowCornerSnap()) { - // try corner-snap to its other sides - dleft = std::abs(wleft - winrect.left()); - dright = std::abs(wright - winrect.right()); + } + + if (snapped) { + if (screen->getWindowCornerSnap()) { + // try corner-snap to its other sides + if (snap_to_windows == BScreen::WindowResistance) { + dleft = winrect.left() - wleft; + dright = wright - winrect.right(); + if (dleft > 0 && dleft < resistance_size) { + // if we're already past the left edge, then don't provide + // resistance + if (moving.left() >= winrect.left()) + dx = winrect.left(); + } else if (dright > 0 && dright < resistance_size) { + // if we're already past the right edge, then don't provide + // resistance + if (moving.right() <= winrect.right()) + dx = winrect.right() - frame.rect.width() + 1; + } + } else { // BScreen::WindowSnap + dleft = abs(wleft - winrect.left()); + dright = abs(wright - winrect.right()); if (dleft < snap_distance && dleft <= dright) dx = winrect.left(); else if (dright < snap_distance) dx = winrect.right() - frame.rect.width() + 1; } - - continue; } + + continue; } } } + } - RectList snaplist; // the list of rects we will try to snap to + if (snap_to_edges) { + RectList rectlist; - // snap to the strut (and screen boundaries for xinerama) + // snap to the screen edges (and screen boundaries for xinerama) #ifdef XINERAMA if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) { - if (! screen->doFullMax()) - snaplist.insert(snaplist.begin(), - screen->allAvailableAreas().begin(), - screen->allAvailableAreas().end()); - - // always snap to the screen edges - snaplist.insert(snaplist.begin(), + rectlist.insert(rectlist.begin(), screen->getXineramaAreas().begin(), screen->getXineramaAreas().end()); } else #endif // XINERAMA - { - if (! screen->doFullMax()) - snaplist.push_back(screen->availableArea()); - - // always snap to the screen edges - snaplist.push_back(screen->getRect()); - } + rectlist.push_back(screen->getRect()); - RectList::const_iterator it, end = snaplist.end(); - for (it = snaplist.begin(); it != end; ++it) { + RectList::const_iterator it, end = rectlist.end(); + for (it = rectlist.begin(); it != end; ++it) { const Rect &srect = *it; + Rect offsetrect; + offsetrect.setCoords(srect.left() + snap_offset, + srect.top() + snap_offset, + srect.right() - snap_offset, + srect.bottom() - snap_offset); + + if (snap_to_edges == BScreen::WindowResistance) { + // if we're not in the rectangle then don't snap to it. + if (! srect.contains(moving)) + continue; + } else { // BScreen::WindowSnap + // if we're not in the rectangle then don't snap to it. + if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(), + frame.rect.height()))) + continue; + } + + if (snap_to_edges == BScreen::WindowResistance) { + int dleft = offsetrect.left() - wleft, + dright = wright - offsetrect.right(), + dtop = offsetrect.top() - wtop, + dbottom = wbottom - offsetrect.bottom(); - // if we're not in the rectangle then don't snap to it. - if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(), - frame.rect.height()))) - continue; + // snap left? + if (dleft > 0 && dleft < resistance_size) + dx = offsetrect.left(); + // snap right? + else if (dright > 0 && dright < resistance_size) + dx = offsetrect.right() - frame.rect.width() + 1; - int dleft = std::abs(wleft - srect.left()), - dright = std::abs(wright - srect.right()), - dtop = std::abs(wtop - srect.top()), - dbottom = std::abs(wbottom - srect.bottom()); + // snap top? + if (dtop > 0 && dtop < resistance_size) + dy = offsetrect.top(); + // snap bottom? + else if (dbottom > 0 && dbottom < resistance_size) + dy = offsetrect.bottom() - frame.rect.height() + 1; + } else { // BScreen::WindowSnap + int dleft = abs(wleft - offsetrect.left()), + dright = abs(wright - offsetrect.right()), + dtop = abs(wtop - offsetrect.top()), + dbottom = abs(wbottom - offsetrect.bottom()); // snap left? if (dleft < snap_distance && dleft <= dright) - dx = srect.left(); + dx = offsetrect.left(); // snap right? else if (dright < snap_distance) - dx = srect.right() - frame.rect.width() + 1; + dx = offsetrect.right() - frame.rect.width() + 1; // snap top? if (dtop < snap_distance && dtop <= dbottom) - dy = srect.top(); + dy = offsetrect.top(); // snap bottom? else if (dbottom < snap_distance) - dy = srect.bottom() - frame.rect.height() + 1; + dy = offsetrect.bottom() - frame.rect.height() + 1; + } } } - - if (screen->doOpaqueMove()) { - configure(dx, dy, frame.rect.width(), frame.rect.height()); - } else { - XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), - screen->getOpGC(), - frame.changing.x(), - frame.changing.y(), - frame.changing.width() - 1, - frame.changing.height() - 1); - - frame.changing.setPos(dx, dy); - - XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(), - screen->getOpGC(), - frame.changing.x(), - frame.changing.y(), - frame.changing.width() - 1, - frame.changing.height() - 1); - } - - screen->showPosition(dx, dy); } @@ -3208,11 +3640,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) { @@ -3261,7 +3696,7 @@ void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) { flags.resizing = True; blackbox->setChangingWindow(this); - int gw, gh; + unsigned int gw, gh; frame.changing = frame.rect; constrain(anchor, &gw, &gh); @@ -3285,36 +3720,44 @@ void BlackboxWindow::doResize(int x_root, int y_root) { screen->getOpGC(), frame.changing.x(), frame.changing.y(), frame.changing.width() - 1, frame.changing.height() - 1); - int gw, gh; + 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(), @@ -3356,7 +3799,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 @@ -3366,15 +3809,17 @@ 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))) { unsigned int zones = screen->getResizeZones(); Corner corner; @@ -3401,10 +3846,60 @@ void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) { } +void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) { + if (! (screen->isSloppyFocus() && isVisible() && isNormal())) + return; + + XEvent e; + bool leave = False, inferior = False; + + while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window, + LeaveNotify, &e)) { + if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) { + leave = True; + inferior = (e.xcrossing.detail == NotifyInferior); + } + } + + if (! leave || inferior) { + if (! isFocused()) { + bool success = setInputFocus(); + if (success) // if focus succeeded install the colormap + installColormap(True); // XXX: shouldnt we honour no install? + + /* + 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(); + } + } +} + + +void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) { + if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal())) + return; + + installColormap(False); + + if (timer->isTiming()) + timer->stop(); +} + + #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 @@ -3433,7 +3928,14 @@ void BlackboxWindow::restore(bool remap) { XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask); // do not leave a shaded window as an icon unless it was an icon - if (flags.shaded && ! flags.iconic) setState(NormalState); + 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); @@ -3464,7 +3966,7 @@ void BlackboxWindow::timeout(void) { } -void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) { +void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) { if ((net->flags & AttribShaded) && ((blackbox_attrib.attrib & AttribShaded) != (net->attrib & AttribShaded))) @@ -3510,59 +4012,16 @@ void BlackboxWindow::changeBlackboxHints(BlackboxHints *net) { if (net->flags & AttribDecoration) { switch (net->decoration) { case DecorNone: - // clear all decorations except close - decorations &= Decor_Close; - + enableDecor(False); break; default: case DecorNormal: - decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify; - - decorations = ((functions & Func_Resize) && !isTransient() ? - decorations | Decor_Handle : - decorations &= ~Decor_Handle); - decorations = (functions & Func_Maximize ? - decorations | Decor_Maximize : - decorations &= ~Decor_Maximize); - - break; - case DecorTiny: - decorations |= Decor_Titlebar | Decor_Iconify; - decorations &= ~(Decor_Border | Decor_Handle); - - decorations = (functions & Func_Maximize ? - decorations | Decor_Maximize : - decorations &= ~Decor_Maximize); - - break; - case DecorTool: - decorations |= Decor_Titlebar; - decorations &= ~(Decor_Iconify | Decor_Border); - - decorations = ((functions & Func_Resize) && !isTransient() ? - decorations | Decor_Handle : - decorations &= ~Decor_Handle); - decorations = (functions & Func_Maximize ? - decorations | Decor_Maximize : - decorations &= ~Decor_Maximize); - + 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); } } @@ -3652,11 +4111,14 @@ void BlackboxWindow::upsize(void) { * The logical width and height are placed into pw and ph, if they * are non-zero. Logical size refers to the users perception of * the window size (for example an xterm resizes in cells, not in pixels). + * pw and ph are then used to display the geometry during window moves, resize, + * etc. * * The physical geometry is placed into frame.changing_{x,y,width,height}. * Physical geometry refers to the geometry of the window in pixels. */ -void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) { +void BlackboxWindow::constrain(Corner anchor, + unsigned int *pw, unsigned int *ph) { // frame.changing represents the requested frame size, we need to // strip the frame margin off and constrain the client size frame.changing.setCoords(frame.changing.left() + frame.margin.left, @@ -3664,39 +4126,48 @@ void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) { frame.changing.right() - frame.margin.right, frame.changing.bottom() - frame.margin.bottom); - int dw = frame.changing.width(), dh = frame.changing.height(), + unsigned int dw = frame.changing.width(), dh = frame.changing.height(), base_width = (client.base_width) ? client.base_width : client.min_width, base_height = (client.base_height) ? client.base_height : client.min_height; - // constrain - if (dw < static_cast(client.min_width)) dw = client.min_width; - if (dh < static_cast(client.min_height)) dh = client.min_height; - if (dw > static_cast(client.max_width)) dw = client.max_width; - if (dh > static_cast(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; + } - dw -= base_width; - dw /= client.width_inc; - dh -= base_height; - dh /= client.height_inc; + assert(dw >= base_width && dh >= base_height); - if (pw) { - if (client.width_inc == 1) - *pw = dw + base_width; - else - *pw = dw; + if (client.width_inc > 1) { + dw -= base_width; + dw /= client.width_inc; } - if (ph) { - if (client.height_inc == 1) - *ph = dh + base_height; - else - *ph = dh; + if (client.height_inc > 1) { + dh -= base_height; + dh /= client.height_inc; } - dw *= client.width_inc; - dw += base_width; - dh *= client.height_inc; - dh += base_height; + if (pw) + *pw = dw; + + if (ph) + *ph = dh; + + if (client.width_inc > 1) { + dw *= client.width_inc; + dw += base_width; + } + if (client.height_inc > 1) { + dh *= client.height_inc; + dh += base_height; + } frame.changing.setSize(dw, dh); @@ -3785,13 +4256,10 @@ BWindowGroup::find(BScreen *screen, bool allow_transients) const { BlackboxWindow *ret = blackbox->getFocusedWindow(); // does the focus window match (or any transient_fors)? - while (ret) { - if (ret->getScreen() == screen && ret->getGroupWindow() == group) { - if (ret->isTransient() && allow_transients) break; - else if (! ret->isTransient()) break; - } - - ret = ret->getTransientFor(); + for (; ret; ret = ret->getTransientFor()) { + if (ret->getScreen() == screen && ret->getGroupWindow() == group && + (! ret->isTransient() || allow_transients)) + break; } if (ret) return ret; @@ -3800,10 +4268,9 @@ BWindowGroup::find(BScreen *screen, bool allow_transients) const { BlackboxWindowList::const_iterator it, end = windowList.end(); for (it = windowList.begin(); it != end; ++it) { ret = *it; - if (ret->getScreen() == screen && ret->getGroupWindow() == group) { - if (ret->isTransient() && allow_transients) break; - else if (! ret->isTransient()) break; - } + if (ret->getScreen() == screen && ret->getGroupWindow() == group && + (! ret->isTransient() || allow_transients)) + break; } return ret;