]> Dogcows Code - chaz/openbox/blobdiff - src/Window.cc
sync with bb-cvs
[chaz/openbox] / src / Window.cc
index 9e3d9f5eabd8a78dd4e183744c186e1794f16903..1b86b285241795e16793f6649f598956abdeea27 100644 (file)
@@ -38,12 +38,16 @@ extern "C" {
 #    include <stdio.h>
 #  endif // HAVE_STDIO_H
 #endif // DEBUG
-}
 
-#include <cstdlib>
+#ifdef HAVE_STDLIB_H
+   #include <stdlib.h>
+#endif // HAVE_STDLIB_H
+}
 
 #include "i18n.hh"
 #include "blackbox.hh"
+#include "Clientmenu.hh"
+#include "Font.hh"
 #include "GCCache.hh"
 #include "Iconmenu.hh"
 #include "Image.hh"
@@ -56,6 +60,7 @@ extern "C" {
 #include "Slit.hh"
 
 using std::string;
+using std::abs;
 
 /*
  * Initializes the class with default values/the window's set initial values.
@@ -84,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");
@@ -108,15 +103,26 @@ 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 = False;
+    flags.send_focus_message = flags.shaped = flags.skip_taskbar =
+    flags.skip_pager = flags.fullscreen = False;
   flags.maximized = 0;
 
   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;
 
@@ -132,118 +138,133 @@ BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
     frame.fgrip_pixel = 0;
   frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
   frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
-  frame.pbutton = frame.ugrip = frame.fgrip = decorations;
+  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();
-
   // 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);
   blackbox->saveWindowSearch(frame.plate, this);
-  blackbox->saveWindowSearch(client.window, this);
 
   // determine if this is a transient window
   getTransientInfo();
 
-  // adjust the window decorations based on transience and window sizes
-  if (isTransient()) {
-    decorations &= ~(Decor_Maximize | Decor_Handle);
-    functions &= ~Func_Maximize;
-  }
+  // determine the window's type, so we can decide its decorations and
+  // functionality, or if we should not manage it at all
+  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_Toolbar:
+    case Type_Utility:
+      // these windows get less functionality
+      functions &= ~(Func_Maximize | Func_Resize | Func_Iconify);
+      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();
-
-  bool place_window = True;
-  if (blackbox->isStartup() || isTransient() ||
-      client.normal_hint_flags & (PPosition|USPosition)) {
-    setGravityOffsets();
-
-
-    if (blackbox->isStartup() ||
-        client.rect.intersects(screen->availableArea()))
-      place_window = False;
-  }
+  
+  setAllowedActions();
 
+  setupDecor();
+  
   if (decorations & Decor_Titlebar)
     createTitlebar();
 
   if (decorations & Decor_Handle)
     createHandle();
 
-#ifdef    SHAPE
-  if (blackbox->hasShapeExtensions() && flags.shaped) {
-    configureShape();
-  }
-#endif // SHAPE
+  // apply the size and gravity hint to the frame
 
-  if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
-    // grab button 1 for changing focus/raising
-    blackbox->grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
-                         GrabModeSync, GrabModeSync, frame.plate, None);
+  upsize();
+
+  bool place_window = True;
+  if (blackbox->isStartup() || isTransient() ||
+      client.normal_hint_flags & (PPosition|USPosition)) {
+    applyGravity(frame.rect);
+
+    if (blackbox->isStartup() || client.rect.intersects(screen->getRect()))
+      place_window = False;
   }
 
-  blackbox->grabButton(Button1, Mod1Mask, frame.window, True,
-                       ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
-                       GrabModeAsync, frame.window, blackbox->getMoveCursor());
-  blackbox->grabButton(Button2, Mod1Mask, frame.window, True,
-                       ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
-                       frame.window, None);
-  blackbox->grabButton(Button3, Mod1Mask, frame.window, True,
-                       ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
-                       GrabModeAsync, frame.window,
-                       blackbox->getLowerRightAngleCursor());
+  // add the window's strut. note this is done *after* placing the window.
+  screen->addStrut(&client.strut);
+  updateStrut();
+  
+  /*
+    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.
+  */
 
-  positionWindows();
-  decorate();
+  XGrabServer(blackbox->getXDisplay());
 
-  if (decorations & Decor_Titlebar)
-    XMapSubwindows(blackbox->getXDisplay(), frame.title);
-  XMapSubwindows(blackbox->getXDisplay(), frame.window);
+  associateClientWindow();
 
-  windowmenu = new Windowmenu(this);
+  blackbox->saveWindowSearch(client.window, this);
 
   if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
     screen->getCurrentWorkspace()->addWindow(this, place_window);
@@ -256,29 +277,51 @@ 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)
+    flags.stuck = True;
 
   if (flags.shaded) {
     flags.shaded = False;
+    initial_state = current_state;
     shade();
-    
+
     /*
-      Because the iconic'ness of shaded windows is lost, we need to set the
-      state to NormalState so that shaded windows on other workspaces will not
-      get shown on the first workspace.
       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.
     */
-    current_state = NormalState;
+    if (initial_state != IconicState)
+      current_state = NormalState;
   }
 
   if (flags.stuck) {
@@ -286,21 +329,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.
-  */
-
-  setFocusFlag(False);
+  // create this last so it only needs to be configured once
+  windowmenu = new Windowmenu(this);
 }
 
 
@@ -313,10 +346,16 @@ BlackboxWindow::~BlackboxWindow(void) {
   if (! timer) // window not managed...
     return;
 
-  if (flags.moving || flags.resizing) {
-    screen->hideGeometry();
-    XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
-  }
+  if (flags.moving)
+    endMove();
+
+  screen->removeStrut(&client.strut);
+  screen->updateAvailableArea();
+
+  // 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)
+    endMove();
 
   delete timer;
 
@@ -329,18 +368,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)
@@ -363,6 +400,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.
@@ -377,10 +474,11 @@ Window BlackboxWindow::createToplevelWindow(void) {
   attrib_create.colormap = screen->getColormap();
   attrib_create.override_redirect = True;
   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
-                             ButtonMotionMask | EnterWindowMask;
+                             ButtonMotionMask |
+                             EnterWindowMask | LeaveWindowMask;
 
   return XCreateWindow(blackbox->getXDisplay(), screen->getRootWindow(),
-                       -1, -1, 1, 1, frame.border_w, screen->getDepth(),
+                       0, 0, 1, 1, frame.border_w, screen->getDepth(),
                        InputOutput, screen->getVisual(), create_mask,
                        &attrib_create);
 }
@@ -419,17 +517,20 @@ void BlackboxWindow::associateClientWindow(void) {
 
   XSelectInput(blackbox->getXDisplay(), frame.plate, SubstructureRedirectMask);
 
-  XGrabServer(blackbox->getXDisplay());
-  XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
-  XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
+  /*
+    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,
-               PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
-  XUngrabServer(blackbox->getXDisplay());
+               event_mask & ~StructureNotifyMask);
+  XReparentWindow(blackbox->getXDisplay(), client.window, frame.plate, 0, 0);
+  XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
 
   XRaiseWindow(blackbox->getXDisplay(), frame.plate);
   XMapSubwindows(blackbox->getXDisplay(), frame.plate);
 
-
 #ifdef    SHAPE
   if (blackbox->hasShapeExtensions()) {
     XShapeSelectInput(blackbox->getXDisplay(), client.window,
@@ -489,13 +590,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) {
@@ -741,19 +837,19 @@ void BlackboxWindow::positionButtons(bool redecorate_label) {
   for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
     switch(*it) {
     case 'C':
-      if (!frame.close_button) createCloseButton();
+      if (! frame.close_button) createCloseButton();
       XMoveResizeWindow(blackbox->getXDisplay(), frame.close_button, x, by,
                         frame.button_w, frame.button_w);
       x += frame.button_w + bsep;
       break;
     case 'I':
-      if (!frame.iconify_button) createIconifyButton();
+      if (! frame.iconify_button) createIconifyButton();
       XMoveResizeWindow(blackbox->getXDisplay(), frame.iconify_button, x, by,
                         frame.button_w, frame.button_w);
       x += frame.button_w + bsep;
       break;
     case 'M':
-      if (!frame.maximize_button) createMaximizeButton();
+      if (! frame.maximize_button) createMaximizeButton();
       XMoveResizeWindow(blackbox->getXDisplay(), frame.maximize_button, x, by,
                         frame.button_w, frame.button_w);
       x += frame.button_w + bsep;
@@ -773,19 +869,15 @@ void BlackboxWindow::positionButtons(bool redecorate_label) {
 
 
 void BlackboxWindow::reconfigure(void) {
+  restoreGravity(client.rect);
   upsize();
-
-  client.rect.setPos(frame.rect.left() + frame.margin.left,
-                     frame.rect.top() + frame.margin.top);
-
+  applyGravity(frame.rect);
   positionWindows();
   decorate();
+  redrawWindowFrame();
 
-  XClearWindow(blackbox->getXDisplay(), frame.window);
-  setFocusFlag(flags.focused);
-
-  configure(frame.rect.x(), frame.rect.y(),
-            frame.rect.width(), frame.rect.height());
+  ungrabButtons();
+  grabButtons();
 
   if (windowmenu) {
     windowmenu->move(windowmenu->getX(), frame.rect.y() + frame.title_h);
@@ -794,14 +886,38 @@ void BlackboxWindow::reconfigure(void) {
 }
 
 
-void BlackboxWindow::updateFocusModel(void) {
-  if ((! screen->isSloppyFocus()) || screen->doClickRaise()) {
+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,
-                         GrabModeSync, GrabModeSync, None, None);
-  } else {
-    blackbox->ungrabButton(Button1, 0, frame.plate);
-  }
+                         GrabModeSync, GrabModeSync, frame.plate, None,
+                         screen->allowScrollLock());
+  
+  if (functions & Func_Move)
+    blackbox->grabButton(Button1, mod_mask, frame.window, True,
+                         ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
+                         GrabModeAsync, frame.window, None,
+                         screen->allowScrollLock());
+  if (functions & Func_Resize)
+    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, mod_mask, frame.window, True,
+                       ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
+                       frame.window, None,
+                       screen->allowScrollLock());
+}
+
+
+void BlackboxWindow::ungrabButtons(void) {
+  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);
 }
 
 
@@ -809,7 +925,8 @@ void BlackboxWindow::positionWindows(void) {
   XMoveResizeWindow(blackbox->getXDisplay(), frame.window,
                     frame.rect.x(), frame.rect.y(), frame.inside_w,
                     (flags.shaded) ? frame.title_h : frame.inside_h);
-  XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window, frame.border_w);
+  XSetWindowBorderWidth(blackbox->getXDisplay(), frame.window,
+                        frame.border_w);
   XSetWindowBorderWidth(blackbox->getXDisplay(), frame.plate,
                         frame.mwm_border_w);
   XMoveResizeWindow(blackbox->getXDisplay(), frame.plate,
@@ -818,6 +935,9 @@ void BlackboxWindow::positionWindows(void) {
                     client.rect.width(), client.rect.height());
   XMoveResizeWindow(blackbox->getXDisplay(), client.window,
                     0, 0, client.rect.width(), client.rect.height());
+  // ensure client.rect contains the real location
+  client.rect.setPos(frame.rect.left() + frame.margin.left,
+                     frame.rect.top() + frame.margin.top);
 
   if (decorations & Decor_Titlebar) {
     if (frame.title == None) createTitlebar();
@@ -860,35 +980,116 @@ void BlackboxWindow::positionWindows(void) {
   } else if (frame.handle) {
     destroyHandle();
   }
+  XSync(blackbox->getXDisplay(), False);
 }
 
 
-void BlackboxWindow::getWMName(void) {
-  XTextProperty text_prop;
+void BlackboxWindow::updateStrut(void) {
+  unsigned long num = 4;
+  unsigned long *data;
+  if (! xatom->getValue(client.window, XAtom::net_wm_strut, XAtom::cardinal,
+                        num, &data))
+    return;
+  if (num == 4) {
+    client.strut.left = data[0];
+    client.strut.right = data[1];
+    client.strut.top = data[2];
+    client.strut.bottom = data[3];
+
+    screen->updateAvailableArea();
+  }
+
+  delete [] data;
+}
+
+
+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,
+                        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
+      window_type = Type_Normal;
 
-  if (XGetWMName(blackbox->getXDisplay(), client.window, &text_prop)) {
-    client.title = textPropertyToString(blackbox->getXDisplay(), text_prop);
-    if (client.title.empty())
-      client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
-    XFree((char *) text_prop.value);
-  } else {
-    client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
+    return False;
+  }
+
+  return True;
+}
+
+
+void BlackboxWindow::getWMName(void) {
+  if (xatom->getValue(client.window, XAtom::net_wm_name,
+                      XAtom::utf8, client.title) &&
+      !client.title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
+    return;
+  }
+  //fall through to using WM_NAME
+  if (xatom->getValue(client.window, XAtom::wm_name, XAtom::ansi, client.title)
+      && !client.title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_name);
+    return;
   }
+  // fall back to an internal default
+  client.title = i18n(WindowSet, WindowUnnamed, "Unnamed");
+  xatom->setValue(client.window, XAtom::net_wm_visible_name, XAtom::utf8,
+                  client.title);
 }
 
 
 void BlackboxWindow::getWMIconName(void) {
-  XTextProperty text_prop;
-
-  if (XGetWMIconName(blackbox->getXDisplay(), client.window, &text_prop)) {
-    client.icon_title =
-      textPropertyToString(blackbox->getXDisplay(), text_prop);
-    if (client.icon_title.empty())
-      client.icon_title = client.title;
-    XFree((char *) text_prop.value);
-  } else {
-    client.icon_title = client.title;
+  if (xatom->getValue(client.window, XAtom::net_wm_icon_name,
+                      XAtom::utf8, client.icon_title) && 
+      !client.icon_title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
+    return;
+  }
+  //fall through to using WM_ICON_NAME
+  if (xatom->getValue(client.window, XAtom::wm_icon_name, XAtom::ansi,
+                      client.icon_title) && 
+      !client.icon_title.empty()) {
+    xatom->eraseValue(client.window, XAtom::net_wm_visible_icon_name);
+    return;
   }
+  // fall back to using the main name
+  client.icon_title = client.title;
+  xatom->setValue(client.window, XAtom::net_wm_visible_icon_name, XAtom::utf8,
+                  client.icon_title);
 }
 
 
@@ -906,12 +1107,12 @@ void BlackboxWindow::getWMProtocols(void) {
   if (XGetWMProtocols(blackbox->getXDisplay(), client.window,
                       &proto, &num_return)) {
     for (int i = 0; i < num_return; ++i) {
-      if (proto[i] == blackbox->getWMDeleteAtom()) {
+      if (proto[i] == xatom->getAtom(XAtom::wm_delete_window)) {
         decorations |= Decor_Close;
         functions |= Func_Close;
-      } else if (proto[i] == blackbox->getWMTakeFocusAtom())
+      } else if (proto[i] == xatom->getAtom(XAtom::wm_take_focus))
         flags.send_focus_message = True;
-      else if (proto[i] == blackbox->getBlackboxStructureMessagesAtom())
+      else if (proto[i] == xatom->getAtom(XAtom::blackbox_structure_messages))
         screen->addNetizen(new Netizen(screen, client.window));
     }
 
@@ -926,7 +1127,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) {
@@ -953,19 +1153,21 @@ 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;
 
     // add window to the appropriate group
     BWindowGroup *group = blackbox->searchGroup(client.window_group);
-    if (! group) // no group found, create it!
-      group = new BWindowGroup(blackbox, client.window_group);
-    group->addWindow(this);
+    if (! group) { // no group found, create it!
+      new BWindowGroup(blackbox, client.window_group);
+      group = blackbox->searchGroup(client.window_group);
+    }
+    if (group)
+      group->addWindow(this);
   }
 
-  client.wm_hint_flags = wmhint->flags;
   XFree(wmhint);
 }
 
@@ -981,6 +1183,11 @@ void BlackboxWindow::getWMNormalHints(void) {
   client.min_width = client.min_height =
     client.width_inc = client.height_inc = 1;
   client.base_width = client.base_height = 0;
+  client.win_gravity = NorthWestGravity;
+#if 0
+  client.min_aspect_x = client.min_aspect_y =
+    client.max_aspect_x = client.max_aspect_y = 1;
+#endif
 
   /*
     use the full screen, not the strut modified size. otherwise when the
@@ -989,11 +1196,7 @@ void BlackboxWindow::getWMNormalHints(void) {
   */
   const Rect& screen_area = screen->getRect();
   client.max_width = screen_area.width();
-
   client.max_height = screen_area.height();
-  client.min_aspect_x = client.min_aspect_y =
-    client.max_aspect_x = client.max_aspect_y = 1;
-  client.win_gravity = NorthWestGravity;
 
   if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
                           &sizehint, &icccm_mask))
@@ -1002,13 +1205,22 @@ void BlackboxWindow::getWMNormalHints(void) {
   client.normal_hint_flags = sizehint.flags;
 
   if (sizehint.flags & PMinSize) {
-    client.min_width = sizehint.min_width;
-    client.min_height = sizehint.min_height;
+    if (sizehint.min_width >= 0)
+      client.min_width = sizehint.min_width;
+    if (sizehint.min_height >= 0)
+      client.min_height = sizehint.min_height;
   }
 
   if (sizehint.flags & PMaxSize) {
-    client.max_width = sizehint.max_width;
-    client.max_height = sizehint.max_height;
+    if (sizehint.max_width > static_cast<signed>(client.min_width))
+      client.max_width = sizehint.max_width;
+    else
+      client.max_width = client.min_width;
+
+    if (sizehint.max_height > static_cast<signed>(client.min_height))
+      client.max_height = sizehint.max_height;
+    else
+      client.max_height = client.min_height;
   }
 
   if (sizehint.flags & PResizeInc) {
@@ -1016,12 +1228,14 @@ void BlackboxWindow::getWMNormalHints(void) {
     client.height_inc = sizehint.height_inc;
   }
 
+#if 0 // we do not support this at the moment
   if (sizehint.flags & PAspect) {
     client.min_aspect_x = sizehint.min_aspect.x;
     client.min_aspect_y = sizehint.min_aspect.y;
     client.max_aspect_x = sizehint.max_aspect.x;
     client.max_aspect_y = sizehint.max_aspect.y;
   }
+#endif
 
   if (sizehint.flags & PBaseSize) {
     client.base_width = sizehint.base_width;
@@ -1033,6 +1247,56 @@ void BlackboxWindow::getWMNormalHints(void) {
 }
 
 
+/*
+ * Gets the NETWM hints for the class' contained window.
+ */
+void BlackboxWindow::getNetWMHints(void) {
+  unsigned long workspace;
+
+  if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+                      workspace)) {
+    if (workspace == 0xffffffff)
+      flags.stuck = True;
+    else
+      blackbox_attrib.workspace = workspace;
+  }
+
+  unsigned long *state;
+  unsigned long num = (unsigned) -1;
+  if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
+                      num, &state)) {
+    bool vert = False,
+         horz = False;
+    for (unsigned long i = 0; i < num; ++i) {
+      if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
+        flags.modal = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
+        flags.shaded = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
+        flags.skip_taskbar = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
+        flags.skip_pager = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
+        flags.fullscreen = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
+        setState(IconicState);
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
+        vert = True;
+      else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
+        horz = True;
+    }
+    if (vert && horz)
+      flags.maximized = 1;
+    else if (vert)
+      flags.maximized = 2;
+    else if (horz)
+      flags.maximized = 3;
+
+    delete [] state;
+  }
+}
+
+
 /*
  * Gets the MWM hints for the class' contained window.
  * This is used while initializing the window to its first state, and not
@@ -1041,38 +1305,36 @@ void BlackboxWindow::getWMNormalHints(void) {
  * false if they are not.
  */
 void BlackboxWindow::getMWMHints(void) {
-  int format;
-  Atom atom_return;
-  unsigned long num, len;
-  MwmHints *mwm_hint = 0;
-
-  int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
-                               blackbox->getMotifWMHintsAtom(), 0,
-                               PropMwmHintsElements, False,
-                               blackbox->getMotifWMHintsAtom(), &atom_return,
-                               &format, &num, &len,
-                               (unsigned char **) &mwm_hint);
-
-  if (ret != Success || ! mwm_hint || num != PropMwmHintsElements)
+  unsigned long num;
+  MwmHints *mwm_hint;
+
+  num = PropMwmHintsElements;
+  if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
+                        XAtom::motif_wm_hints, num,
+                        (unsigned long **)&mwm_hint))
+    return;
+  if (num < PropMwmHintsElements) {
+    delete [] mwm_hint;
     return;
+  }
 
   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;
     }
   }
 
@@ -1095,7 +1357,7 @@ void BlackboxWindow::getMWMHints(void) {
         functions |= Func_Close;
     }
   }
-  XFree(mwm_hint);
+  delete [] mwm_hint;
 }
 
 
@@ -1107,19 +1369,18 @@ void BlackboxWindow::getMWMHints(void) {
  * they are not.
  */
 bool BlackboxWindow::getBlackboxHints(void) {
-  int format;
-  Atom atom_return;
-  unsigned long num, len;
-  BlackboxHints *blackbox_hint = 0;
-
-  int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
-                               blackbox->getBlackboxHintsAtom(), 0,
-                               PropBlackboxHintsElements, False,
-                               blackbox->getBlackboxHintsAtom(), &atom_return,
-                               &format, &num, &len,
-                               (unsigned char **) &blackbox_hint);
-  if (ret != Success || ! blackbox_hint || num != PropBlackboxHintsElements)
+  unsigned long num;
+  BlackboxHints *blackbox_hint;
+
+  num = PropBlackboxHintsElements;
+  if (! xatom->getValue(client.window, XAtom::blackbox_hints,
+                        XAtom::blackbox_hints, num,
+                        (unsigned long **)&blackbox_hint))
+    return False;
+  if (num < PropBlackboxHintsElements) {
+    delete [] blackbox_hint;
     return False;
+  }
 
   if (blackbox_hint->flags & AttribShaded)
     flags.shaded = (blackbox_hint->attrib & AttribShaded);
@@ -1146,41 +1407,20 @@ 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();
   }
-  XFree(blackbox_hint);
+  
+  delete [] blackbox_hint;
+
   return True;
 }
 
@@ -1188,13 +1428,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,
@@ -1234,9 +1473,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;
+  }
 }
 
 
@@ -1248,11 +1502,18 @@ BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
 }
 
 
+/*
+ * This function is responsible for updating both the client and the frame
+ * rectangles.
+ * According to the ICCCM a client message is not sent for a resize, only a
+ * move.
+ */
 void BlackboxWindow::configure(int dx, int dy,
                                unsigned int dw, unsigned int dh) {
-  bool send_event = (frame.rect.x() != dx || frame.rect.y() != dy);
+  bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
+                     ! flags.moving);
 
-  if ((dw != frame.rect.width()) || (dh != frame.rect.height())) {
+  if (dw != frame.rect.width() || dh != frame.rect.height()) {
     frame.rect.setRect(dx, dy, dw, dh);
     frame.inside_w = frame.rect.width() - (frame.border_w * 2);
     frame.inside_h = frame.rect.height() - (frame.border_w * 2);
@@ -1273,18 +1534,22 @@ void BlackboxWindow::configure(int dx, int dy,
 
     positionWindows();
     decorate();
-    setFocusFlag(flags.focused);
-    redrawAllButtons();
+    redrawWindowFrame();
   } else {
     frame.rect.setPos(dx, dy);
 
     XMoveWindow(blackbox->getXDisplay(), frame.window,
                 frame.rect.x(), frame.rect.y());
-
-    if (! flags.moving) send_event = True;
+    /*
+      we may have been called just after an opaque window move, so even though
+      the old coords match the new ones no ConfigureNotify has been sent yet.
+      There are likely other times when this will be relevant as well.
+    */
+    if (! flags.moving) send_event = True;
   }
 
-  if (send_event && ! flags.moving) {
+  if (send_event) {
+    // if moving, the update and event will occur when the move finishes
     client.rect.setPos(frame.rect.left() + frame.margin.left,
                        frame.rect.top() + frame.margin.top);
 
@@ -1302,10 +1567,10 @@ void BlackboxWindow::configure(int dx, int dy,
     event.xconfigure.above = frame.window;
     event.xconfigure.override_redirect = False;
 
-    XSendEvent(blackbox->getXDisplay(), client.window, True,
-               NoEventMask, &event);
-
+    XSendEvent(blackbox->getXDisplay(), client.window, False,
+               StructureNotifyMask, &event);
     screen->updateNetizenConfigNotify(&event);
+    XFlush(blackbox->getXDisplay());
   }
 }
 
@@ -1346,37 +1611,33 @@ void BlackboxWindow::configureShape(void) {
 bool BlackboxWindow::setInputFocus(void) {
   if (flags.focused) return True;
 
-  assert(! flags.iconic);
-
-  // 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;
-  }
+  assert(flags.stuck ||  // window must be on the current workspace or sticky
+         blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
 
-  if (! client.rect.intersects(screen->getRect())) {
-    // client is outside the screen, move it to the center
-    configure((screen->getWidth() - frame.rect.width()) / 2,
-              (screen->getHeight() - frame.rect.height()) / 2,
-              frame.rect.width(), frame.rect.height());
-  }
+  /*
+     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
+     it.
+  */
+  if (window_type == Type_Normal || window_type == Type_Dialog)
+    if (! frame.rect.intersects(screen->getRect())) {
+      // client is outside the screen, move it to the center
+      configure((screen->getWidth() - frame.rect.width()) / 2,
+                (screen->getHeight() - frame.rect.height()) / 2,
+                frame.rect.width(), frame.rect.height());
+    }
 
   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
@@ -1388,17 +1649,18 @@ bool BlackboxWindow::setInputFocus(void) {
   if (flags.send_focus_message) {
     XEvent ce;
     ce.xclient.type = ClientMessage;
-    ce.xclient.message_type = blackbox->getWMProtocolsAtom();
+    ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
     ce.xclient.display = blackbox->getXDisplay();
     ce.xclient.window = client.window;
     ce.xclient.format = 32;
-    ce.xclient.data.l[0] = blackbox->getWMTakeFocusAtom();
+    ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
     ce.xclient.data.l[1] = blackbox->getLastTime();
     ce.xclient.data.l[2] = 0l;
     ce.xclient.data.l[3] = 0l;
     ce.xclient.data.l[4] = 0l;
     XSendEvent(blackbox->getXDisplay(), client.window, False,
                NoEventMask, &ce);
+    XFlush(blackbox->getXDisplay());
   }
 
   return ret;
@@ -1408,10 +1670,13 @@ bool BlackboxWindow::setInputFocus(void) {
 void BlackboxWindow::iconify(void) {
   if (flags.iconic) 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)
+    endMove();
+    
   if (windowmenu) windowmenu->hide();
 
-  setState(IconicState);
-
   /*
    * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
    * we need to clear the event mask on client.window for a split second.
@@ -1419,18 +1684,27 @@ void BlackboxWindow::iconify(void) {
    * split second, leaving us with a ghost window... so, we need to do this
    * while the X server is grabbed
    */
+  unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
+                             StructureNotifyMask;
   XGrabServer(blackbox->getXDisplay());
-  XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
-  XUnmapWindow(blackbox->getXDisplay(), client.window);
   XSelectInput(blackbox->getXDisplay(), client.window,
-               PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
+               event_mask & ~StructureNotifyMask);
+  XUnmapWindow(blackbox->getXDisplay(), client.window);
+  XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
   XUngrabServer(blackbox->getXDisplay());
 
   XUnmapWindow(blackbox->getXDisplay(), frame.window);
   flags.visible = False;
   flags.iconic = True;
 
+  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 &&
@@ -1449,10 +1723,14 @@ void BlackboxWindow::iconify(void) {
       if (! (*it)->flags.iconic) (*it)->iconify();
     }
   }
+  screen->updateStackingList();
 }
 
 
 void BlackboxWindow::show(void) {
+  flags.visible = True;
+  flags.iconic = False;
+
   current_state = (flags.shaded) ? IconicState : NormalState;
   setState(current_state);
 
@@ -1460,15 +1738,23 @@ void BlackboxWindow::show(void) {
   XMapSubwindows(blackbox->getXDisplay(), frame.window);
   XMapWindow(blackbox->getXDisplay(), frame.window);
 
-  flags.visible = True;
-  flags.iconic = False;
+#if 0
+  int real_x, real_y;
+  Window child;
+  XTranslateCoordinates(blackbox->getXDisplay(), client.window,
+                        screen->getRootWindow(),
+                        0, 0, &real_x, &real_y, &child);
+  fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
+          client.rect.left(), client.rect.top(), real_x, real_y);
+  assert(client.rect.left() == real_x && client.rect.top() == real_y);
+#endif
 }
 
 
 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
   if (flags.iconic || reassoc)
     screen->reassociateWindow(this, BSENTINEL, False);
-  else if (blackbox_attrib.workspace != screen->getCurrentWorkspace()->getID())
+  else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
     return;
 
   show();
@@ -1476,9 +1762,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)
@@ -1489,33 +1774,41 @@ void BlackboxWindow::deiconify(bool reassoc, bool raise) {
 void BlackboxWindow::close(void) {
   XEvent ce;
   ce.xclient.type = ClientMessage;
-  ce.xclient.message_type = blackbox->getWMProtocolsAtom();
+  ce.xclient.message_type =  xatom->getAtom(XAtom::wm_protocols);
   ce.xclient.display = blackbox->getXDisplay();
   ce.xclient.window = client.window;
   ce.xclient.format = 32;
-  ce.xclient.data.l[0] = blackbox->getWMDeleteAtom();
+  ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
   ce.xclient.data.l[1] = CurrentTime;
   ce.xclient.data.l[2] = 0l;
   ce.xclient.data.l[3] = 0l;
   ce.xclient.data.l[4] = 0l;
   XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
+  XFlush(blackbox->getXDisplay());
 }
 
 
 void BlackboxWindow::withdraw(void) {
-  setState(current_state);
-
+  // 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)
+    endMove();
+    
   flags.visible = False;
   flags.iconic = False;
 
+  setState(current_state);
+
   XUnmapWindow(blackbox->getXDisplay(), frame.window);
 
   XGrabServer(blackbox->getXDisplay());
 
-  XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
-  XUnmapWindow(blackbox->getXDisplay(), client.window);
+  unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
+                             StructureNotifyMask;
   XSelectInput(blackbox->getXDisplay(), client.window,
-               PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
+               event_mask & ~StructureNotifyMask);
+  XUnmapWindow(blackbox->getXDisplay(), client.window);
+  XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
 
   XUngrabServer(blackbox->getXDisplay());
 
@@ -1524,6 +1817,11 @@ void BlackboxWindow::withdraw(void) {
 
 
 void BlackboxWindow::maximize(unsigned int button) {
+  // 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)
+    endMove();
+
   // handle case where menu is open then the max button is used instead
   if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
 
@@ -1534,7 +1832,7 @@ void BlackboxWindow::maximize(unsigned int button) {
     blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
 
     /*
-      when a resize is begun, maximize(0) is called to clear any maximization
+      when a resize finishes, maximize(0) is called to clear any maximization
       flags currently set.  Otherwise it still thinks it is maximized.
       so we do not need to call configure() because resizing will handle it
     */
@@ -1545,7 +1843,7 @@ void BlackboxWindow::maximize(unsigned int button) {
     blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
     blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
 
-    redrawAllButtons();
+    redrawAllButtons(); // in case it is not called in configure()
     setState(current_state);
     return;
   }
@@ -1557,9 +1855,21 @@ void BlackboxWindow::maximize(unsigned int button) {
   blackbox_attrib.premax_h =
     client.rect.height() + frame.margin.top + frame.margin.bottom;
 
-  const Rect &screen_area = screen->availableArea();
-  frame.changing = screen_area;
-  constrain(TopLeft);
+#ifdef    XINERAMA
+  if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
+    // find the area to use
+    RectList availableAreas = screen->allAvailableAreas();
+    RectList::iterator it, end = availableAreas.end();
+
+    for (it = availableAreas.begin(); it != end; ++it)
+      if (it->intersects(frame.rect)) break;
+    if (it == end) // the window isn't inside an area
+      it = availableAreas.begin(); // so just default to the first one
+
+    frame.changing = *it;
+  } else
+#endif // XINERAMA
+  frame.changing = screen->availableArea();
 
   switch(button) {
   case 1:
@@ -1584,6 +1894,8 @@ void BlackboxWindow::maximize(unsigned int button) {
     break;
   }
 
+  constrain(TopLeft);
+
   if (flags.shaded) {
     blackbox_attrib.flags ^= AttribShaded;
     blackbox_attrib.attrib ^= AttribShaded;
@@ -1596,13 +1908,34 @@ void BlackboxWindow::maximize(unsigned int button) {
             frame.changing.width(), frame.changing.height());
   if (flags.focused)
     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
-  redrawAllButtons();
+  redrawAllButtons(); // in case it is not called in configure()
   setState(current_state);
 }
 
 
 // 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,
@@ -1624,6 +1957,15 @@ void BlackboxWindow::remaximize(void) {
 void BlackboxWindow::setWorkspace(unsigned int n) {
   blackbox_attrib.flags |= AttribWorkspace;
   blackbox_attrib.workspace = n;
+  if (n == BSENTINEL) { // iconified window
+    /*
+       we set the workspace to 'all workspaces' so that taskbars will show the
+       window. otherwise, it made uniconifying a window imposible without the
+       blackbox workspace menu
+    */
+    n = 0xffffffff;
+  }
+  xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
 }
 
 
@@ -1642,7 +1984,7 @@ void BlackboxWindow::shade(void) {
                          frame.margin.bottom);
   } else {
     if (! (decorations & Decor_Titlebar))
-      return;
+      return; // can't shade it without a titlebar!
 
     XResizeWindow(blackbox->getXDisplay(), frame.window,
                   frame.inside_w, frame.title_h);
@@ -1658,15 +2000,26 @@ void BlackboxWindow::shade(void) {
 }
 
 
+/*
+ * (Un)Sticks a window and its relatives.
+ */
 void BlackboxWindow::stick(void) {
   if (flags.stuck) {
     blackbox_attrib.flags ^= AttribOmnipresent;
     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);
+    // temporary fix since sticky windows suck. set the hint to what we
+    // actually hold in our data.
+    xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
+                    blackbox_attrib.workspace);
 
     setState(current_state);
   } else {
@@ -1675,18 +2028,31 @@ void BlackboxWindow::stick(void) {
     blackbox_attrib.flags |= AttribOmnipresent;
     blackbox_attrib.attrib |= AttribOmnipresent;
 
+    // temporary fix since sticky windows suck. set the hint to a different
+    // 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);
   }
+  // go up the chain
+  if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
+      client.transient_for->isStuck() != flags.stuck)
+    client.transient_for->stick();
+  // go down the chain
+  BlackboxWindowList::iterator it;
+  const BlackboxWindowList::iterator end = client.transientList.end();
+  for (it = client.transientList.begin(); it != end; ++it)
+    if ((*it)->isStuck() != flags.stuck)
+      (*it)->stick();
 }
 
 
-void BlackboxWindow::setFocusFlag(bool focus) {
-  // only focus a window if it is visible
-  if (focus && !flags.visible)
-    return;
-
-  flags.focused = focus;
-
+void BlackboxWindow::redrawWindowFrame(void) const {
   if (decorations & Decor_Titlebar) {
     if (flags.focused) {
       if (frame.ftitle)
@@ -1762,14 +2128,29 @@ void BlackboxWindow::setFocusFlag(bool focus) {
       XSetWindowBorder(blackbox->getXDisplay(),
                        frame.plate, frame.uborder_pixel);
   }
+}
 
-  if (screen->isSloppyFocus() && screen->doAutoRaise()) {
-    if (isFocused()) timer->start();
-    else timer->stop();
-  }
 
-  if (isFocused())
+void BlackboxWindow::setFocusFlag(bool focus) {
+  // only focus a window if it is visible
+  if (focus && ! flags.visible)
+    return;
+
+  flags.focused = focus;
+
+  redrawWindowFrame();
+
+  if (flags.focused)
     blackbox->setFocusedWindow(this);
+  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);
+  }
 }
 
 
@@ -1777,8 +2158,8 @@ void BlackboxWindow::installColormap(bool install) {
   int i = 0, ncmap = 0;
   Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
                                             client.window, &ncmap);
-  XWindowAttributes wattrib;
   if (cmaps) {
+    XWindowAttributes wattrib;
     if (XGetWindowAttributes(blackbox->getXDisplay(),
                              client.window, &wattrib)) {
       if (install) {
@@ -1806,6 +2187,28 @@ void BlackboxWindow::installColormap(bool install) {
 }
 
 
+void BlackboxWindow::setAllowedActions(void) {
+  Atom actions[7];
+  int num = 0;
+  
+  actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
+  actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
+  actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
+
+  if (functions & Func_Move)
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
+  if (functions & Func_Resize)
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
+  if (functions & Func_Maximize) {
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
+    actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
+  }
+
+  xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
+                  actions, num);
+}
+
+
 void BlackboxWindow::setState(unsigned long new_state) {
   current_state = new_state;
 
@@ -1817,60 +2220,69 @@ void BlackboxWindow::setState(unsigned long new_state) {
   xatom->setValue(client.window, XAtom::blackbox_attributes,
                   XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
                   PropBlackboxAttributesElements);
+
+  Atom netstate[8];
+  int num = 0;
+  if (flags.modal)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
+  if (flags.shaded)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
+  if (flags.iconic)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
+  if (flags.skip_taskbar)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
+  if (flags.skip_pager)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
+  if (flags.fullscreen)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
+  if (flags.maximized == 1 || flags.maximized == 2)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
+  if (flags.maximized == 1 || flags.maximized == 3)
+    netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
+  xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
+                  netstate, num);
 }
 
 
 bool BlackboxWindow::getState(void) {
-  current_state = 0;
-
-  Atom atom_return;
-  bool ret = False;
-  unsigned long *state, nitems;
-
-  if (! xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state, nitems,
-                        &state))
-    return False;
-
-  current_state = static_cast<unsigned long>(state[0]);
-  delete state;
-
-  return True;
+  bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
+                             current_state);
+  if (! ret) current_state = 0;
+  return ret;
 }
 
 
 void BlackboxWindow::restoreAttributes(void) {
-  Atom atom_return;
-  int foo;
-  unsigned long ulfoo, nitems;
-
+  unsigned long num = PropBlackboxAttributesElements;
   BlackboxAttributes *net;
-  int ret = XGetWindowProperty(blackbox->getXDisplay(), client.window,
-                               blackbox->getBlackboxAttributesAtom(), 0l,
-                               PropBlackboxAttributesElements, False,
-                               blackbox->getBlackboxAttributesAtom(),
-                               &atom_return, &foo, &nitems, &ulfoo,
-                               (unsigned char **) &net);
-  if (ret != Success || ! net || nitems != PropBlackboxAttributesElements)
+  if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
+                        XAtom::blackbox_attributes, num,
+                        (unsigned long **)&net))
     return;
+  if (num < PropBlackboxAttributesElements) {
+    delete [] net;
+    return;
+  }
 
   if (net->flags & AttribShaded && net->attrib & AttribShaded) {
     flags.shaded = False;
+    unsigned long orig_state = current_state;
     shade();
 
     /*
-      Because the iconic'ness of shaded windows is lost, we need to set the
-      state to NormalState so that shaded windows on other workspaces will not
-      get shown on the first workspace.
       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.
     */
-    current_state = NormalState;
+    if (orig_state != IconicState)
+      current_state = WithdrawnState;
  }
 
-  if ((net->workspace != screen->getCurrentWorkspaceID()) &&
-      (net->workspace < screen->getWorkspaceCount())) {
+  if (net->workspace != screen->getCurrentWorkspaceID() &&
+      net->workspace < screen->getWorkspaceCount())
     screen->reassociateWindow(this, net->workspace, True);
 
+  if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
+      (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
     // set to WithdrawnState so it will be mapped on the new workspace
     if (current_state == NormalState) current_state = WithdrawnState;
   } else if (current_state == WithdrawnState) {
@@ -1879,8 +2291,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
@@ -1910,88 +2322,156 @@ void BlackboxWindow::restoreAttributes(void) {
     blackbox_attrib.premax_h = h;
   }
 
-  // with the state set it will then be the map events job to read the window's
-  // state and behave accordingly
+  if (net->flags & AttribDecoration) {
+    switch (net->decoration) {
+    case DecorNone:
+      enableDecor(False);
+      break;
 
-  XFree((void *) net);
+    /* 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
+
+  delete [] net;
 }
 
 
 /*
- * Positions the frame according the the client window position and window
- * gravity.
+ * Positions the Rect r according the the client window position and
+ * window gravity.
  */
-void BlackboxWindow::setGravityOffsets(void) {
-  // x coordinates for each gravity type
-  const int x_west = client.rect.x();
-  const int x_east = client.rect.right() - frame.inside_w + 1;
-  const int x_center = client.rect.left() +
-    ((client.rect.width() - frame.rect.width()) / 2);
-  // y coordinates for each gravity type
-  const int y_north = client.rect.y();
-  const int y_south = client.rect.bottom() - frame.inside_h + 1;
-  const int y_center = client.rect.top() +
-    ((client.rect.height() - frame.rect.height()) / 2);
+void BlackboxWindow::applyGravity(Rect &r) {
+  // apply horizontal window gravity
+  switch (client.win_gravity) {
+  default:
+  case NorthWestGravity:
+  case SouthWestGravity:
+  case WestGravity:
+    r.setX(client.rect.x());
+    break;
 
+  case NorthGravity:
+  case SouthGravity:
+  case CenterGravity:
+    r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
+    break;
+
+  case NorthEastGravity:
+  case SouthEastGravity:
+  case EastGravity:
+    r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setX(client.rect.x() - frame.margin.left);
+    break;
+  }
+
+  // apply vertical window gravity
   switch (client.win_gravity) {
   default:
-  case NorthWestGravity: frame.rect.setPos(x_west,   y_north);  break;
-  case NorthGravity:     frame.rect.setPos(x_center, y_north);  break;
-  case NorthEastGravity: frame.rect.setPos(x_east,   y_north);  break;
-  case SouthWestGravity: frame.rect.setPos(x_west,   y_south);  break;
-  case SouthGravity:     frame.rect.setPos(x_center, y_south);  break;
-  case SouthEastGravity: frame.rect.setPos(x_east,   y_south);  break;
-  case WestGravity:      frame.rect.setPos(x_west,   y_center); break;
-  case CenterGravity:    frame.rect.setPos(x_center, y_center); break;
-  case EastGravity:      frame.rect.setPos(x_east,   y_center); break;
+  case NorthWestGravity:
+  case NorthEastGravity:
+  case NorthGravity:
+    r.setY(client.rect.y());
+    break;
+
+  case CenterGravity:
+  case EastGravity:
+  case WestGravity:
+    r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
+    break;
+
+  case SouthWestGravity:
+  case SouthEastGravity:
+  case SouthGravity:
+    r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
+    break;
 
   case ForgetGravity:
   case StaticGravity:
-    frame.rect.setPos(client.rect.x() - frame.margin.left,
-                      client.rect.y() - frame.margin.top);
+    r.setY(client.rect.y() - frame.margin.top);
     break;
   }
 }
 
 
 /*
- * The reverse of the setGravityOffsets function. Uses the frame window's
- * position to find the window's reference point.
+ * The reverse of the applyGravity function.
+ *
+ * Positions the Rect r according to the frame window position and
+ * window gravity.
  */
-void BlackboxWindow::restoreGravity(void) {
-  // x coordinates for each gravity type
-  const int x_west = frame.rect.x();
-  const int x_east = frame.rect.x() + frame.inside_w - client.rect.width();
-  const int x_center = frame.rect.x() -
-    ((client.rect.width() - frame.rect.width()) / 2);
-  // y coordinates for each gravity type
-  const int y_north = frame.rect.y();
-  const int y_south = frame.rect.y() + frame.inside_h - client.rect.height();
-  const int y_center = frame.rect.y() -
-    ((client.rect.height() - frame.rect.height()) / 2);
-
-  switch(client.win_gravity) {
+void BlackboxWindow::restoreGravity(Rect &r) {
+  // restore horizontal window gravity
+  switch (client.win_gravity) {
   default:
-  case NorthWestGravity: client.rect.setPos(x_west,   y_north);  break;
-  case NorthGravity:     client.rect.setPos(x_center, y_north);  break;
-  case NorthEastGravity: client.rect.setPos(x_east,   y_north);  break;
-  case SouthWestGravity: client.rect.setPos(x_west,   y_south);  break;
-  case SouthGravity:     client.rect.setPos(x_center, y_south);  break;
-  case SouthEastGravity: client.rect.setPos(x_east,   y_south);  break;
-  case WestGravity:      client.rect.setPos(x_west,   y_center); break;
-  case CenterGravity:    client.rect.setPos(x_center, y_center); break;
-  case EastGravity:      client.rect.setPos(x_east,   y_center); break;
+  case NorthWestGravity:
+  case SouthWestGravity:
+  case WestGravity:
+    r.setX(frame.rect.x());
+    break;
+
+  case NorthGravity:
+  case SouthGravity:
+  case CenterGravity:
+    r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
+    break;
+
+  case NorthEastGravity:
+  case SouthEastGravity:
+  case EastGravity:
+    r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
+    break;
 
   case ForgetGravity:
   case StaticGravity:
-    client.rect.setPos(frame.rect.left() + frame.margin.left,
-                       frame.rect.top() + frame.margin.top);
+    r.setX(frame.rect.x() + frame.margin.left);
+    break;
+  }
+
+  // restore vertical window gravity
+  switch (client.win_gravity) {
+  default:
+  case NorthWestGravity:
+  case NorthEastGravity:
+  case NorthGravity:
+    r.setY(frame.rect.y());
+    break;
+
+  case CenterGravity:
+  case EastGravity:
+  case WestGravity:
+    r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
+    break;
+
+  case SouthWestGravity:
+  case SouthEastGravity:
+  case SouthGravity:
+    r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
+    break;
+
+  case ForgetGravity:
+  case StaticGravity:
+    r.setY(frame.rect.y() + frame.margin.top);
     break;
   }
 }
 
 
-void BlackboxWindow::redrawLabel(void) {
+void BlackboxWindow::redrawLabel(void) const {
   if (flags.focused) {
     if (frame.flabel)
       XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
@@ -2011,31 +2491,23 @@ void BlackboxWindow::redrawLabel(void) {
 
   WindowStyle *style = screen->getWindowStyle();
 
-  int pos = frame.bevel_w * 2,
-    dlen = style->doJustify(client.title.c_str(), pos, frame.label_w,
-                            frame.bevel_w * 4, i18n.multibyte());
-
-  BPen pen((flags.focused) ? style->l_text_focus : style->l_text_unfocus,
-           style->font);
-  if (i18n.multibyte())
-    XmbDrawString(blackbox->getXDisplay(), frame.label, style->fontset,
-                  pen.gc(), pos,
-                  (1 - style->fontset_extents->max_ink_extent.y),
-                  client.title.c_str(), dlen);
-  else
-    XDrawString(blackbox->getXDisplay(), frame.label, pen.gc(), pos,
-                (style->font->ascent + 1), client.title.c_str(), dlen);
+  int pos = frame.bevel_w * 2;
+  style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
+  style->font->drawString(frame.label, pos, 1,
+                          (flags.focused ? style->l_text_focus :
+                           style->l_text_unfocus),
+                          client.title);
 }
 
 
-void BlackboxWindow::redrawAllButtons(void) {
+void BlackboxWindow::redrawAllButtons(void) const {
   if (frame.iconify_button) redrawIconifyButton(False);
   if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
   if (frame.close_button) redrawCloseButton(False);
 }
 
 
-void BlackboxWindow::redrawIconifyButton(bool pressed) {
+void BlackboxWindow::redrawIconifyButton(bool pressed) const {
   if (! pressed) {
     if (flags.focused) {
       if (frame.fbutton)
@@ -2069,7 +2541,7 @@ void BlackboxWindow::redrawIconifyButton(bool pressed) {
 }
 
 
-void BlackboxWindow::redrawMaximizeButton(bool pressed) {
+void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
   if (! pressed) {
     if (flags.focused) {
       if (frame.fbutton)
@@ -2105,7 +2577,7 @@ void BlackboxWindow::redrawMaximizeButton(bool pressed) {
 }
 
 
-void BlackboxWindow::redrawCloseButton(bool pressed) {
+void BlackboxWindow::redrawCloseButton(bool pressed) const {
   if (! pressed) {
     if (flags.focused) {
       if (frame.fbutton)
@@ -2141,7 +2613,7 @@ void BlackboxWindow::redrawCloseButton(bool pressed) {
 }
 
 
-void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
+void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
   if (re->window != client.window)
     return;
 
@@ -2150,6 +2622,14 @@ void BlackboxWindow::mapRequestEvent(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 (blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
+      blackbox_attrib.workspace < screen->getWorkspaceCount())
+    if (current_state == NormalState) current_state = WithdrawnState;
+
   switch (current_state) {
   case IconicState:
     iconify();
@@ -2165,16 +2645,29 @@ void BlackboxWindow::mapRequestEvent(XMapRequestEvent *re) {
   default:
     show();
     screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
-    if (! blackbox->isStartup() && (isTransient() || screen->doFocusNew())) {
-      XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped..
-      setInputFocus();
+    if (isNormal()) {
+      if (! blackbox->isStartup()) {
+        XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
+        if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
+                                     getTransientFor()->isFocused())) {
+          setInputFocus();
+        }
+        if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
+          int x, y, rx, ry;
+          Window c, r;
+          unsigned int m;
+          XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
+                        &r, &c, &rx, &ry, &x, &y, &m);
+          beginMove(rx, ry);
+        }
+      }
     }
     break;
   }
 }
 
 
-void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
+void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
   if (ue->window != client.window)
     return;
 
@@ -2187,7 +2680,7 @@ void BlackboxWindow::unmapNotifyEvent(XUnmapEvent *ue) {
 }
 
 
-void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
+void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
   if (de->window != client.window)
     return;
 
@@ -2200,7 +2693,7 @@ void BlackboxWindow::destroyNotifyEvent(XDestroyWindowEvent *de) {
 }
 
 
-void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
+void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
   if (re->window != client.window || re->parent == frame.plate)
     return;
 
@@ -2216,21 +2709,34 @@ void BlackboxWindow::reparentNotifyEvent(XReparentEvent *re) {
 }
 
 
-void BlackboxWindow::propertyNotifyEvent(Atom atom) {
-  switch(atom) {
+void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
+  if (pe->state == PropertyDelete || ! validateClient())
+    return;
+
+#if 0
+  fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
+          client.window);
+#endif
+
+  switch(pe->atom) {
   case XA_WM_CLASS:
   case XA_WM_CLIENT_MACHINE:
   case XA_WM_COMMAND:
     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();
@@ -2246,6 +2752,7 @@ void BlackboxWindow::propertyNotifyEvent(Atom atom) {
     if (flags.iconic) screen->propagateWindowName(this);
     break;
 
+  case XAtom::net_wm_name:
   case XA_WM_NAME:
     getWMName();
 
@@ -2260,14 +2767,20 @@ void BlackboxWindow::propertyNotifyEvent(Atom atom) {
 
     if ((client.normal_hint_flags & PMinSize) &&
         (client.normal_hint_flags & PMaxSize)) {
+      // the window now can/can't resize itself, so the buttons need to be
+      // regrabbed.
+      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 {
-        decorations |= Decor_Maximize | Decor_Handle;
-        functions |= Func_Resize | Func_Maximize;
+        if (! isTransient())
+          functions |= Func_Maximize;
+        functions |= Func_Resize;
       }
+      grabButtons();
+      setAllowedActions();
+      setupDecor();
     }
 
     Rect old_rect = frame.rect;
@@ -2281,7 +2794,7 @@ void BlackboxWindow::propertyNotifyEvent(Atom atom) {
   }
 
   default:
-    if (atom == blackbox->getWMProtocolsAtom()) {
+    if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
       getWMProtocols();
 
       if ((decorations & Decor_Close) && (! frame.close_button)) {
@@ -2292,6 +2805,8 @@ void BlackboxWindow::propertyNotifyEvent(Atom atom) {
         }
         if (windowmenu) windowmenu->reconfigure();
       }
+    } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
+      updateStrut();
     }
 
     break;
@@ -2299,7 +2814,11 @@ void BlackboxWindow::propertyNotifyEvent(Atom atom) {
 }
 
 
-void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
+void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
+#if 0
+  fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
+#endif
+
   if (frame.label == ee->window && (decorations & Decor_Titlebar))
     redrawLabel();
   else if (frame.close_button == ee->window)
@@ -2311,32 +2830,35 @@ void BlackboxWindow::exposeEvent(XExposeEvent *ee) {
 }
 
 
-void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
+void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
   if (cr->window != client.window || flags.iconic)
     return;
 
-  int cx = frame.rect.x(), cy = frame.rect.y();
-  unsigned int cw = frame.rect.width(), ch = frame.rect.height();
-
   if (cr->value_mask & CWBorderWidth)
     client.old_bw = cr->border_width;
 
-  if (cr->value_mask & CWX)
-    cx = cr->x - frame.margin.left;
+  if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
+    Rect req = frame.rect;
 
-  if (cr->value_mask & CWY)
-    cy = cr->y - frame.margin.top;
+    if (cr->value_mask & (CWX | CWY)) {
+      if (cr->value_mask & CWX)
+        client.rect.setX(cr->x);
+      if (cr->value_mask & CWY)
+        client.rect.setY(cr->y);
 
-  if (cr->value_mask & CWWidth)
-    cw = cr->width + frame.margin.left + frame.margin.right;
+      applyGravity(req);
+    }
 
-  if (cr->value_mask & CWHeight)
-    ch = cr->height + frame.margin.top + frame.margin.bottom;
+    if (cr->value_mask & CWWidth)
+      req.setWidth(cr->width + frame.margin.left + frame.margin.right);
 
-  if (frame.rect != Rect(cx, cy, cw, ch))
-    configure(cx, cy, cw, ch);
+    if (cr->value_mask & CWHeight)
+      req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
 
-  if (cr->value_mask & CWStackMode) {
+    configure(req.x(), req.y(), req.width(), req.height());
+  }
+
+  if (cr->value_mask & CWStackMode && !isDesktop()) {
     switch (cr->detail) {
     case Below:
     case BottomIf:
@@ -2353,10 +2875,15 @@ void BlackboxWindow::configureRequestEvent(XConfigureRequestEvent *cr) {
 }
 
 
-void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
-  if (frame.maximize_button == be->window) {
+void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (frame.maximize_button == be->window && be->button <= 3) {
     redrawMaximizeButton(True);
-  } else if (be->button == 1 || (be->button == 3 && be->state == Mod1Mask)) {
+  } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
     if (! flags.focused)
       setInputFocus();
 
@@ -2374,7 +2901,7 @@ void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
       if (frame.title == be->window || frame.label == be->window) {
         if (((be->time - lastButtonPressTime) <=
              blackbox->getDoubleClickInterval()) ||
-            (be->state & ControlMask)) {
+            (be->state == ControlMask)) {
           lastButtonPressTime = 0;
           shade();
         } else {
@@ -2382,9 +2909,6 @@ void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
         }
       }
 
-      frame.grab_x = be->x_root - frame.rect.x() - frame.border_w;
-      frame.grab_y = be->y_root - frame.rect.y() - frame.border_w;
-
       if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
 
       screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
@@ -2395,392 +2919,794 @@ void BlackboxWindow::buttonPressEvent(XButtonEvent *be) {
   } else if (windowmenu && be->button == 3 &&
              (frame.title == be->window || frame.label == be->window ||
               frame.handle == be->window || frame.window == be->window)) {
-    int mx = 0, my = 0;
-
-    if (frame.title == be->window || frame.label == be->window) {
-      mx = be->x_root - (windowmenu->getWidth() / 2);
-      my = frame.rect.y() + frame.title_h + frame.border_w;
-    } else if (frame.handle == be->window) {
-      mx = be->x_root - (windowmenu->getWidth() / 2);
-      my = frame.rect.bottom() - frame.handle_h - (frame.border_w * 3) -
-           windowmenu->getHeight();
+    if (windowmenu->isVisible()) {
+      windowmenu->hide();
     } else {
-      mx = be->x_root - (windowmenu->getWidth() / 2);
+      int mx = be->x_root - windowmenu->getWidth() / 2,
+          my = be->y_root - windowmenu->getHeight() / 2;
 
-      if (be->y <= static_cast<signed>(frame.bevel_w))
-        my = frame.rect.y() + frame.title_h;
-      else
-        my = be->y_root - (windowmenu->getHeight() / 2);
-    }
+      // snap the window menu into a corner/side if necessary
+      int left_edge, right_edge, top_edge, bottom_edge;
 
-    // snap the window menu into a corner if necessary - we check the
-    // position of the menu with the coordinates of the client to
-    // make the comparisions easier.
-    // XXX: this needs some work!
-    if (mx > client.rect.right() -
-        static_cast<signed>(windowmenu->getWidth()))
-      mx = frame.rect.right() - windowmenu->getWidth() - frame.border_w + 1;
-    if (mx < client.rect.left())
-      mx = frame.rect.x();
-
-    if (my > client.rect.bottom() -
-        static_cast<signed>(windowmenu->getHeight()))
-      my = frame.rect.bottom() - windowmenu->getHeight() - frame.border_w + 1;
-    if (my < client.rect.top())
-      my = frame.rect.y() + ((decorations & Decor_Titlebar) ?
-                             frame.title_h : 0);
-
-    if (windowmenu) {
-      if (! windowmenu->isVisible()) {
-        windowmenu->move(mx, my);
-        windowmenu->show();
-        XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
-        XRaiseWindow(blackbox->getXDisplay(),
-                     windowmenu->getSendToMenu()->getWindowID());
-      } else {
-        windowmenu->hide();
-      }
+      /*
+         the " + (frame.border_w * 2) - 1" bits are to get the proper width
+         and height of the menu, as the sizes returned by it do not include
+         the borders.
+       */
+      left_edge = frame.rect.x();
+      right_edge = frame.rect.right() -
+        (windowmenu->getWidth() + (frame.border_w * 2) - 1);
+      top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
+      bottom_edge = client.rect.bottom() -
+        (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
+        (frame.border_w + frame.mwm_border_w);
+
+      if (mx < left_edge)
+        mx = left_edge;
+      if (mx > right_edge)
+        mx = right_edge;
+      if (my < top_edge)
+        my = top_edge;
+      if (my > bottom_edge)
+        my = bottom_edge;
+
+      windowmenu->move(mx, my);
+      windowmenu->show();
+      XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
+      XRaiseWindow(blackbox->getXDisplay(),
+                   windowmenu->getSendToMenu()->getWindowID());
     }
   // mouse wheel up
   } else if (be->button == 4) {
     if ((be->window == frame.label ||
-         be->window == frame.title) &&
+         be->window == frame.title ||
+         be->window == frame.maximize_button ||
+         be->window == frame.iconify_button ||
+         be->window == frame.close_button) &&
         ! flags.shaded)
       shade();
   // mouse wheel down
   } else if (be->button == 5) {
     if ((be->window == frame.label ||
-         be->window == frame.title) &&
+         be->window == frame.title ||
+         be->window == frame.maximize_button ||
+         be->window == frame.iconify_button ||
+         be->window == frame.close_button) &&
         flags.shaded)
       shade();
   }
 }
 
 
-void BlackboxWindow::buttonReleaseEvent(XButtonEvent *re) {
-  if (re->window == frame.maximize_button) {
+void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
+#ifdef DEBUG
+  fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (re->window == frame.maximize_button &&
+      re->button >= 1 && re->button <= 3) {
     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
       maximize(re->button);
     } else {
       redrawMaximizeButton(flags.maximized);
     }
-  } else if (re->window == frame.iconify_button) {
+  } else if (re->window == frame.iconify_button && re->button == 1) {
     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
       iconify();
     } else {
       redrawIconifyButton(False);
     }
-  } else if (re->window == frame.close_button) {
+  } else if (re->window == frame.close_button & re->button == 1) {
     if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
         (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
       close();
     redrawCloseButton(False);
   } else if (flags.moving) {
-    flags.moving = False;
+    endMove();
+  } else if (flags.resizing) {
+    endResize();
+  } else if (re->window == frame.window) {
+    if (re->button == 2 && re->state == mod_mask)
+      XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
+  }
+}
 
-    if (! screen->doOpaqueMove()) {
-      /* when drawing the rubber band, we need to make sure we only draw inside
-       * the frame... frame.changing_* contain the new coords for the window,
-       * so we need to subtract 1 from changing_w/changing_h every where we
-       * draw the rubber band (for both moving and resizing)
-       */
-      XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
-                     screen->getOpGC(), frame.changing.x(), frame.changing.y(),
-                     frame.changing.width() - 1, frame.changing.height() - 1);
-      XUngrabServer(blackbox->getXDisplay());
 
-      configure(frame.changing.x(), frame.changing.y(),
-                frame.changing.width(), frame.changing.height());
-    } else {
-      configure(frame.rect.x(), frame.rect.y(),
-                frame.rect.width(), frame.rect.height());
-    }
-    screen->hideGeometry();
-    XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
-  } else if (flags.resizing) {
+
+void BlackboxWindow::beginMove(int x_root, int y_root) {
+  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.
+  */
+  BlackboxWindow *changing = blackbox->getChangingWindow();
+  if (changing && changing != this) {
+    if (changing->flags.moving)
+      changing->endMove();
+    else // if (changing->flags.resizing)
+      changing->endResize();
+  }
+  
+  XGrabPointer(blackbox->getXDisplay(), frame.window, False,
+               PointerMotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync,
+               None, blackbox->getMoveCursor(), CurrentTime);
+
+  if (windowmenu && windowmenu->isVisible())
+    windowmenu->hide();
+
+  flags.moving = True;
+  blackbox->setChangingWindow(this);
+
+  if (! screen->doOpaqueMove()) {
+    XGrabServer(blackbox->getXDisplay());
+
+    frame.changing = frame.rect;
+    screen->showPosition(frame.changing.x(), frame.changing.y());
+
     XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
-                   screen->getOpGC(), frame.changing.x(), frame.changing.y(),
-                   frame.changing.width() - 1, frame.changing.height() - 1);
-    XUngrabServer(blackbox->getXDisplay());
+                   screen->getOpGC(),
+                   frame.changing.x(),
+                   frame.changing.y(),
+                   frame.changing.width() - 1,
+                   frame.changing.height() - 1);
+  }
 
-    screen->hideGeometry();
+  frame.grab_x = x_root - frame.rect.x() - frame.border_w;
+  frame.grab_y = y_root - frame.rect.y() - frame.border_w;
+}
 
-    constrain((re->window == frame.left_grip) ? TopRight : TopLeft);
 
-    // unset maximized state when resized after fully maximized
-    if (flags.maximized == 1)
-      maximize(0);
-    flags.resizing = False;
-    configure(frame.changing.x(), frame.changing.y(),
-              frame.changing.width(), frame.changing.height());
+void BlackboxWindow::doMove(int x_root, int y_root) {
+  assert(flags.moving);
+  assert(blackbox->getChangingWindow() == this);
 
-    XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
-  } else if (re->window == frame.window) {
-    if (re->button == 2 && re->state == Mod1Mask)
-      XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
+  int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
+  dx -= frame.border_w;
+  dy -= frame.border_w;
+
+  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(),
+                   screen->getOpGC(),
+                   frame.changing.x(),
+                   frame.changing.y(),
+                   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(),
+                   screen->getOpGC(),
+                   frame.changing.x(),
+                   frame.changing.y(),
+                   frame.changing.width() - 1,
+                   frame.changing.height() - 1);
   }
+
+  screen->showPosition(dx, dy);
 }
 
 
-void BlackboxWindow::motionNotifyEvent(XMotionEvent *me) {
-  if (! flags.resizing && (me->state & Button1Mask) &&
-      (functions & Func_Move) &&
-      (frame.title == me->window || frame.label == me->window ||
-       frame.handle == me->window || frame.window == me->window)) {
-    if (! flags.moving) {
-      XGrabPointer(blackbox->getXDisplay(), me->window, False,
-                   Button1MotionMask | ButtonReleaseMask,
-                   GrabModeAsync, GrabModeAsync,
-                   None, blackbox->getMoveCursor(), CurrentTime);
+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 (windowmenu && windowmenu->isVisible())
-        windowmenu->hide();
+    if (dest > 0) dest--;
+    else dest = screen->getNumberOfWorkspaces() - 1;
 
-      flags.moving = True;
+  } else if (x_root >= screen->getRect().right()) {
+    warp = True;
 
-      if (! screen->doOpaqueMove()) {
-        XGrabServer(blackbox->getXDisplay());
+    if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
+    else dest = 0;
+  }
+  if (! warp)
+    return;
 
-        frame.changing = frame.rect;
-        screen->showPosition(frame.changing.x(), frame.changing.y());
+  bool focus = flags.focused; // had focus while moving?
 
-        XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
-                       screen->getOpGC(),
-                       frame.changing.x(),
-                       frame.changing.y(),
-                       frame.changing.width() - 1,
-                       frame.changing.height() - 1);
-      }
-    } else {
-      int dx = me->x_root - frame.grab_x, dy = me->y_root - frame.grab_y;
-      dx -= frame.border_w;
-      dy -= frame.border_w;
-
-      const int snap_distance = screen->getEdgeSnapThreshold();
-
-      if (snap_distance) {
-        // window corners
-        const int wleft = dx,
-                 wright = dx + frame.rect.width() - 1,
-                   wtop = dy,
-                wbottom = dy + frame.rect.height() - 1;
-
-        // Maybe this should be saved in the class, and set in the setWorkspace
-        // function!!
-        Workspace *w = screen->getWorkspace(getWorkspaceNumber());
-        assert(w);
-
-        if (screen->getWindowToWindowSnap()) {
-          // 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
-
-            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());
-
-            // snap top of other window?
-            if (dtop < snap_distance && dtop <= dbottom) {
-              dy = winrect.top() - frame.rect.height();
-
-              if (screen->getWindowCornerSnap()) {
-                // try corner-snap to its other sides
-                dleft = std::abs(wleft - winrect.left());
-                dright = std::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;
-              }
+  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;
+  }
 
-              continue;
-            }
-            // snap bottom of other window?
-            else if (dbottom < snap_distance) {
-              dy = winrect.bottom() + 1;
-
-              if (screen->getWindowCornerSnap()) {
-                // try corner-snap to its other sides
-                dleft = std::abs(wleft - winrect.left());
-                dright = std::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;
-              }
+  if (! flags.stuck)
+    screen->reassociateWindow(this, dest, False);
+  screen->changeWorkspaceID(dest);
 
-              continue;
-            }
+  if (screen->doOpaqueMove())
+    XGrabServer(blackbox->getXDisplay());
 
-            // snap left of other window?
-            if (dleft < snap_distance && dleft <= dright) {
-              dx = winrect.left() - frame.rect.width();
+  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->getWindowCornerSnap()) {
-                // try corner-snap to its other sides
-                dtop = std::abs(wtop - winrect.top());
-                dbottom = std::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;
-              }
+  if (screen->doOpaqueMove())
+    XUngrabServer(blackbox->getXDisplay());
 
-              continue;
-            }
-            // snap right of other window?
-            else if (dright < snap_distance) {
-              dx = winrect.right() + 1;
-
-              if (screen->getWindowCornerSnap()) {
-                // try corner-snap to its other sides
-                dtop = std::abs(wtop - winrect.top());
-                dbottom = std::abs(wbottom - winrect.bottom());
-                if (dtop < snap_distance && dtop <= dbottom)
+  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);
+    }
+
+    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_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;
+
+      int dleft, dright, dtop, dbottom;
+
+      // 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)) {
+
+        if (snap_to_windows == BScreen::WindowResistance) {
+          dleft = wright - offsetrect.left();
+          dright = offsetrect.right() - wleft;
+
+          // 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 = offsetrect.left() - frame.rect.width();
+            snapped = True;
+          }
+          // snap right of other window?
+          else if (dright < snap_distance) {
+            dx = offsetrect.right() + 1;
+            snapped = True;
+          }            
+        }
+
+        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 < snap_distance)
+              } 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;
+        }
+      }
+
+      // 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 = offsetrect.top() - frame.rect.height();
+            snapped = True;
+          }
+          // snap bottom of other window?
+          else if (dbottom < snap_distance) {
+            dy = offsetrect.bottom() + 1;
+            snapped = True;
+          }
+
+        }
 
-              continue;
+        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;
         }
-                
-        // try snap to the screen's available area
-        Rect srect = screen->availableArea();
+      }
+    }
+  }
 
-        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());
+  if (snap_to_edges) {
+    RectList rectlist;
+
+    // snap to the screen edges (and screen boundaries for xinerama)
+#ifdef    XINERAMA
+    if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
+      rectlist.insert(rectlist.begin(),
+                      screen->getXineramaAreas().begin(),
+                      screen->getXineramaAreas().end());
+    } else
+#endif // XINERAMA
+      rectlist.push_back(screen->getRect());
+
+    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();
+
+        // 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;
+
+        // 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;
-
-        if (! screen->doFullMax()) {
-          srect = screen->getRect(); // now get the full screen
-
-          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 left?
-          if (dleft < snap_distance && dleft <= dright)
-            dx = srect.left();
-          // snap right?
-          else if (dright < snap_distance)
-            dx = srect.right() - frame.rect.width() + 1;
-
-          // snap top?
-          if (dtop < snap_distance && dtop <= dbottom)
-            dy = srect.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);
-    }
-  } else if ((functions & Func_Resize) &&
-             (((me->state & Button1Mask) &&
-               (me->window == frame.right_grip ||
-                me->window == frame.left_grip)) ||
-              (me->state & (Mod1Mask | Button3Mask) &&
-               me->window == frame.window))) {
-    bool left = (me->window == frame.left_grip);
-
-    if (! flags.resizing) {
-      XGrabServer(blackbox->getXDisplay());
-      XGrabPointer(blackbox->getXDisplay(), me->window, False,
-                   ButtonMotionMask | ButtonReleaseMask,
-                   GrabModeAsync, GrabModeAsync, None,
-                   ((left) ? blackbox->getLowerLeftAngleCursor() :
-                    blackbox->getLowerRightAngleCursor()),
-                   CurrentTime);
-
-      flags.resizing = True;
-
-      int gw, gh;
-      frame.grab_x = me->x;
-      frame.grab_y = me->y;
-      frame.changing = frame.rect;
-
-      constrain((left) ? TopRight : TopLeft, &gw, &gh);
-
-      XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
-                     screen->getOpGC(), frame.changing.x(), frame.changing.y(),
-                     frame.changing.width() - 1, frame.changing.height() - 1);
-
-      screen->showGeometry(gw, gh);
-    } else {
-      XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
-                     screen->getOpGC(), frame.changing.x(), frame.changing.y(),
-                     frame.changing.width() - 1, frame.changing.height() - 1);
+void BlackboxWindow::endMove(void) {
+  assert(flags.moving);
+  assert(blackbox->getChangingWindow() == this);
+
+  flags.moving = False;
+  blackbox->setChangingWindow(0);
 
-      int gw, gh;
+  if (! screen->doOpaqueMove()) {
+    /* when drawing the rubber band, we need to make sure we only draw inside
+     * the frame... frame.changing_* contain the new coords for the window,
+     * so we need to subtract 1 from changing_w/changing_h every where we
+     * draw the rubber band (for both moving and resizing)
+     */
+    XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+                   screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                   frame.changing.width() - 1, frame.changing.height() - 1);
+      XUngrabServer(blackbox->getXDisplay());
+  
+      configure(frame.changing.x(), frame.changing.y(),
+                frame.changing.width(), frame.changing.height());
+  } else {
+    configure(frame.rect.x(), frame.rect.y(),
+              frame.rect.width(), frame.rect.height());
+  }
+  screen->hideGeometry();
 
-      Corner anchor;
+  XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
+
+  // if there are any left over motions from the move, drop them now
+  XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
+  XEvent e;
+  while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
+                                MotionNotify, &e));
+}
 
-      if (left) {
-        anchor = TopRight;
-        frame.changing.setCoords(me->x_root - frame.grab_x, frame.rect.top(),
-                                 frame.rect.right(), frame.rect.bottom());
-        frame.changing.setHeight(frame.rect.height() + (me->y - frame.grab_y));
+
+void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
+  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.
+  */
+  BlackboxWindow *changing = blackbox->getChangingWindow();
+  if (changing && changing != this) {
+    if (changing->flags.moving)
+      changing->endMove();
+    else // if (changing->flags.resizing)
+      changing->endResize();
+  }
+
+  resize_dir = dir;
+
+  Cursor cursor;
+  Corner anchor;
+  
+  switch (resize_dir) {
+  case BottomLeft:
+    anchor = TopRight;
+    cursor = blackbox->getLowerLeftAngleCursor();
+    break;
+
+  case BottomRight:
+    anchor = TopLeft;
+    cursor = blackbox->getLowerRightAngleCursor();
+    break;
+
+  case TopLeft:
+    anchor = BottomRight;
+    cursor = blackbox->getUpperLeftAngleCursor();
+    break;
+
+  case TopRight:
+    anchor = BottomLeft;
+    cursor = blackbox->getUpperRightAngleCursor();
+    break;
+
+  default:
+    assert(false); // unhandled Corner
+    return;        // unreachable, for the compiler
+  }
+  
+  XGrabServer(blackbox->getXDisplay());
+  XGrabPointer(blackbox->getXDisplay(), frame.window, False,
+               PointerMotionMask | ButtonReleaseMask,
+               GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
+
+  flags.resizing = True;
+  blackbox->setChangingWindow(this);
+
+  unsigned int gw, gh;
+  frame.changing = frame.rect;
+
+  constrain(anchor,  &gw, &gh);
+
+  XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+
+  screen->showGeometry(gw, gh);
+  
+  frame.grab_x = x_root;
+  frame.grab_y = y_root;
+}
+
+
+void BlackboxWindow::doResize(int x_root, int y_root) {
+  assert(flags.resizing);
+  assert(blackbox->getChangingWindow() == this);
+
+  XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+
+  unsigned int gw, gh;
+  Corner anchor;
+
+  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));
+    break;
+  case BottomRight:
+    anchor = TopLeft;
+    frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
+                           frame.rect.height() + (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));
+    break;
+  case TopRight:
+    anchor = BottomLeft;
+    frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
+                           frame.rect.height() - (y_root - frame.grab_y));
+    break;
+
+  default:
+    assert(false); // unhandled Corner
+    return;        // unreachable, for the compiler
+  }
+  
+  constrain(anchor, &gw, &gh);
+
+  XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+
+  screen->showGeometry(gw, gh);
+}
+
+
+void BlackboxWindow::endResize(void) {
+  assert(flags.resizing);
+  assert(blackbox->getChangingWindow() == this);
+
+  XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
+                 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
+                 frame.changing.width() - 1, frame.changing.height() - 1);
+  XUngrabServer(blackbox->getXDisplay());
+
+  // unset maximized state after resized when fully maximized
+  if (flags.maximized == 1)
+    maximize(0);
+  
+  flags.resizing = False;
+  blackbox->setChangingWindow(0);
+
+  configure(frame.changing.x(), frame.changing.y(),
+            frame.changing.width(), frame.changing.height());
+  screen->hideGeometry();
+
+  XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
+  
+  // if there are any left over motions from the resize, drop them now
+  XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
+  XEvent e;
+  while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
+                                MotionNotify, &e));
+}
+
+
+void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
+#if 0
+  fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
+          client.window);
+#endif
+
+  if (flags.moving) {
+    doMove(me->x_root, me->y_root);
+  } else if (flags.resizing) {
+    doResize(me->x_root, me->y_root);
+  } else {
+    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 & 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;
+      
+      if (me->window == frame.left_grip) {
+        corner = BottomLeft;
+      } else if (me->window == frame.right_grip || zones == 1) {
+        corner = BottomRight;
       } else {
-        anchor = TopLeft;
-        frame.changing.setSize(frame.rect.width() + (me->x - frame.grab_x),
-                               frame.rect.height() + (me->y - frame.grab_y));
+        bool top;
+        bool left = (me->x_root - frame.rect.x() <=
+                     static_cast<signed>(frame.rect.width() / 2));
+        if (zones == 2)
+          top = False;
+        else // (zones == 4)
+          top = (me->y_root - frame.rect.y() <=
+                 static_cast<signed>(frame.rect.height() / 2));
+        corner = (top ? (left ? TopLeft : TopRight) :
+                        (left ? BottomLeft : BottomRight));
       }
 
-      constrain(anchor, &gw, &gh);
+      beginResize(me->x_root, me->y_root, corner);
+    }
+  }
+}
+
 
-      XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
-                     screen->getOpGC(), frame.changing.x(), frame.changing.y(),
-                     frame.changing.width() - 1, frame.changing.height() - 1);
+void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
+  if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
+    return;
 
-      screen->showGeometry(gw, gh);
+  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) && ! 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();
+}
+
+
+void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
+  if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
+    return;
+
+  installColormap(False);
+
+  if (timer->isTiming())
+    timer->stop();
 }
 
 
@@ -2815,7 +3741,17 @@ void BlackboxWindow::restore(bool remap) {
   XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
   XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
 
-  restoreGravity();
+  // do not leave a shaded window as an icon unless it was an icon
+  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);
   XUnmapWindow(blackbox->getXDisplay(), client.window);
@@ -2844,7 +3780,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)))
@@ -2890,59 +3826,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 (frame.window) {
-      XMapSubwindows(blackbox->getXDisplay(), frame.window);
-      XMapWindow(blackbox->getXDisplay(), frame.window);
-    }
-
-    reconfigure();
-    setState(current_state);
   }
 }
 
@@ -2970,12 +3863,7 @@ void BlackboxWindow::upsize(void) {
     // the height of the titlebar is based upon the height of the font being
     // used to display the window's title
     WindowStyle *style = screen->getWindowStyle();
-    if (i18n.multibyte())
-      frame.title_h = (style->fontset_extents->max_ink_extent.height +
-                       (frame.bevel_w * 2) + 2);
-    else
-      frame.title_h = (style->font->ascent + style->font->descent +
-                       (frame.bevel_w * 2) + 2);
+    frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
 
     frame.label_h = frame.title_h - (frame.bevel_w * 2);
     frame.button_w = (frame.label_h - 2);
@@ -3037,11 +3925,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,
@@ -3049,29 +3940,42 @@ 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<signed>(client.min_width)) dw = client.min_width;
-  if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
-  if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
-  if (dh > static_cast<signed>(client.max_height)) dh = client.max_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);
+
+  if (client.width_inc > 1) {
+    dw -= base_width;
+    dw /= client.width_inc;
+  }
+  if (client.height_inc > 1) {
+    dh -= base_height;
+    dh /= client.height_inc;
+  }
 
-  dw -= base_width;
-  dw /= client.width_inc;
-  dh -= base_height;
-  dh /= client.height_inc;
+  if (pw)
+    *pw = dw;
 
-  if (pw) *pw = dw;
-  if (ph) *ph = dh;
+  if (ph)
+    *ph = dh;
 
-  dw *= client.width_inc;
-  dw += base_width;
-  dh *= client.height_inc;
-  dh += base_height;
+  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);
 
@@ -3082,34 +3986,40 @@ void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
                            frame.changing.bottom() + frame.margin.bottom);
 
   // move frame.changing to the specified anchor
+  int dx = 0,
+      dy = 0;
   switch (anchor) {
   case TopLeft:
-    // nothing to do
     break;
 
   case TopRight:
-    int dx = frame.rect.right() - frame.changing.right();
-    frame.changing.setPos(frame.changing.x() + dx, frame.changing.y());
+    dx = frame.rect.right() - frame.changing.right();
+    break;
+
+  case BottomLeft:
+    dy = frame.rect.bottom() - frame.changing.bottom();
+    break;
+
+  case BottomRight:
+    dx = frame.rect.right() - frame.changing.right();
+    dy = frame.rect.bottom() - frame.changing.bottom();
     break;
+
+  default:
+    assert(false);  // unhandled corner
   }
+  frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
 }
 
 
-int WindowStyle::doJustify(const char *text, int &start_pos,
-                           unsigned int max_length, unsigned int modifier,
-                           bool multibyte) const {
-  size_t text_len = strlen(text);
+void WindowStyle::doJustify(const std::string &text, int &start_pos,
+                            unsigned int max_length,
+                            unsigned int modifier) const {
+  size_t text_len = text.size();
   unsigned int length;
 
   do {
-    if (multibyte) {
-      XRectangle ink, logical;
-      XmbTextExtents(fontset, text, text_len, &ink, &logical);
-      length = logical.width;
-    } else {
-      length = XTextWidth(font, text, text_len);
-    }
-    length += modifier;
+    length = font->measureString(string(text, 0, text_len)) + modifier;
   } while (length > max_length && text_len-- > 0);
 
   switch (justify) {
@@ -3125,8 +4035,6 @@ int WindowStyle::doJustify(const char *text, int &start_pos,
   default:
     break;
   }
-
-  return text_len;
 }
 
 
@@ -3139,16 +4047,8 @@ BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
     return;
   }
 
-  /*
-    watch for destroy notify on the group window (in addition to
-    any other events we are looking for)
-
-    since some managed windows can also be window group controllers,
-    we need to make sure that we don't clobber the event mask for the
-    managed window
-  */
   XSelectInput(blackbox->getXDisplay(), group,
-               wattrib.your_event_mask | StructureNotifyMask);
+               PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
 
   blackbox->saveGroupSearch(group, this);
 }
@@ -3164,13 +4064,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;
@@ -3179,10 +4076,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;
This page took 0.096628 seconds and 4 git commands to generate.