[metacity] libmetacity: redo button layout code



commit b2106b20993ad67e71a913acffb729445911b007
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Thu Aug 18 02:37:25 2016 +0300

    libmetacity: redo button layout code

 libmetacity/Makefile.am                            |    4 +-
 libmetacity/meta-button-layout-private.h           |   16 +-
 libmetacity/meta-button-layout.c                   |  184 ++---
 libmetacity/{meta-button-enums.h => meta-button.h} |   27 +-
 libmetacity/meta-frame-style-private.h             |    2 +-
 libmetacity/meta-theme-gtk.c                       |  503 +++++-------
 libmetacity/meta-theme-impl-private.h              |   19 +-
 libmetacity/meta-theme-impl.c                      |  195 ++++--
 libmetacity/meta-theme-metacity.c                  |  750 +++++++-----------
 libmetacity/meta-theme.c                           |  109 +++-
 libmetacity/meta-theme.h                           |   50 +-
 src/ui/frames.c                                    |  859 ++++++++++----------
 src/ui/frames.h                                    |    2 +
 13 files changed, 1323 insertions(+), 1397 deletions(-)
---
diff --git a/libmetacity/Makefile.am b/libmetacity/Makefile.am
index 426cb8c..785e3e3 100644
--- a/libmetacity/Makefile.am
+++ b/libmetacity/Makefile.am
@@ -3,7 +3,7 @@ NULL =
 lib_LTLIBRARIES = libmetacity.la
 
 libmetacity_la_SOURCES = \
-       meta-button-enums.h \
+       meta-button.h \
        meta-button-layout.c \
        meta-button-layout-private.h \
        meta-button-private.h \
@@ -69,7 +69,7 @@ libmetacity_la_LIBADD = \
 
 libmetacity_includedir = $(includedir)/metacity/libmetacity
 libmetacity_include_HEADERS = \
-       meta-button-enums.h \
+       meta-button.h \
        meta-color.h \
        meta-enum-types.h \
        meta-frame-borders.h \
diff --git a/libmetacity/meta-button-layout-private.h b/libmetacity/meta-button-layout-private.h
index 62c62df..4926b1d 100644
--- a/libmetacity/meta-button-layout-private.h
+++ b/libmetacity/meta-button-layout-private.h
@@ -19,23 +19,17 @@
 #ifndef META_BUTTON_LAYOUT_PRIVATE_H
 #define META_BUTTON_LAYOUT_PRIVATE_H
 
-#include "meta-button-enums.h"
+#include "meta-button.h"
 
 G_BEGIN_DECLS
 
 typedef struct
 {
-  /* buttons in the group on the left side */
-  MetaButtonType  left_buttons[META_BUTTON_TYPE_LAST];
-  gboolean        left_buttons_has_spacer[META_BUTTON_TYPE_LAST];
-  gint            n_left_buttons;
+  MetaButton *left_buttons;
+  gint        n_left_buttons;
 
-  /* buttons in the group on the right side */
-  MetaButtonType  right_buttons[META_BUTTON_TYPE_LAST];
-  gboolean        right_buttons_has_spacer[META_BUTTON_TYPE_LAST];
-  gint            n_right_buttons;
-
-  MetaButtonState button_states[META_BUTTON_TYPE_LAST];
+  MetaButton *right_buttons;
+  gint        n_right_buttons;
 } MetaButtonLayout;
 
 MetaButtonLayout *meta_button_layout_new  (const gchar      *str,
diff --git a/libmetacity/meta-button-layout.c b/libmetacity/meta-button-layout.c
index fb92712..4679cbf 100644
--- a/libmetacity/meta-button-layout.c
+++ b/libmetacity/meta-button-layout.c
@@ -21,7 +21,7 @@
 #include "meta-button-layout-private.h"
 
 static MetaButtonType
-meta_button_type_from_string (const gchar *str)
+type_from_string (const gchar *str)
 {
   if (g_strcmp0 (str, "menu") == 0)
     return META_BUTTON_TYPE_MENU;
@@ -45,12 +45,14 @@ meta_button_type_from_string (const gchar *str)
     return META_BUTTON_TYPE_STICK;
   else if (g_strcmp0 (str, "unstick") == 0)
     return META_BUTTON_TYPE_UNSTICK;
+  else if (g_strcmp0 (str, "spacer") == 0)
+    return META_BUTTON_TYPE_SPACER;
 
   return META_BUTTON_TYPE_LAST;
 }
 
 static MetaButtonType
-meta_button_type_get_opposite (MetaButtonType type)
+get_opposite_type (MetaButtonType type)
 {
   switch (type)
     {
@@ -74,155 +76,141 @@ meta_button_type_get_opposite (MetaButtonType type)
       case META_BUTTON_TYPE_MINIMIZE:
       case META_BUTTON_TYPE_MAXIMIZE:
       case META_BUTTON_TYPE_CLOSE:
+      case META_BUTTON_TYPE_SPACER:
       case META_BUTTON_TYPE_LAST:
-        return META_BUTTON_TYPE_LAST;
-
       default:
-        return META_BUTTON_TYPE_LAST;
+        break;
     }
-}
-
-static void
-meta_button_layout_init (MetaButtonLayout *layout)
-{
-  gint i;
-
-  for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
-    {
-      layout->left_buttons[i] = META_BUTTON_TYPE_LAST;
-      layout->left_buttons_has_spacer[i] = FALSE;
 
-      layout->right_buttons[i] = META_BUTTON_TYPE_LAST;
-      layout->right_buttons_has_spacer[i] = FALSE;
-    }
+  return META_BUTTON_TYPE_LAST;
 }
 
-static void
-string_to_buttons (const gchar    *str,
-                   MetaButtonType  side_buttons[META_BUTTON_TYPE_LAST],
-                   gboolean        side_has_spacer[META_BUTTON_TYPE_LAST])
+static MetaButton *
+string_to_buttons (const gchar *str,
+                   gint        *n_buttons)
 {
-  gint i;
-  gint b;
-  gboolean used[META_BUTTON_TYPE_LAST];
   gchar **buttons;
+  MetaButton *retval;
+  gint index;
+  gint i;
+
+  *n_buttons = 0;
 
-  i = 0;
-  while (i < META_BUTTON_TYPE_LAST)
-    used[i++] = FALSE;
+  if (str == NULL)
+    return NULL;
 
   buttons = g_strsplit (str, ",", -1);
 
-  i = b = 0;
-  while (buttons[b] != NULL)
+  for (i = 0; buttons[i] != NULL; i++)
     {
       MetaButtonType type;
 
-      type = meta_button_type_from_string (buttons[b]);
+      type = type_from_string (buttons[i]);
 
-      if (i > 0 && g_strcmp0 ("spacer", buttons[b]) == 0)
+      if (type != META_BUTTON_TYPE_LAST)
         {
-          side_has_spacer[i - 1] = TRUE;
-
-          type = meta_button_type_get_opposite (type);
-          if (type != META_BUTTON_TYPE_LAST)
-            side_has_spacer[i - 2] = TRUE;
+          if (get_opposite_type (type) != META_BUTTON_TYPE_LAST)
+            *n_buttons += 2;
+          else
+            *n_buttons += 1;
         }
       else
         {
-          if (type != META_BUTTON_TYPE_LAST && !used[type])
-            {
-              side_buttons[i] = type;
-              used[type] = TRUE;
-              i++;
+          g_debug ("Ignoring unknown button name - '%s'", buttons[i]);
+        }
+    }
 
-              type = meta_button_type_get_opposite (type);
-              if (type != META_BUTTON_TYPE_LAST)
-                side_buttons[i++] = type;
-            }
-          else
+  retval = g_new0 (MetaButton, *n_buttons);
+  index = 0;
+
+  for (i = 0; buttons[i] != NULL; i++)
+    {
+      MetaButtonType type;
+
+      type = type_from_string (buttons[i]);
+
+      if (type != META_BUTTON_TYPE_LAST)
+        {
+          GdkRectangle empty;
+          MetaButton tmp;
+
+          empty.x = 0;
+          empty.y = 0;
+          empty.width = 0;
+          empty.height = 0;
+
+          tmp.type = type;
+          tmp.state = META_BUTTON_STATE_NORMAL;
+          tmp.rect.visible = empty;
+          tmp.rect.clickable = empty;
+          tmp.visible = TRUE;
+
+          retval[index++] = tmp;
+
+          type = get_opposite_type (type);
+          if (type != META_BUTTON_TYPE_LAST)
             {
-              g_debug ("Ignoring unknown or already-used button name - '%s'",
-                       buttons[b]);
+              tmp.type = type;
+              retval[index++] = tmp;
             }
         }
-
-      b++;
     }
 
   g_strfreev (buttons);
+
+  return retval;
 }
 
 MetaButtonLayout *
 meta_button_layout_new (const gchar *str,
                         gboolean     invert)
 {
-  gchar **sides;
   MetaButtonLayout *layout;
-  MetaButtonLayout *rtl_layout;
-  gint i;
-  gint j;
+  gchar **sides;
+  const gchar *buttons;
+  gint n_buttons;
 
   layout = g_new0 (MetaButtonLayout, 1);
-  meta_button_layout_init (layout);
-
   sides = g_strsplit (str, ":", 2);
 
-  if (sides[0] != NULL)
-    {
-      string_to_buttons (sides[0], layout->left_buttons,
-                         layout->left_buttons_has_spacer);
-    }
+  buttons = sides[0];
+  layout->left_buttons = string_to_buttons (buttons, &n_buttons);
+  layout->n_left_buttons = n_buttons;
 
-  if (sides[0] != NULL && sides[1] != NULL)
-    {
-      string_to_buttons (sides[1], layout->right_buttons,
-                         layout->right_buttons_has_spacer);
-    }
+  buttons = sides[0] != NULL ? sides[1] : NULL;
+  layout->right_buttons = string_to_buttons (buttons, &n_buttons);
+  layout->n_right_buttons = n_buttons;
 
   g_strfreev (sides);
 
-  if (!invert)
-    return layout;
-
-  rtl_layout = g_new0 (MetaButtonLayout, 1);
-  meta_button_layout_init (rtl_layout);
-
-  i = 0;
-  while (rtl_layout->left_buttons[i] != META_BUTTON_TYPE_LAST)
-    i++;
-
-  for (j = 0; j < i; j++)
+  if (invert)
     {
-      rtl_layout->right_buttons[j] = layout->left_buttons[i - j - 1];
+      MetaButtonLayout *rtl_layout;
+      gint i;
 
-      if (j == 0)
-        rtl_layout->right_buttons_has_spacer[i - 1] = layout->left_buttons_has_spacer[i - j - 1];
-      else
-        rtl_layout->right_buttons_has_spacer[j - 1] = layout->left_buttons_has_spacer[i - j - 1];
-    }
+      rtl_layout = g_new0 (MetaButtonLayout, 1);
+      rtl_layout->left_buttons = g_new0 (MetaButton, layout->n_right_buttons);
+      rtl_layout->right_buttons = g_new0 (MetaButton, layout->n_left_buttons);
 
-  i = 0;
-  while (rtl_layout->left_buttons[i] != META_BUTTON_TYPE_LAST)
-    i++;
+      for (i = 0; i < layout->n_left_buttons; i++)
+        rtl_layout->right_buttons[i] = rtl_layout->left_buttons[layout->n_left_buttons - i];
 
-  for (j = 0; j < i; j++)
-    {
-      rtl_layout->left_buttons[j] = layout->right_buttons[i - j - 1];
+      for (i = 0; i < layout->n_right_buttons; i++)
+        rtl_layout->left_buttons[i] = rtl_layout->right_buttons[layout->n_right_buttons - i];
 
-      if (j == 0)
-        rtl_layout->left_buttons_has_spacer[i - 1] = layout->right_buttons_has_spacer[i - j - 1];
-      else
-        rtl_layout->left_buttons_has_spacer[j - 1] = layout->right_buttons_has_spacer[i - j - 1];
-    }
+      meta_button_layout_free (layout);
 
-  meta_button_layout_free (layout);
+      return rtl_layout;
+    }
 
-  return rtl_layout;
+  return layout;
 }
 
 void
 meta_button_layout_free (MetaButtonLayout *layout)
 {
+  g_free (layout->left_buttons);
+  g_free (layout->right_buttons);
+
   g_free (layout);
 }
diff --git a/libmetacity/meta-button-enums.h b/libmetacity/meta-button.h
similarity index 66%
rename from libmetacity/meta-button-enums.h
rename to libmetacity/meta-button.h
index 4ef4505..a087e14 100644
--- a/libmetacity/meta-button-enums.h
+++ b/libmetacity/meta-button.h
@@ -16,10 +16,10 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef META_BUTTON_ENUMS_H
-#define META_BUTTON_ENUMS_H
+#ifndef META_BUTTON_H
+#define META_BUTTON_H
 
-#include <glib.h>
+#include <gdk/gdk.h>
 
 G_BEGIN_DECLS
 
@@ -36,6 +36,7 @@ typedef enum
   META_BUTTON_TYPE_UNABOVE,
   META_BUTTON_TYPE_STICK,
   META_BUTTON_TYPE_UNSTICK,
+  META_BUTTON_TYPE_SPACER,
   META_BUTTON_TYPE_LAST
 } MetaButtonType;
 
@@ -47,6 +48,26 @@ typedef enum
   META_BUTTON_STATE_LAST
 } MetaButtonState;
 
+typedef struct
+{
+  MetaButtonType  type;
+  MetaButtonState state;
+
+  /* The computed size of a button (really just a way of tying its visible
+   * and clickable areas together). The reason for two different rectangles
+   * here is Fitts' law & maximized windows; See bug #97703 for more details.
+   */
+  struct {
+    /* The area where the button's image is drawn. */
+    GdkRectangle  visible;
+
+    /* The area where the button can be activated by clicking */
+    GdkRectangle clickable;
+  } rect;
+
+  gboolean       visible;
+} MetaButton;
+
 G_END_DECLS
 
 #endif
diff --git a/libmetacity/meta-frame-style-private.h b/libmetacity/meta-frame-style-private.h
index f3351b3..380ac18 100644
--- a/libmetacity/meta-frame-style-private.h
+++ b/libmetacity/meta-frame-style-private.h
@@ -19,7 +19,7 @@
 #ifndef META_FRAME_STYLE_PRIVATE_H
 #define META_FRAME_STYLE_PRIVATE_H
 
-#include "meta-button-enums.h"
+#include "meta-button.h"
 #include "meta-button-private.h"
 #include "meta-color-spec-private.h"
 #include "meta-draw-op-private.h"
diff --git a/libmetacity/meta-theme-gtk.c b/libmetacity/meta-theme-gtk.c
index 94fba18..22a8f67 100644
--- a/libmetacity/meta-theme-gtk.c
+++ b/libmetacity/meta-theme-gtk.c
@@ -288,120 +288,6 @@ meta_theme_gtk_get_frame_borders (MetaThemeImpl    *impl,
   scale_border (&borders->total, scale);
 }
 
-static MetaButtonSpace *
-rect_for_type (MetaFrameGeometry *fgeom,
-               MetaFrameFlags     flags,
-               MetaButtonType     type)
-{
-  switch (type)
-    {
-      case META_BUTTON_TYPE_MENU:
-        if (flags & META_FRAME_ALLOWS_MENU)
-          return &fgeom->menu_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_APPMENU:
-        if (flags & META_FRAME_ALLOWS_APPMENU)
-          return &fgeom->appmenu_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_MINIMIZE:
-        if (flags & META_FRAME_ALLOWS_MINIMIZE)
-          return &fgeom->min_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_MAXIMIZE:
-        if (flags & META_FRAME_ALLOWS_MAXIMIZE)
-          return &fgeom->max_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_CLOSE:
-        if (flags & META_FRAME_ALLOWS_DELETE)
-          return &fgeom->close_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_SHADE:
-        if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
-          return &fgeom->shade_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_ABOVE:
-        if (!(flags & META_FRAME_ABOVE))
-          return &fgeom->above_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_STICK:
-        if (!(flags & META_FRAME_STUCK))
-          return &fgeom->stick_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_UNSHADE:
-        if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
-          return &fgeom->unshade_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_UNABOVE:
-        if (flags & META_FRAME_ABOVE)
-          return &fgeom->unabove_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_UNSTICK:
-        if (flags & META_FRAME_STUCK)
-          return &fgeom->unstick_rect;
-        else
-          return NULL;
-
-      case META_BUTTON_TYPE_LAST:
-      default:
-        break;
-    }
-
-  return NULL;
-}
-
-static gboolean
-strip_button (MetaButtonSpace *func_rects[META_BUTTON_TYPE_LAST],
-              int             *n_rects,
-              MetaButtonSpace *to_strip)
-{
-  int i;
-
-  i = 0;
-  while (i < *n_rects)
-    {
-      if (func_rects[i] == to_strip)
-        {
-          *n_rects -= 1;
-
-          /* shift the other rects back in the array */
-          while (i < *n_rects)
-            {
-              func_rects[i] = func_rects[i+1];
-
-              ++i;
-            }
-
-          func_rects[i] = NULL;
-
-          return TRUE;
-        }
-
-      ++i;
-    }
-
-  return FALSE; /* did not strip anything */
-}
-
 static void
 meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
                               MetaFrameLayout   *layout,
@@ -425,14 +311,6 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
   int button_width, button_height;
   int min_size_for_rounding;
 
-  /* the left/right rects in order; the max # of rects
-   * is the number of button functions
-   */
-  MetaButtonSpace *left_func_rects[META_BUTTON_TYPE_LAST];
-  MetaButtonSpace *right_func_rects[META_BUTTON_TYPE_LAST];
-  gboolean left_buttons_has_spacer[META_BUTTON_TYPE_LAST];
-  gboolean right_buttons_has_spacer[META_BUTTON_TYPE_LAST];
-
   META_THEME_IMPL_GET_CLASS (impl)->get_frame_borders (impl, layout,
                                                        style_info, text_height,
                                                        flags, type, &borders);
@@ -464,16 +342,6 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
   button_width *= scale;
   button_height *= scale;
 
-  /* FIXME all this code sort of pretends that duplicate buttons
-   * with the same function are allowed, but that breaks the
-   * code in frames.c, so isn't really allowed right now.
-   * Would need left_close_rect, right_close_rect, etc.
-   */
-
-  /* Init all button rects to 0, lame hack */
-  memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
-          LENGTH_OF_BUTTON_RECTS);
-
   n_left = 0;
   n_right = 0;
   n_left_spacers = 0;
@@ -481,35 +349,44 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
 
   if (!layout->hide_buttons)
     {
-      /* Try to fill in rects */
-      for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->left_buttons[i] != META_BUTTON_TYPE_LAST; i++)
+      MetaButton *button;
+
+      for (i = 0; i < button_layout->n_left_buttons; i++)
         {
-          left_func_rects[n_left] = rect_for_type (fgeom, flags,
-                                                   button_layout->left_buttons[i]);
-          if (left_func_rects[n_left] != NULL)
-            {
-              left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
-              if (button_layout->left_buttons_has_spacer[i])
-                ++n_left_spacers;
+          button = &button_layout->left_buttons[i];
+          button->visible = is_button_visible (button, flags);
 
-              ++n_left;
+          if (button->visible)
+            {
+              if (button->type != META_BUTTON_TYPE_SPACER)
+                n_left++;
+              else
+                n_left_spacers++;
             }
         }
 
-      for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->right_buttons[i] != META_BUTTON_TYPE_LAST; i++)
+      for (i = 0; i < button_layout->n_right_buttons; i++)
         {
-          right_func_rects[n_right] = rect_for_type (fgeom, flags,
-                                                     button_layout->right_buttons[i]);
-          if (right_func_rects[n_right] != NULL)
-            {
-              right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
-              if (button_layout->right_buttons_has_spacer[i])
-                ++n_right_spacers;
+          button = &button_layout->right_buttons[i];
+          button->visible = is_button_visible (button, flags);
 
-              ++n_right;
+          if (button->visible)
+            {
+              if (button->type != META_BUTTON_TYPE_SPACER)
+                n_left++;
+              else
+                n_left_spacers++;
             }
         }
     }
+  else
+    {
+      for (i = 0; i < button_layout->n_left_buttons; i++)
+        button_layout->left_buttons[i].visible = FALSE;
+
+      for (i = 0; i < button_layout->n_right_buttons; i++)
+        button_layout->right_buttons[i].visible = FALSE;
+    }
 
   /* Be sure buttons fit */
   while (n_left > 0 || n_right > 0)
@@ -536,51 +413,41 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
       /* First try to remove separators */
       if (n_left_spacers > 0)
         {
-          left_buttons_has_spacer[--n_left_spacers] = FALSE;
-          continue;
+          if (strip_button (button_layout->left_buttons,
+                            button_layout->n_left_buttons,
+                            META_BUTTON_TYPE_SPACER))
+            {
+              n_left_spacers--;
+              continue;
+            }
+          else
+            {
+              g_assert_not_reached ();
+            }
         }
       else if (n_right_spacers > 0)
         {
-          right_buttons_has_spacer[--n_right_spacers] = FALSE;
-          continue;
+          if (strip_button (button_layout->right_buttons,
+                            button_layout->n_right_buttons,
+                            META_BUTTON_TYPE_SPACER))
+            {
+              n_right_spacers--;
+              continue;
+            }
+          else
+            {
+              g_assert_not_reached ();
+            }
         }
 
       /* Otherwise we need to shave out a button. Shave
        * above, stick, shade, min, max, close, then menu (menu is most useful);
        * prefer the default button locations.
        */
-      if (strip_button (left_func_rects, &n_left, &fgeom->above_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->above_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->stick_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->stick_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->shade_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->shade_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->min_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->min_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->max_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->max_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->close_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->close_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->menu_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->menu_rect))
-        continue;
-      else if (strip_button (right_func_rects, &n_right, &fgeom->appmenu_rect))
-        continue;
-      else if (strip_button (left_func_rects, &n_left, &fgeom->appmenu_rect))
-        continue;
+      if (strip_buttons (button_layout, &n_left, &n_right))
+        {
+          continue;
+        }
       else
         {
           g_error ("Could not find a button to strip. n_left = %d n_right = %d",
@@ -588,10 +455,6 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
         }
     }
 
-  /* Save the button layout */
-  button_layout->n_left_buttons = n_left;
-  button_layout->n_right_buttons = n_right;
-
   /* center buttons vertically */
   button_y = borders.invisible.top + layout->gtk.frame_border.top * scale +
              (content_height - button_height) / 2;
@@ -600,48 +463,61 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
   x = width - borders.invisible.right - layout->gtk.frame_border.right * scale -
       layout->gtk.titlebar_border.right * scale;
 
-  i = n_right - 1;
-  while (i >= 0)
+  for (i = button_layout->n_right_buttons - 1; i >= 0; i--)
     {
-      MetaButtonSpace *rect;
+      MetaButton *button;
+      GdkRectangle rect;
+
+      button = &button_layout->right_buttons[i];
+
+      if (button->visible == FALSE)
+        continue;
 
-      if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
+      /* if we go negative, leave the buttons we don't get to as 0 - width */
+      if (x < 0)
         break;
 
       x -= layout->gtk.button_margin.right * scale;
 
-      rect = right_func_rects[i];
+      rect.y = button_y;
+      rect.width = button_width;
+      rect.height = button_height;
 
-      rect->visible.x = x - button_width;
-      if (right_buttons_has_spacer[i])
-        rect->visible.x -= (button_width * 0.75);
+      if (button->type == META_BUTTON_TYPE_SPACER)
+        {
+          rect.x = x - button_width * 0.75;
+          rect.width *= 0.75;
+        }
+      else
+        {
+          rect.x = x - button_width;
+        }
 
-      rect->visible.y = button_y;
-      rect->visible.width = button_width;
-      rect->visible.height = button_height;
+      button->rect.visible = rect;
+      button->rect.clickable = rect;
 
-      if (flags & META_FRAME_MAXIMIZED ||
-          flags & META_FRAME_TILED_LEFT ||
-          flags & META_FRAME_TILED_RIGHT)
+      if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_RIGHT) &&
+          i == button_layout->n_right_buttons - 1)
         {
-          rect->clickable.x = rect->visible.x;
-          rect->clickable.y = rect->visible.y;
-          rect->clickable.width = button_width;
-          rect->clickable.height = button_height;
-
-          if (i == n_right - 1)
-            rect->clickable.width += layout->gtk.frame_border.right * scale +
-                                     layout->gtk.titlebar_border.right * scale;
+          gint extra_width;
+          gint extra_height;
+
+          extra_width = layout->gtk.button_margin.right * scale +
+                        layout->gtk.frame_border.right * scale +
+                        layout->gtk.titlebar_border.right * scale;
+
+          /* FIXME: */
+          extra_height = 0;
+
+          button->rect.clickable.y -= extra_height;
+          button->rect.clickable.width += extra_width;
+          button->rect.clickable.height += extra_height;
         }
-      else
-        g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
 
-      x = rect->visible.x - layout->gtk.button_margin.left * scale;
+      x = rect.x - layout->gtk.button_margin.left * scale;
 
       if (i > 0)
         x -= layout->gtk.titlebar_spacing * scale;
-
-      --i;
     }
 
   /* save right edge of titlebar for later use */
@@ -653,34 +529,50 @@ meta_theme_gtk_calc_geometry (MetaThemeImpl     *impl,
   x = borders.invisible.left + layout->gtk.frame_border.left * scale +
       layout->gtk.titlebar_border.left * scale;
 
-  for (i = 0; i < n_left; i++)
+  for (i = 0; i < button_layout->n_left_buttons; i++)
     {
-      MetaButtonSpace *rect;
+      MetaButton *button;
+      GdkRectangle rect;
+
+      button = &button_layout->left_buttons[i];
+
+      if (button->visible == FALSE)
+        continue;
 
-      x += layout->gtk.button_margin.left * scale;
+      rect.x = x + layout->gtk.button_margin.left * scale;
+      rect.y = button_y;
+      rect.width = button_width;
+      rect.height = button_height;
 
-      rect = left_func_rects[i];
+      if (button->type == META_BUTTON_TYPE_SPACER)
+        rect.width *= 0.75;
 
-      rect->visible.x = x;
-      rect->visible.y = button_y;
-      rect->visible.width = button_width;
-      rect->visible.height = button_height;
+      button->rect.visible = rect;
+      button->rect.clickable = rect;
 
-      if (flags & META_FRAME_MAXIMIZED)
+      if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_LEFT) &&
+          i == 0)
         {
-          rect->clickable.x = rect->visible.x;
-          rect->clickable.y = rect->visible.y;
-          rect->clickable.width = button_width;
-          rect->clickable.height = button_height;
+          gint extra_width;
+          gint extra_height;
+
+          extra_width = layout->gtk.button_margin.left * scale +
+                        layout->gtk.frame_border.left * scale +
+                        layout->gtk.titlebar_border.left * scale;
+
+          /* FIXME: */
+          extra_height = 0;
+
+          button->rect.clickable.x -= extra_width;
+          button->rect.clickable.y -= extra_height;
+          button->rect.clickable.width += extra_width;
+          button->rect.clickable.height += extra_height;
         }
-      else
-        g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
 
-      x = rect->visible.x + rect->visible.width + layout->gtk.button_margin.right * scale;
-      if (i < n_left - 1)
+      x = rect.x + rect.width + layout->gtk.button_margin.right * scale;
+
+      if (i < button_layout->n_left_buttons - 1)
         x += layout->gtk.titlebar_spacing * scale;
-      if (left_buttons_has_spacer[i])
-        x += (button_width * 0.75);
     }
 
   /* Center vertically in the available content area */
@@ -747,10 +639,10 @@ meta_theme_gtk_draw_frame (MetaThemeImpl           *impl,
   gdouble scale;
   GtkStyleContext *context;
   GtkStateFlags state;
-  MetaButtonType button_type;
   MetaRectangleDouble visible_rect;
   MetaRectangleDouble titlebar_rect;
   const MetaFrameBorders *borders;
+  gint side;
 
   /* We opt out of GTK+ HiDPI handling, so we have to do the scaling
    * ourselves; the nitty-gritty is a bit confusing, so here is an overview:
@@ -827,49 +719,73 @@ meta_theme_gtk_draw_frame (MetaThemeImpl           *impl,
 
   context = meta_style_info_get_style (style_info, META_STYLE_ELEMENT_BUTTON);
   state = gtk_style_context_get_state (context);
-  for (button_type = 0; button_type < META_BUTTON_TYPE_LAST; button_type++)
-    {
-      MetaButtonState button_state;
-      const char *button_class;
-      GdkRectangle tmp_rect;
-      MetaRectangleDouble button_rect;
 
-      button_class = get_class_from_button_type (button_type);
+  for (side = 0; side < 2; side++)
+    {
+      MetaButton *buttons;
+      gint n_buttons;
+      gint i;
 
-      if (button_class)
-        gtk_style_context_add_class (context, button_class);
+      if (side == 0)
+        {
+          buttons = button_layout->left_buttons;
+          n_buttons = button_layout->n_left_buttons;
+        }
+      else if (side == 1)
+        {
+          buttons = button_layout->right_buttons;
+          n_buttons = button_layout->n_right_buttons;
+        }
+      else
+        {
+          g_assert_not_reached ();
+        }
 
-      button_state = button_layout->button_states[button_type];
+      for (i = 0; i < n_buttons; i++)
+        {
+          MetaButton *button;
+          gdouble x;
+          gdouble y;
+          gdouble width;
+          gdouble height;
+          const gchar *button_class;
+          const gchar *icon_name;
+          GdkPixbuf *pixbuf;
+
+          button = &buttons[i];
+
+          x = button->rect.visible.x / scale;
+          y = button->rect.visible.y / scale;
+          width = button->rect.visible.width / scale;
+          height = button->rect.visible.height / scale;
+
+          if (!button->visible || button->type == META_BUTTON_TYPE_SPACER ||
+              width <= 0 || height <= 0)
+            {
+              continue;
+            }
 
-      if (button_state == META_BUTTON_STATE_PRELIGHT)
-        gtk_style_context_set_state (context, state | GTK_STATE_PRELIGHT);
-      else if (button_state == META_BUTTON_STATE_PRESSED)
-        gtk_style_context_set_state (context, state | GTK_STATE_ACTIVE);
-      else
-        gtk_style_context_set_state (context, state);
+          button_class = get_class_from_button_type (button->type);
 
-      cairo_save (cr);
+          if (button_class)
+            gtk_style_context_add_class (context, button_class);
 
-      get_button_rect_for_type (button_type, fgeom, &tmp_rect);
+          if (button->state == META_BUTTON_STATE_PRELIGHT)
+            gtk_style_context_set_state (context, state | GTK_STATE_PRELIGHT);
+          else if (button->state == META_BUTTON_STATE_PRESSED)
+            gtk_style_context_set_state (context, state | GTK_STATE_ACTIVE);
+          else
+            gtk_style_context_set_state (context, state);
 
-      button_rect.x = tmp_rect.x / scale;
-      button_rect.y = tmp_rect.y / scale;
-      button_rect.width = tmp_rect.width / scale;
-      button_rect.height = tmp_rect.height / scale;
+          cairo_save (cr);
 
-      if (button_rect.width > 0 && button_rect.height > 0)
-        {
-          GdkPixbuf *pixbuf = NULL;
-          const char *icon_name = NULL;
+          gtk_render_background (context, cr, x, y, width, height);
+          gtk_render_frame (context, cr, x, y, width, height);
 
-          gtk_render_background (context, cr,
-                                 button_rect.x, button_rect.y,
-                                 button_rect.width, button_rect.height);
-          gtk_render_frame (context, cr,
-                            button_rect.x, button_rect.y,
-                            button_rect.width, button_rect.height);
+          icon_name = NULL;
+          pixbuf = NULL;
 
-          switch (button_type)
+          switch (button->type)
             {
               case META_BUTTON_TYPE_CLOSE:
                 icon_name = "window-close-symbolic";
@@ -895,49 +811,58 @@ meta_theme_gtk_draw_frame (MetaThemeImpl           *impl,
               case META_BUTTON_TYPE_UNSHADE:
               case META_BUTTON_TYPE_UNABOVE:
               case META_BUTTON_TYPE_UNSTICK:
+              case META_BUTTON_TYPE_SPACER:
               case META_BUTTON_TYPE_LAST:
               default:
-                icon_name = NULL;
                 break;
             }
 
           if (icon_name)
             {
-              GtkIconTheme *theme = gtk_icon_theme_get_default ();
+              GtkIconTheme *theme;
               GtkIconInfo *info;
 
+              theme = gtk_icon_theme_get_default ();
               info = gtk_icon_theme_lookup_icon_for_scale (theme, icon_name,
                                                            style->layout->gtk.icon_size,
                                                            scale, 0);
 
+              g_assert (pixbuf == NULL);
               pixbuf = gtk_icon_info_load_symbolic_for_context (info, context, NULL, NULL);
             }
 
           if (pixbuf)
             {
-              float width, height;
-              gdouble x, y;
-
-              width = gdk_pixbuf_get_width (pixbuf) / scale;
-              height = gdk_pixbuf_get_height (pixbuf) / scale;
-              x = button_rect.x + (button_rect.width - width) / 2;
-              y = button_rect.y + (button_rect.height - height) / 2;
-
-              cairo_translate (cr, x, y);
-              cairo_scale (cr,
-                           width / style->layout->gtk.icon_size / scale,
-                           height / style->layout->gtk.icon_size / scale);
+              gdouble pwidth;
+              gdouble pheight;
+              gdouble px;
+              gdouble py;
+              gdouble scale_x;
+              gdouble scale_y;
+
+              pwidth = gdk_pixbuf_get_width (pixbuf) / scale;
+              pheight = gdk_pixbuf_get_height (pixbuf) / scale;
+              px = x + (width - pwidth) / 2;
+              py = y + (height - pheight) / 2;
+
+              scale_x = pwidth / style->layout->gtk.icon_size / scale;
+              scale_y = pheight / style->layout->gtk.icon_size / scale;
+
+              cairo_translate (cr, px, py);
+              cairo_scale (cr, scale_x, scale_y);
               gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
               cairo_paint (cr);
 
               g_object_unref (pixbuf);
             }
-        }
 
-      cairo_restore (cr);
+          cairo_restore (cr);
+
+          if (button_class)
+            gtk_style_context_remove_class (context, button_class);
 
-      if (button_class)
-        gtk_style_context_remove_class (context, button_class);
+          gtk_style_context_set_state (context, state);
+        }
     }
 }
 
diff --git a/libmetacity/meta-theme-impl-private.h b/libmetacity/meta-theme-impl-private.h
index 77cba2c..1112fc3 100644
--- a/libmetacity/meta-theme-impl-private.h
+++ b/libmetacity/meta-theme-impl-private.h
@@ -89,17 +89,26 @@ MetaFrameStyleSet *meta_theme_impl_get_style_set  (MetaThemeImpl           *impl
                                                    MetaFrameType            type);
 
 G_GNUC_INTERNAL
-void               get_button_rect_for_type       (MetaButtonType           type,
-                                                   const MetaFrameGeometry *fgeom,
-                                                   GdkRectangle            *rect);
-
-G_GNUC_INTERNAL
 void               scale_border                   (GtkBorder               *border,
                                                    double                   factor);
 
 G_GNUC_INTERNAL
 int                get_window_scaling_factor      (void);
 
+G_GNUC_INTERNAL
+gboolean           is_button_visible              (MetaButton              *button,
+                                                   MetaFrameFlags           flags);
+
+G_GNUC_INTERNAL
+gboolean           strip_button                   (MetaButton              *buttons,
+                                                   gint                     n_buttons,
+                                                   MetaButtonType           type);
+
+G_GNUC_INTERNAL
+gboolean           strip_buttons                  (MetaButtonLayout        *layout,
+                                                   gint                    *n_left,
+                                                   gint                    *n_right);
+
 G_END_DECLS
 
 #endif
diff --git a/libmetacity/meta-theme-impl.c b/libmetacity/meta-theme-impl.c
index ac2735c..1a1da9e 100644
--- a/libmetacity/meta-theme-impl.c
+++ b/libmetacity/meta-theme-impl.c
@@ -132,64 +132,6 @@ meta_theme_impl_get_style_set (MetaThemeImpl *impl,
 }
 
 void
-get_button_rect_for_type (MetaButtonType           type,
-                          const MetaFrameGeometry *fgeom,
-                          GdkRectangle            *rect)
-{
-  switch (type)
-    {
-    case META_BUTTON_TYPE_CLOSE:
-      *rect = fgeom->close_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_SHADE:
-      *rect = fgeom->shade_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_UNSHADE:
-      *rect = fgeom->unshade_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_ABOVE:
-      *rect = fgeom->above_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_UNABOVE:
-      *rect = fgeom->unabove_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_STICK:
-      *rect = fgeom->stick_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_UNSTICK:
-      *rect = fgeom->unstick_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_MAXIMIZE:
-      *rect = fgeom->max_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_MINIMIZE:
-      *rect = fgeom->min_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_MENU:
-      *rect = fgeom->menu_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_APPMENU:
-      *rect = fgeom->appmenu_rect.visible;
-      break;
-
-    case META_BUTTON_TYPE_LAST:
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-}
-
-void
 scale_border (GtkBorder *border,
               double     factor)
 {
@@ -214,3 +156,140 @@ get_window_scaling_factor (void)
   else
     return 1;
 }
+
+gboolean
+is_button_visible (MetaButton     *button,
+                   MetaFrameFlags  flags)
+{
+  gboolean visible;
+
+  visible = FALSE;
+
+  switch (button->type)
+    {
+      case META_BUTTON_TYPE_MENU:
+        if (flags & META_FRAME_ALLOWS_MENU)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_APPMENU:
+        if (flags & META_FRAME_ALLOWS_APPMENU)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_MINIMIZE:
+        if (flags & META_FRAME_ALLOWS_MINIMIZE)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_MAXIMIZE:
+        if (flags & META_FRAME_ALLOWS_MAXIMIZE)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_CLOSE:
+        if (flags & META_FRAME_ALLOWS_DELETE)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_SHADE:
+        if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_ABOVE:
+        if (!(flags & META_FRAME_ABOVE))
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_STICK:
+        if (!(flags & META_FRAME_STUCK))
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_UNSHADE:
+        if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_UNABOVE:
+        if (flags & META_FRAME_ABOVE)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_UNSTICK:
+        if (flags & META_FRAME_STUCK)
+          visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_SPACER:
+        visible = TRUE;
+        break;
+
+      case META_BUTTON_TYPE_LAST:
+      default:
+        break;
+    }
+
+  return visible;
+}
+
+gboolean
+strip_button (MetaButton     *buttons,
+              gint            n_buttons,
+              MetaButtonType  type)
+{
+  gint i;
+
+  for (i = 0; i < n_buttons; i++)
+    {
+      if (buttons[i].type == type && buttons[i].visible)
+        {
+          buttons[i].visible = FALSE;
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+gboolean
+strip_buttons (MetaButtonLayout *layout,
+               gint             *n_left,
+               gint             *n_right)
+{
+  gint count;
+  MetaButtonType types[META_BUTTON_TYPE_LAST];
+  gint i;
+
+  count = 0;
+  types[count++] = META_BUTTON_TYPE_ABOVE;
+  types[count++] = META_BUTTON_TYPE_UNABOVE;
+  types[count++] = META_BUTTON_TYPE_STICK;
+  types[count++] = META_BUTTON_TYPE_UNSTICK;
+  types[count++] = META_BUTTON_TYPE_SHADE;
+  types[count++] = META_BUTTON_TYPE_UNSHADE;
+  types[count++] = META_BUTTON_TYPE_MINIMIZE;
+  types[count++] = META_BUTTON_TYPE_MAXIMIZE;
+  types[count++] = META_BUTTON_TYPE_CLOSE;
+  types[count++] = META_BUTTON_TYPE_MENU;
+  types[count++] = META_BUTTON_TYPE_APPMENU;
+
+  for (i = 0; i < count; i++)
+    {
+      if (strip_button (layout->left_buttons, layout->n_left_buttons,
+                        types[i]))
+        {
+          *n_left -= 1;
+          return TRUE;
+        }
+      else if (strip_button (layout->right_buttons, layout->n_right_buttons,
+                             types[i]))
+        {
+          *n_left -= 1;
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
diff --git a/libmetacity/meta-theme-metacity.c b/libmetacity/meta-theme-metacity.c
index 7d41ec2..d39a2dd 100644
--- a/libmetacity/meta-theme-metacity.c
+++ b/libmetacity/meta-theme-metacity.c
@@ -4697,57 +4697,28 @@ meta_theme_metacity_get_frame_borders (MetaThemeImpl    *impl,
   scale_border (&borders->total, scale);
 }
 
-static MetaButtonSpace *
-rect_for_type (MetaThemeMetacity *metacity,
-               MetaFrameGeometry *fgeom,
-               MetaFrameFlags     flags,
-               MetaButtonType     type)
+static gboolean
+is_button_allowed (MetaThemeMetacity *metacity,
+                   MetaButtonType     type)
 {
   if (theme_allows (metacity, META_THEME_SHADE_STICK_ABOVE_BUTTONS))
     {
       switch (type)
         {
           case META_BUTTON_TYPE_SHADE:
-            if ((flags & META_FRAME_ALLOWS_SHADE) && !(flags & META_FRAME_SHADED))
-              return &fgeom->shade_rect;
-            else
-              return NULL;
-
           case META_BUTTON_TYPE_ABOVE:
-            if (!(flags & META_FRAME_ABOVE))
-              return &fgeom->above_rect;
-            else
-              return NULL;
-
           case META_BUTTON_TYPE_STICK:
-            if (!(flags & META_FRAME_STUCK))
-              return &fgeom->stick_rect;
-            else
-              return NULL;
-
           case META_BUTTON_TYPE_UNSHADE:
-            if ((flags & META_FRAME_ALLOWS_SHADE) && (flags & META_FRAME_SHADED))
-              return &fgeom->unshade_rect;
-            else
-              return NULL;
-
           case META_BUTTON_TYPE_UNABOVE:
-            if (flags & META_FRAME_ABOVE)
-              return &fgeom->unabove_rect;
-            else
-              return NULL;
-
           case META_BUTTON_TYPE_UNSTICK:
-            if (flags & META_FRAME_STUCK)
-              return &fgeom->unstick_rect;
-            else
-              return NULL;
+            return TRUE;
 
           case META_BUTTON_TYPE_MENU:
           case META_BUTTON_TYPE_APPMENU:
           case META_BUTTON_TYPE_MINIMIZE:
           case META_BUTTON_TYPE_MAXIMIZE:
           case META_BUTTON_TYPE_CLOSE:
+          case META_BUTTON_TYPE_SPACER:
           case META_BUTTON_TYPE_LAST:
           default:
             break;
@@ -4758,34 +4729,12 @@ rect_for_type (MetaThemeMetacity *metacity,
   switch (type)
     {
       case META_BUTTON_TYPE_MENU:
-        if (flags & META_FRAME_ALLOWS_MENU)
-          return &fgeom->menu_rect;
-        else
-          return NULL;
-
       case META_BUTTON_TYPE_APPMENU:
-        if (flags & META_FRAME_ALLOWS_APPMENU)
-          return &fgeom->appmenu_rect;
-        else
-          return NULL;
-
       case META_BUTTON_TYPE_MINIMIZE:
-        if (flags & META_FRAME_ALLOWS_MINIMIZE)
-          return &fgeom->min_rect;
-        else
-          return NULL;
-
       case META_BUTTON_TYPE_MAXIMIZE:
-        if (flags & META_FRAME_ALLOWS_MAXIMIZE)
-          return &fgeom->max_rect;
-        else
-          return NULL;
-
       case META_BUTTON_TYPE_CLOSE:
-        if (flags & META_FRAME_ALLOWS_DELETE)
-          return &fgeom->close_rect;
-        else
-          return NULL;
+      case META_BUTTON_TYPE_SPACER:
+        return TRUE;
 
       case META_BUTTON_TYPE_STICK:
       case META_BUTTON_TYPE_SHADE:
@@ -4798,50 +4747,14 @@ rect_for_type (MetaThemeMetacity *metacity,
          * therefore, we don't show the button. return NULL and all will
          * be well.
          */
-        return NULL;
+        break;
 
       case META_BUTTON_TYPE_LAST:
       default:
         break;
     }
 
-  return NULL;
-}
-
-static gboolean
-strip_button (MetaButtonSpace *func_rects[META_BUTTON_TYPE_LAST],
-              GdkRectangle    *bg_rects[META_BUTTON_TYPE_LAST],
-              int             *n_rects,
-              MetaButtonSpace *to_strip)
-{
-  int i;
-
-  i = 0;
-  while (i < *n_rects)
-    {
-      if (func_rects[i] == to_strip)
-        {
-          *n_rects -= 1;
-
-          /* shift the other rects back in the array */
-          while (i < *n_rects)
-            {
-              func_rects[i] = func_rects[i+1];
-              bg_rects[i] = bg_rects[i+1];
-
-              ++i;
-            }
-
-          func_rects[i] = NULL;
-          bg_rects[i] = NULL;
-
-          return TRUE;
-        }
-
-      ++i;
-    }
-
-  return FALSE; /* did not strip anything */
+  return FALSE;
 }
 
 static void
@@ -4867,16 +4780,6 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
   int min_size_for_rounding;
   int scale;
 
-  /* the left/right rects in order; the max # of rects
-   * is the number of button functions
-   */
-  MetaButtonSpace *left_func_rects[META_BUTTON_TYPE_LAST];
-  MetaButtonSpace *right_func_rects[META_BUTTON_TYPE_LAST];
-  GdkRectangle *left_bg_rects[META_BUTTON_TYPE_LAST];
-  gboolean left_buttons_has_spacer[META_BUTTON_TYPE_LAST];
-  GdkRectangle *right_bg_rects[META_BUTTON_TYPE_LAST];
-  gboolean right_buttons_has_spacer[META_BUTTON_TYPE_LAST];
-
   META_THEME_IMPL_GET_CLASS (impl)->get_frame_borders (impl, layout,
                                                        style_info, text_height,
                                                        flags, type, &borders);
@@ -4914,16 +4817,6 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
       break;
     }
 
-  /* FIXME all this code sort of pretends that duplicate buttons
-   * with the same function are allowed, but that breaks the
-   * code in frames.c, so isn't really allowed right now.
-   * Would need left_close_rect, right_close_rect, etc.
-   */
-
-  /* Init all button rects to 0, lame hack */
-  memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0',
-          LENGTH_OF_BUTTON_RECTS);
-
   n_left = 0;
   n_right = 0;
   n_left_spacers = 0;
@@ -4933,64 +4826,45 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
 
   if (!layout->hide_buttons)
     {
-      /* Try to fill in rects */
-      for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->left_buttons[i] != META_BUTTON_TYPE_LAST; i++)
+      MetaButton *button;
+
+      for (i = 0; i < button_layout->n_left_buttons; i++)
         {
-          left_func_rects[n_left] = rect_for_type (metacity, fgeom, flags,
-                                                   button_layout->left_buttons[i]);
-          if (left_func_rects[n_left] != NULL)
-            {
-              left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i];
-              if (button_layout->left_buttons_has_spacer[i])
-                ++n_left_spacers;
+          button = &button_layout->left_buttons[i];
+          button->visible = is_button_visible (button, flags) &&
+                            is_button_allowed (metacity, button->type);
 
-              ++n_left;
+          if (button->visible)
+            {
+              if (button->type != META_BUTTON_TYPE_SPACER)
+                n_left++;
+              else
+                n_left_spacers++;
             }
         }
 
-      for (i = 0; i < META_BUTTON_TYPE_LAST && button_layout->right_buttons[i] != META_BUTTON_TYPE_LAST; i++)
+      for (i = 0; i < button_layout->n_right_buttons; i++)
         {
-          right_func_rects[n_right] = rect_for_type (metacity, fgeom, flags,
-                                                     button_layout->right_buttons[i]);
-          if (right_func_rects[n_right] != NULL)
-            {
-              right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i];
-              if (button_layout->right_buttons_has_spacer[i])
-                ++n_right_spacers;
+          button = &button_layout->right_buttons[i];
+          button->visible = is_button_visible (button, flags) &&
+                            is_button_allowed (metacity, button->type);
 
-              ++n_right;
+          if (button->visible)
+            {
+              if (button->type != META_BUTTON_TYPE_SPACER)
+                n_left++;
+              else
+                n_left_spacers++;
             }
         }
     }
-
-  for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
+  else
     {
-      left_bg_rects[i] = NULL;
-      right_bg_rects[i] = NULL;
-    }
+      for (i = 0; i < button_layout->n_left_buttons; i++)
+        button_layout->left_buttons[i].visible = FALSE;
 
-  for (i = 0; i < n_left; i++)
-    {
-      if (n_left == 1)
-        left_bg_rects[i] = &fgeom->left_single_background;
-      else if (i == 0)
-        left_bg_rects[i] = &fgeom->left_left_background;
-      else if (i == (n_left - 1))
-        left_bg_rects[i] = &fgeom->left_right_background;
-      else
-        left_bg_rects[i] = &fgeom->left_middle_backgrounds[i - 1];
-    }
-
-  for (i = 0; i < n_right; i++)
-    {
-      if (n_right == 1)
-        right_bg_rects[i] = &fgeom->right_single_background;
-      else if (i == (n_right - 1))
-        right_bg_rects[i] = &fgeom->right_right_background;
-      else if (i == 0)
-        right_bg_rects[i] = &fgeom->right_left_background;
-      else
-        right_bg_rects[i] = &fgeom->right_middle_backgrounds[i - 1];
+      for (i = 0; i < button_layout->n_right_buttons; i++)
+        button_layout->right_buttons[i].visible = FALSE;
     }
 
   /* Be sure buttons fit */
@@ -5021,67 +4895,41 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
       /* First try to remove separators */
       if (n_left_spacers > 0)
         {
-          left_buttons_has_spacer[--n_left_spacers] = FALSE;
-          continue;
+          if (strip_button (button_layout->left_buttons,
+                            button_layout->n_left_buttons,
+                            META_BUTTON_TYPE_SPACER))
+            {
+              n_left_spacers--;
+              continue;
+            }
+          else
+            {
+              g_assert_not_reached ();
+            }
         }
       else if (n_right_spacers > 0)
         {
-          right_buttons_has_spacer[--n_right_spacers] = FALSE;
-          continue;
+          if (strip_button (button_layout->right_buttons,
+                            button_layout->n_right_buttons,
+                            META_BUTTON_TYPE_SPACER))
+            {
+              n_right_spacers--;
+              continue;
+            }
+          else
+            {
+              g_assert_not_reached ();
+            }
         }
 
       /* Otherwise we need to shave out a button. Shave
        * above, stick, shade, min, max, close, then menu (menu is most useful);
        * prefer the default button locations.
        */
-      if (strip_button (left_func_rects, left_bg_rects,
-                        &n_left, &fgeom->above_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->above_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                        &n_left, &fgeom->stick_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->stick_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                        &n_left, &fgeom->shade_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->shade_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                        &n_left, &fgeom->min_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->min_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                             &n_left, &fgeom->max_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->max_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                             &n_left, &fgeom->close_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->close_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->menu_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                             &n_left, &fgeom->menu_rect))
-        continue;
-      else if (strip_button (right_func_rects, right_bg_rects,
-                             &n_right, &fgeom->appmenu_rect))
-        continue;
-      else if (strip_button (left_func_rects, left_bg_rects,
-                             &n_left, &fgeom->appmenu_rect))
-        continue;
+      if (strip_buttons (button_layout, &n_left, &n_right))
+        {
+          continue;
+        }
       else
         {
           g_error ("Could not find a button to strip. n_left = %d n_right = %d",
@@ -5089,10 +4937,6 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
         }
     }
 
-  /* Save the button layout */
-  button_layout->n_left_buttons = n_left;
-  button_layout->n_right_buttons = n_right;
-
   /* center buttons vertically */
   button_y = (borders.visible.top -
               (button_height + layout->button_border.top * scale + layout->button_border.bottom * scale)) / 
2 + layout->button_border.top * scale + borders.invisible.top;
@@ -5100,45 +4944,56 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
   /* right edge of farthest-right button */
   x = width - layout->metacity.right_titlebar_edge * scale - borders.invisible.right;
 
-  i = n_right - 1;
-  while (i >= 0)
+  for (i = button_layout->n_right_buttons - 1; i >= 0; i--)
     {
-      MetaButtonSpace *rect;
+      MetaButton *button;
+      GdkRectangle rect;
 
-      if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
-        break;
+      button = &button_layout->right_buttons[i];
+
+      if (button->visible == FALSE)
+        continue;
 
-      rect = right_func_rects[i];
-      rect->visible.x = x - layout->button_border.right * scale - button_width;
-      if (right_buttons_has_spacer[i])
-        rect->visible.x -= (button_width * 0.75);
+      /* if we go negative, leave the buttons we don't get to as 0 - width */
+      if (x < 0)
+        break;
 
-      rect->visible.y = button_y;
-      rect->visible.width = button_width;
-      rect->visible.height = button_height;
+      rect.y = button_y;
+      rect.width = button_width;
+      rect.height = button_height;
 
-      if (flags & META_FRAME_MAXIMIZED ||
-          flags & META_FRAME_TILED_LEFT ||
-          flags & META_FRAME_TILED_RIGHT)
+      if (button->type == META_BUTTON_TYPE_SPACER)
         {
-          rect->clickable.x = rect->visible.x;
-          rect->clickable.y = rect->visible.y;
-          rect->clickable.width = button_width;
-          rect->clickable.height = button_height;
-
-          if (i == n_right - 1)
-            rect->clickable.width += layout->metacity.right_titlebar_edge * scale +
-                                     layout->metacity.right_width * scale +
-                                     layout->button_border.right * scale;
+          rect.x = x - button_width * 0.75;
+          rect.width *= 0.75;
         }
       else
-        g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
+        {
+          rect.x = x - layout->button_border.right * scale - button_width;
+        }
+
+      button->rect.visible = rect;
+      button->rect.clickable = rect;
 
-      *(right_bg_rects[i]) = rect->visible;
+      if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_RIGHT) &&
+          i == button_layout->n_right_buttons - 1)
+        {
+          gint extra_width;
+          gint extra_height;
+
+          extra_width = layout->metacity.right_titlebar_edge * scale +
+                        layout->metacity.right_width * scale +
+                        layout->button_border.right * scale;
 
-      x = rect->visible.x - layout->button_border.left * scale;
+          /* FIXME: */
+          extra_height = 0;
+
+          button->rect.clickable.y -= extra_height;
+          button->rect.clickable.width += extra_width;
+          button->rect.clickable.height += extra_height;
+        }
 
-      --i;
+      x = rect.x - layout->button_border.left * scale;
     }
 
   /* save right edge of titlebar for later use */
@@ -5148,32 +5003,48 @@ meta_theme_metacity_calc_geometry (MetaThemeImpl     *impl,
    * the left-side buttons
    */
   x = layout->metacity.left_titlebar_edge * scale + borders.invisible.left;
-  for (i = 0; i < n_left; i++)
+
+  for (i = 0; i < button_layout->n_left_buttons; i++)
     {
-      MetaButtonSpace *rect;
+      MetaButton *button;
+      GdkRectangle rect;
+
+      button = &button_layout->left_buttons[i];
+
+      if (button->visible == FALSE)
+        continue;
 
-      rect = left_func_rects[i];
+      rect.x = x + layout->button_border.left * scale;;
+      rect.y = button_y;
+      rect.width = button_width;
+      rect.height = button_height;
 
-      rect->visible.x = x + layout->button_border.left * scale;
-      rect->visible.y = button_y;
-      rect->visible.width = button_width;
-      rect->visible.height = button_height;
+      if (button->type == META_BUTTON_TYPE_SPACER)
+        rect.width *= 0.75;
 
-      if (flags & META_FRAME_MAXIMIZED)
+      button->rect.visible = rect;
+      button->rect.clickable = rect;
+
+      if ((flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_LEFT) &&
+          i == 0)
         {
-          rect->clickable.x = rect->visible.x;
-          rect->clickable.y = rect->visible.y;
-          rect->clickable.width = button_width;
-          rect->clickable.height = button_height;
-        }
-      else
-        g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
+          gint extra_width;
+          gint extra_height;
+
+          extra_width = layout->metacity.left_titlebar_edge * scale +
+                        layout->metacity.left_width * scale +
+                        layout->button_border.left * scale;
 
-      x = rect->visible.x + rect->visible.width + layout->button_border.right * scale;
-      if (left_buttons_has_spacer[i])
-        x += (button_width * 0.75);
+          /* FIXME: */
+          extra_height = 0;
 
-      *(left_bg_rects[i]) = rect->visible;
+          button->rect.clickable.x -= extra_width;
+          button->rect.clickable.y -= extra_height;
+          button->rect.clickable.width += extra_width;
+          button->rect.clickable.height += extra_height;
+        }
+
+      x = rect.x + rect.width + layout->button_border.right * scale;
     }
 
   /* We always fill as much vertical space as possible with title rect,
@@ -5286,179 +5157,111 @@ clip_to_rounded_corners (cairo_t                 *cr,
   cairo_clip (cr);
 }
 
-static void
-get_button_rect (MetaButtonFunction       function,
-                 const MetaFrameGeometry *fgeom,
-                 gint                     middle_background_offset,
-                 GdkRectangle            *rect)
+static MetaButtonFunction
+get_button_function (MetaButtonType type,
+                     gboolean       background,
+                     gint           button,
+                     gint           n_buttons,
+                     gint           side)
 {
-  switch (function)
+  if (background)
     {
-    case META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND:
-      *rect = fgeom->left_left_background;
-      break;
-
-    case META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND:
-      *rect = fgeom->left_middle_backgrounds[middle_background_offset];
-      break;
-
-    case META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND:
-      *rect = fgeom->left_right_background;
-      break;
-
-    case META_BUTTON_FUNCTION_LEFT_SINGLE_BACKGROUND:
-      *rect = fgeom->left_single_background;
-      break;
-
-    case META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND:
-      *rect = fgeom->right_left_background;
-      break;
-
-    case META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND:
-      *rect = fgeom->right_middle_backgrounds[middle_background_offset];
-      break;
-
-    case META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND:
-      *rect = fgeom->right_right_background;
-      break;
-
-    case META_BUTTON_FUNCTION_RIGHT_SINGLE_BACKGROUND:
-      *rect = fgeom->right_single_background;
-      break;
-
-    case META_BUTTON_FUNCTION_CLOSE:
-      *rect = fgeom->close_rect.visible;
-      break;
-
-    case META_BUTTON_FUNCTION_SHADE:
-      *rect = fgeom->shade_rect.visible;
-      break;
-
-    case META_BUTTON_FUNCTION_UNSHADE:
-      *rect = fgeom->unshade_rect.visible;
-      break;
-
-    case META_BUTTON_FUNCTION_ABOVE:
-      *rect = fgeom->above_rect.visible;
-      break;
+      if (side == 0) /* left */
+        {
+          if (n_buttons == 1)
+            {
+              return META_BUTTON_FUNCTION_LEFT_SINGLE_BACKGROUND;
+            }
+          else if (n_buttons == 2)
+            {
+              if (button == 0)
+                return META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND;
+              else
+                return META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND;
+            }
+          else if (n_buttons > 2)
+            {
+              if (button == 0)
+                return META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND;
+              else if (button == n_buttons - 1)
+                return META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND;
+              else
+                return META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND;
+            }
+        }
+      else if (side == 1) /* right */
+        {
+          if (n_buttons == 1)
+            {
+              return META_BUTTON_FUNCTION_RIGHT_SINGLE_BACKGROUND;
+            }
+          else if (n_buttons == 2)
+            {
+              if (button == 0)
+                return META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND;
+              else
+                return META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND;
+            }
+          else if (n_buttons > 2)
+            {
+              if (button == 0)
+                return META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND;
+              else if (button == n_buttons - 1)
+                return META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND;
+              else
+                return META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND;
+            }
+        }
+      else
+        {
+          g_assert_not_reached ();
+        }
+    }
+  else
+    {
+      switch (type)
+        {
+          case META_BUTTON_TYPE_SHADE:
+            return META_BUTTON_FUNCTION_SHADE;
 
-    case META_BUTTON_FUNCTION_UNABOVE:
-      *rect = fgeom->unabove_rect.visible;
-      break;
+          case META_BUTTON_TYPE_UNSHADE:
+            return META_BUTTON_FUNCTION_UNSHADE;
 
-    case META_BUTTON_FUNCTION_STICK:
-      *rect = fgeom->stick_rect.visible;
-      break;
+          case META_BUTTON_TYPE_ABOVE:
+            return META_BUTTON_FUNCTION_ABOVE;
 
-    case META_BUTTON_FUNCTION_UNSTICK:
-      *rect = fgeom->unstick_rect.visible;
-      break;
+          case META_BUTTON_TYPE_UNABOVE:
+            return META_BUTTON_FUNCTION_UNABOVE;
 
-    case META_BUTTON_FUNCTION_MAXIMIZE:
-      *rect = fgeom->max_rect.visible;
-      break;
+          case META_BUTTON_TYPE_STICK:
+            return META_BUTTON_FUNCTION_STICK;
 
-    case META_BUTTON_FUNCTION_MINIMIZE:
-      *rect = fgeom->min_rect.visible;
-      break;
+          case META_BUTTON_TYPE_UNSTICK:
+            return META_BUTTON_FUNCTION_UNSTICK;
 
-    case META_BUTTON_FUNCTION_MENU:
-      *rect = fgeom->menu_rect.visible;
-      break;
+          case META_BUTTON_TYPE_MENU:
+            return META_BUTTON_FUNCTION_MENU;
 
-    case META_BUTTON_FUNCTION_APPMENU:
-      *rect = fgeom->appmenu_rect.visible;
-      break;
+          case META_BUTTON_TYPE_APPMENU:
+            return META_BUTTON_FUNCTION_APPMENU;
 
-    case META_BUTTON_FUNCTION_LAST:
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-}
+          case META_BUTTON_TYPE_MINIMIZE:
+            return META_BUTTON_FUNCTION_MINIMIZE;
 
-static MetaButtonState
-map_button_state (MetaButtonFunction       button_function,
-                  const MetaFrameGeometry *fgeom,
-                  gint                     middle_bg_offset,
-                  const MetaButtonLayout  *button_layout)
-{
-  MetaButtonType type = META_BUTTON_TYPE_LAST;
+          case META_BUTTON_TYPE_MAXIMIZE:
+            return META_BUTTON_FUNCTION_MAXIMIZE;
 
-  switch (button_function)
-    {
-    /* First handle types, which map directly */
-    case META_BUTTON_FUNCTION_SHADE:
-      type = META_BUTTON_TYPE_SHADE;
-      break;
-    case META_BUTTON_FUNCTION_ABOVE:
-      type = META_BUTTON_TYPE_ABOVE;
-      break;
-    case META_BUTTON_FUNCTION_STICK:
-      type = META_BUTTON_TYPE_STICK;
-      break;
-    case META_BUTTON_FUNCTION_UNSHADE:
-      type = META_BUTTON_TYPE_UNSHADE;
-      break;
-    case META_BUTTON_FUNCTION_UNABOVE:
-      type = META_BUTTON_TYPE_UNABOVE;
-      break;;
-    case META_BUTTON_FUNCTION_UNSTICK:
-      type = META_BUTTON_TYPE_UNSTICK;
-      break;
-    case META_BUTTON_FUNCTION_MENU:
-      type = META_BUTTON_TYPE_MENU;
-      break;
-    case META_BUTTON_FUNCTION_APPMENU:
-      type = META_BUTTON_TYPE_APPMENU;
-      break;
-    case META_BUTTON_FUNCTION_MINIMIZE:
-      type = META_BUTTON_TYPE_MINIMIZE;
-      break;
-    case META_BUTTON_FUNCTION_MAXIMIZE:
-      type = META_BUTTON_TYPE_MAXIMIZE;
-      break;
-    case META_BUTTON_FUNCTION_CLOSE:
-      type = META_BUTTON_TYPE_CLOSE;
-      break;
+          case META_BUTTON_TYPE_CLOSE:
+            return META_BUTTON_FUNCTION_CLOSE;
 
-    /* Map position buttons to the corresponding type */
-    case META_BUTTON_FUNCTION_RIGHT_LEFT_BACKGROUND:
-    case META_BUTTON_FUNCTION_RIGHT_SINGLE_BACKGROUND:
-      if (button_layout->n_right_buttons > 0)
-        type = button_layout->right_buttons[0];
-      break;
-    case META_BUTTON_FUNCTION_RIGHT_RIGHT_BACKGROUND:
-      if (button_layout->n_right_buttons > 0)
-        type = button_layout->right_buttons[button_layout->n_right_buttons - 1];
-      break;
-    case META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND:
-      if (middle_bg_offset + 1 < button_layout->n_right_buttons)
-        type = button_layout->right_buttons[middle_bg_offset + 1];
-      break;
-    case META_BUTTON_FUNCTION_LEFT_LEFT_BACKGROUND:
-    case META_BUTTON_FUNCTION_LEFT_SINGLE_BACKGROUND:
-      if (button_layout->n_left_buttons > 0)
-        type = button_layout->left_buttons[0];
-      break;
-    case META_BUTTON_FUNCTION_LEFT_RIGHT_BACKGROUND:
-      if (button_layout->n_left_buttons > 0)
-        type = button_layout->left_buttons[button_layout->n_left_buttons - 1];
-      break;
-    case META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND:
-      if (middle_bg_offset + 1 < button_layout->n_left_buttons)
-        type = button_layout->left_buttons[middle_bg_offset + 1];
-      break;
-    case META_BUTTON_FUNCTION_LAST:
-      break;
-    default:
-      break;
+          case META_BUTTON_TYPE_SPACER:
+          case META_BUTTON_TYPE_LAST:
+          default:
+            break;
+        }
     }
 
-  if (type != META_BUTTON_TYPE_LAST)
-    return button_layout->button_states[type];
-
-  return META_BUTTON_STATE_LAST;
+  return META_BUTTON_FUNCTION_LAST;
 }
 
 static void
@@ -5474,7 +5277,7 @@ meta_theme_metacity_draw_frame (MetaThemeImpl           *impl,
                                 GdkPixbuf               *icon)
 {
   gdouble scale;
-  int i, j;
+  gint i;
   MetaRectangleDouble visible_rect;
   MetaRectangleDouble titlebar_rect;
   MetaRectangleDouble left_titlebar_edge;
@@ -5685,55 +5488,78 @@ meta_theme_metacity_draw_frame (MetaThemeImpl           *impl,
       /* Draw buttons just before overlay */
       if ((i + 1) == META_FRAME_PIECE_OVERLAY)
         {
-          MetaDrawOpList *op_list;
-          int middle_bg_offset;
+          gint side;
 
-          middle_bg_offset = 0;
-          j = 0;
-          while (j < META_BUTTON_FUNCTION_LAST)
+          for (side = 0; side < 2; side++)
             {
-              GdkRectangle tmp_rect;
-              MetaButtonState button_state;
+              MetaButton *buttons;
+              gint n_buttons;
+              gint j;
 
-              get_button_rect (j, fgeom, middle_bg_offset, &tmp_rect);
-
-              rect.x = tmp_rect.x / scale;
-              rect.y = tmp_rect.y / scale;
-              rect.width = tmp_rect.width / scale;
-              rect.height = tmp_rect.height / scale;
-
-              button_state = map_button_state (j, fgeom, middle_bg_offset,
-                                               button_layout);
-
-              op_list = meta_frame_style_get_button (style, j, button_state);
+              if (side == 0)
+                {
+                  buttons = button_layout->left_buttons;
+                  n_buttons = button_layout->n_left_buttons;
+                }
+              else if (side == 1)
+                {
+                  buttons = button_layout->right_buttons;
+                  n_buttons = button_layout->n_right_buttons;
+                }
+              else
+                {
+                  g_assert_not_reached ();
+                }
 
-              if (op_list)
+              for (j = 0; j < n_buttons; j++)
                 {
-                  cairo_save (cr);
+                  MetaButton *button;
+                  gint op;
 
-                  cairo_rectangle (cr, rect.x, rect.y, rect.width, rect.height);
-                  cairo_clip (cr);
+                  button = &buttons[j];
 
-                  if (gdk_cairo_get_clip_rectangle (cr, NULL))
+                  rect.x = button->rect.visible.x / scale;
+                  rect.y = button->rect.visible.y / scale;
+                  rect.width = button->rect.visible.width / scale;
+                  rect.height = button->rect.visible.height / scale;
+
+                  if (!button->visible ||
+                      button->type == META_BUTTON_TYPE_SPACER ||
+                      rect.width <= 0 || rect.height <= 0)
                     {
-                      meta_draw_op_list_draw_with_style (op_list, context, cr,
-                                                         &draw_info, rect);
+                      continue;
                     }
 
-                  cairo_restore (cr);
-                }
+                  for (op = 0; op < 2; op++)
+                    {
+                      MetaButtonFunction function;
+                      MetaDrawOpList *op_list;
 
-              /* MIDDLE_BACKGROUND type may get drawn more than once */
-              if ((j == META_BUTTON_FUNCTION_RIGHT_MIDDLE_BACKGROUND ||
-                   j == META_BUTTON_FUNCTION_LEFT_MIDDLE_BACKGROUND) &&
-                  (middle_bg_offset < (MAX_MIDDLE_BACKGROUNDS - 1)))
-                {
-                  ++middle_bg_offset;
-                }
-              else
-                {
-                  middle_bg_offset = 0;
-                  ++j;
+                      function = get_button_function (button->type, op == 0,
+                                                      j, n_buttons, op);
+
+                      op_list = meta_frame_style_get_button (style, function,
+                                                             button->state);
+
+                      if (op_list)
+                        {
+                          cairo_save (cr);
+
+                          cairo_rectangle (cr, rect.x, rect.y,
+                                           rect.width, rect.height);
+                          cairo_clip (cr);
+
+                          if (gdk_cairo_get_clip_rectangle (cr, NULL))
+                            {
+                              meta_draw_op_list_draw_with_style (op_list,
+                                                                 context, cr,
+                                                                 &draw_info,
+                                                                 rect);
+                            }
+
+                          cairo_restore (cr);
+                        }
+                    }
                 }
             }
         }
diff --git a/libmetacity/meta-theme.c b/libmetacity/meta-theme.c
index efabb3f..7084ef4 100644
--- a/libmetacity/meta-theme.c
+++ b/libmetacity/meta-theme.c
@@ -570,6 +570,63 @@ meta_theme_set_button_layout (MetaTheme   *theme,
   theme->button_layout = meta_button_layout_new (button_layout, invert);
 }
 
+gboolean
+meta_theme_get_button (MetaTheme  *theme,
+                       gint        x,
+                       gint        y,
+                       MetaButton *button)
+{
+  gint side;
+
+  for (side = 0; side < 2; side++)
+    {
+      MetaButton *buttons;
+      gint n_buttons;
+      gint i;
+
+      if (side == 0)
+        {
+          buttons = theme->button_layout->left_buttons;
+          n_buttons = theme->button_layout->n_left_buttons;
+        }
+      else if (side == 1)
+        {
+          buttons = theme->button_layout->right_buttons;
+          n_buttons = theme->button_layout->n_right_buttons;
+        }
+      else
+        {
+          g_assert_not_reached ();
+        }
+
+      for (i = 0; i < n_buttons; i++)
+        {
+          MetaButton *btn;
+          GdkRectangle rect;
+
+          btn = &buttons[i];
+          rect = btn->rect.visible;
+
+          if (!btn->visible || btn->type == META_BUTTON_TYPE_SPACER ||
+              rect.width <= 0 || rect.height <= 0)
+            {
+              continue;
+            }
+
+          rect = btn->rect.clickable;
+
+          if (x >= rect.x && x < (rect.x + rect.width) &&
+              y >= rect.y && y < (rect.y + rect.height))
+            {
+              *button = *btn;
+              return TRUE;
+            }
+        }
+    }
+
+  return FALSE;
+}
+
 void
 meta_theme_set_composited (MetaTheme *theme,
                            gboolean   composited)
@@ -694,20 +751,50 @@ meta_theme_draw_frame (MetaTheme           *theme,
                              title_height, flags, client_width, client_height,
                              theme->button_layout, type, &fgeom);
 
-  for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
+  for (i = 0; i < 2; i++)
     {
-      MetaButtonState state;
-      GdkRectangle rect;
+      MetaButton *buttons;
+      gint n_buttons;
+      gint j;
 
-      get_button_rect_for_type (i, &fgeom, &rect);
-
-      state = META_BUTTON_STATE_NORMAL;
-      if (func != NULL)
-        state = (* func) (i, rect, user_data);
-
-      g_assert (state >= META_BUTTON_STATE_NORMAL && state < META_BUTTON_STATE_LAST);
+      if (i == 0)
+        {
+          buttons = theme->button_layout->left_buttons;
+          n_buttons = theme->button_layout->n_left_buttons;
+        }
+      else if (i == 1)
+        {
+          buttons = theme->button_layout->right_buttons;
+          n_buttons = theme->button_layout->n_right_buttons;
+        }
+      else
+        {
+          g_assert_not_reached ();
+        }
 
-      theme->button_layout->button_states[i] = state;
+      for (j = 0; j < n_buttons; j++)
+        {
+          MetaButton *button;
+          MetaButtonState state;
+          GdkRectangle rect;
+
+          button = &buttons[j];
+          state = META_BUTTON_STATE_NORMAL;
+          rect = button->rect.visible;
+
+          if (!button->visible || button->type == META_BUTTON_TYPE_SPACER ||
+              rect.width <= 0 || rect.height <= 0)
+            {
+              button->state = state;
+              continue;
+            }
+
+          if (func != NULL)
+            state = (* func) (button->type, button->rect.clickable, user_data);
+
+          g_assert (state >= META_BUTTON_STATE_NORMAL && state < META_BUTTON_STATE_LAST);
+          button->state = state;
+        }
     }
 
   impl_class->draw_frame (theme->impl, style, style_info, cr, &fgeom,
diff --git a/libmetacity/meta-theme.h b/libmetacity/meta-theme.h
index 4ee1d7c..4174ffe 100644
--- a/libmetacity/meta-theme.h
+++ b/libmetacity/meta-theme.h
@@ -20,13 +20,12 @@
 #define META_THEME_H
 
 #include <gtk/gtk.h>
-#include <libmetacity/meta-button-enums.h>
+#include <libmetacity/meta-button.h>
 #include <libmetacity/meta-frame-borders.h>
 #include <libmetacity/meta-frame-enums.h>
 
 G_BEGIN_DECLS
 
-typedef struct _MetaButtonSpace MetaButtonSpace;
 typedef struct _MetaFrameGeometry MetaFrameGeometry;
 
 typedef MetaButtonState (* MetaButtonStateFunc) (MetaButtonType type,
@@ -82,20 +81,6 @@ typedef enum
 } MetaThemeType;
 
 /**
- * The computed size of a button (really just a way of tying its
- * visible and clickable areas together).
- * The reason for two different rectangles here is Fitts' law & maximized
- * windows; see bug #97703 for more details.
- */
-struct _MetaButtonSpace
-{
-  /** The screen area where the button's image is drawn */
-  GdkRectangle visible;
-  /** The screen area where the button can be activated by clicking */
-  GdkRectangle clickable;
-};
-
-/**
  * Calculated actual geometry of the frame
  */
 struct _MetaFrameGeometry
@@ -107,34 +92,6 @@ struct _MetaFrameGeometry
 
   GdkRectangle title_rect;
 
-  /* used for a memset hack */
-#define ADDRESS_OF_BUTTON_RECTS(fgeom) (((char*)(fgeom)) + G_STRUCT_OFFSET (MetaFrameGeometry, close_rect))
-#define LENGTH_OF_BUTTON_RECTS (G_STRUCT_OFFSET (MetaFrameGeometry, right_single_background) + sizeof 
(GdkRectangle) - G_STRUCT_OFFSET (MetaFrameGeometry, close_rect))
-
-  /* The button rects (if changed adjust memset hack) */
-  MetaButtonSpace close_rect;
-  MetaButtonSpace max_rect;
-  MetaButtonSpace min_rect;
-  MetaButtonSpace menu_rect;
-  MetaButtonSpace appmenu_rect;
-  MetaButtonSpace shade_rect;
-  MetaButtonSpace above_rect;
-  MetaButtonSpace stick_rect;
-  MetaButtonSpace unshade_rect;
-  MetaButtonSpace unabove_rect;
-  MetaButtonSpace unstick_rect;
-
-#define MAX_MIDDLE_BACKGROUNDS (META_BUTTON_TYPE_LAST - 2)
-  GdkRectangle left_left_background;
-  GdkRectangle left_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS];
-  GdkRectangle left_right_background;
-  GdkRectangle left_single_background;
-  GdkRectangle right_left_background;
-  GdkRectangle right_middle_backgrounds[MAX_MIDDLE_BACKGROUNDS];
-  GdkRectangle right_right_background;
-  GdkRectangle right_single_background;
-  /* End of button rects (if changed adjust memset hack) */
-
   /* Round corners */
   guint top_left_corner_rounded_radius;
   guint top_right_corner_rounded_radius;
@@ -156,6 +113,11 @@ void           meta_theme_set_button_layout (MetaTheme                   *theme,
                                              const gchar                 *button_layout,
                                              gboolean                     invert);
 
+gboolean       meta_theme_get_button        (MetaTheme                   *theme,
+                                             gint                         x,
+                                             gint                         y,
+                                             MetaButton                  *button);
+
 void           meta_theme_set_composited    (MetaTheme                   *theme,
                                              gboolean                     composited);
 
diff --git a/src/ui/frames.c b/src/ui/frames.c
index e639abb..7c25b2c 100644
--- a/src/ui/frames.c
+++ b/src/ui/frames.c
@@ -48,9 +48,6 @@ static void meta_frames_destroy       (GtkWidget *widget);
 static void meta_frames_finalize      (GObject   *object);
 static void meta_frames_style_updated (GtkWidget *widget);
 
-static void meta_frames_update_prelit_control (MetaFrames      *frames,
-                                               MetaUIFrame     *frame,
-                                               MetaFrameControl control);
 static gboolean meta_frames_button_press_event    (GtkWidget           *widget,
                                                    GdkEventButton      *event);
 static gboolean meta_frames_button_release_event  (GtkWidget           *widget,
@@ -81,13 +78,6 @@ static MetaUIFrame* meta_frames_lookup_window (MetaFrames *frames,
 static void meta_frames_font_changed          (MetaFrames *frames);
 static void meta_frames_button_layout_changed (MetaFrames *frames);
 
-
-static GdkRectangle*    control_rect (MetaFrameControl   control,
-                                      MetaFrameGeometry *fgeom);
-static MetaFrameControl get_control  (MetaFrames        *frames,
-                                      MetaUIFrame       *frame,
-                                      int                x,
-                                      int                y);
 static void clear_tip (MetaFrames *frames);
 static void invalidate_all_caches (MetaFrames *frames);
 static void invalidate_whole_window (MetaFrames *frames,
@@ -114,6 +104,277 @@ struct _MetaFrames
 G_DEFINE_TYPE (MetaFrames, meta_frames, GTK_TYPE_WINDOW)
 
 static void
+get_client_rect (MetaFrameGeometry *fgeom,
+                 GdkRectangle      *rect)
+{
+  rect->x = fgeom->borders.total.left;
+  rect->y = fgeom->borders.total.top;
+  rect->width = fgeom->width - fgeom->borders.total.right - rect->x;
+  rect->height = fgeom->height - fgeom->borders.total.bottom - rect->y;
+}
+
+#define RESIZE_EXTENDS 15
+#define TOP_RESIZE_HEIGHT 4
+static MetaFrameControl
+get_control (MetaFrames  *frames,
+             MetaUIFrame *frame,
+             gint         x,
+             gint         y)
+{
+  MetaFrameGeometry fgeom;
+  MetaFrameFlags flags;
+  MetaFrameType type;
+  gboolean has_vert, has_horiz;
+  gboolean has_north_resize;
+  GdkRectangle client;
+  MetaFrameBorders borders;
+  MetaTheme *theme;
+  MetaButton button;
+
+  meta_frames_calc_geometry (frames, frame, &fgeom);
+  get_client_rect (&fgeom, &client);
+
+  borders = fgeom.borders;
+  theme = meta_ui_get_theme ();
+
+  if (x < borders.invisible.left - borders.resize.left ||
+      y < borders.invisible.top - borders.resize.top ||
+      x > fgeom.width - borders.invisible.right + borders.resize.right ||
+      y > fgeom.height - borders.invisible.bottom + borders.resize.bottom)
+    return META_FRAME_CONTROL_NONE;
+
+  if (POINT_IN_RECT (x, y, client))
+    return META_FRAME_CONTROL_CLIENT_AREA;
+
+  meta_core_get (frames->xdisplay, frame->xwindow,
+                 META_CORE_GET_FRAME_FLAGS, &flags,
+                 META_CORE_GET_FRAME_TYPE, &type,
+                 META_CORE_GET_END);
+
+  if (meta_theme_get_button (theme, x, y, &button))
+    {
+      switch (button.type)
+        {
+          case META_BUTTON_TYPE_CLOSE:
+            return META_FRAME_CONTROL_DELETE;
+
+          case META_BUTTON_TYPE_MINIMIZE:
+            return META_FRAME_CONTROL_MINIMIZE;
+
+          case META_BUTTON_TYPE_MENU:
+            return META_FRAME_CONTROL_MENU;
+
+          case META_BUTTON_TYPE_APPMENU:
+            return META_FRAME_CONTROL_APPMENU;
+
+          case META_BUTTON_TYPE_MAXIMIZE:
+            if (flags & META_FRAME_MAXIMIZED)
+              return META_FRAME_CONTROL_UNMAXIMIZE;
+            else
+              return META_FRAME_CONTROL_MAXIMIZE;
+
+          case META_BUTTON_TYPE_SHADE:
+            return META_FRAME_CONTROL_SHADE;
+
+          case META_BUTTON_TYPE_UNSHADE:
+            return META_FRAME_CONTROL_UNSHADE;
+
+          case META_BUTTON_TYPE_ABOVE:
+            return META_FRAME_CONTROL_ABOVE;
+
+          case META_BUTTON_TYPE_UNABOVE:
+            return META_FRAME_CONTROL_UNABOVE;
+
+          case META_BUTTON_TYPE_STICK:
+            return META_FRAME_CONTROL_STICK;
+
+          case META_BUTTON_TYPE_UNSTICK:
+            return META_FRAME_CONTROL_UNSTICK;
+
+          case META_BUTTON_TYPE_SPACER:
+          case META_BUTTON_TYPE_LAST:
+          default:
+            break;
+        }
+    }
+
+  has_north_resize = (type != META_FRAME_TYPE_ATTACHED);
+  has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0;
+  has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0;
+
+  if (POINT_IN_RECT (x, y, fgeom.title_rect))
+    {
+      if (has_vert && y <= (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT) && has_north_resize)
+        return META_FRAME_CONTROL_RESIZE_N;
+      else
+        return META_FRAME_CONTROL_TITLE;
+    }
+
+  /* South resize always has priority over north resize,
+   * in case of overlap.
+   */
+
+  if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) &&
+      x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS))
+    {
+      if (has_vert && has_horiz)
+        return META_FRAME_CONTROL_RESIZE_SE;
+      else if (has_vert)
+        return META_FRAME_CONTROL_RESIZE_S;
+      else if (has_horiz)
+        return META_FRAME_CONTROL_RESIZE_E;
+    }
+  else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) &&
+           x <= (fgeom.borders.total.left + RESIZE_EXTENDS))
+    {
+      if (has_vert && has_horiz)
+        return META_FRAME_CONTROL_RESIZE_SW;
+      else if (has_vert)
+        return META_FRAME_CONTROL_RESIZE_S;
+      else if (has_horiz)
+        return META_FRAME_CONTROL_RESIZE_W;
+    }
+  else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) &&
+           x <= (fgeom.borders.total.left + RESIZE_EXTENDS) && has_north_resize)
+    {
+      if (has_vert && has_horiz)
+        return META_FRAME_CONTROL_RESIZE_NW;
+      else if (has_vert)
+        return META_FRAME_CONTROL_RESIZE_N;
+      else if (has_horiz)
+        return META_FRAME_CONTROL_RESIZE_W;
+    }
+  else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) &&
+           x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS) && has_north_resize)
+    {
+      if (has_vert && has_horiz)
+        return META_FRAME_CONTROL_RESIZE_NE;
+      else if (has_vert)
+        return META_FRAME_CONTROL_RESIZE_N;
+      else if (has_horiz)
+        return META_FRAME_CONTROL_RESIZE_E;
+    }
+  else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT))
+    {
+      if (has_vert && has_north_resize)
+        return META_FRAME_CONTROL_RESIZE_N;
+    }
+  else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS))
+    {
+      if (has_vert)
+        return META_FRAME_CONTROL_RESIZE_S;
+    }
+  else if (x <= fgeom.borders.total.left + RESIZE_EXTENDS)
+    {
+      if (has_horiz)
+        return META_FRAME_CONTROL_RESIZE_W;
+    }
+  else if (x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS))
+    {
+      if (has_horiz)
+        return META_FRAME_CONTROL_RESIZE_E;
+    }
+
+  if (y >= fgeom.borders.total.top)
+    return META_FRAME_CONTROL_NONE;
+  else
+    return META_FRAME_CONTROL_TITLE;
+}
+
+static gboolean
+get_control_rect (MetaFrameControl   control,
+                  MetaFrameGeometry *fgeom,
+                  gint               x,
+                  gint               y,
+                  GdkRectangle      *rect)
+{
+  MetaButtonType type;
+  MetaTheme *theme;
+  MetaButton button;
+
+  type = META_BUTTON_TYPE_LAST;
+
+  switch (control)
+    {
+      case META_FRAME_CONTROL_TITLE:
+        *rect = fgeom->title_rect;
+        return TRUE;
+
+      case META_FRAME_CONTROL_DELETE:
+        type = META_BUTTON_TYPE_CLOSE;
+        break;
+
+      case META_FRAME_CONTROL_MENU:
+        type = META_BUTTON_TYPE_MENU;
+        break;
+
+      case META_FRAME_CONTROL_APPMENU:
+        type = META_BUTTON_TYPE_APPMENU;
+        break;
+
+      case META_FRAME_CONTROL_MINIMIZE:
+        type = META_BUTTON_TYPE_MINIMIZE;
+        break;
+
+      case META_FRAME_CONTROL_MAXIMIZE:
+      case META_FRAME_CONTROL_UNMAXIMIZE:
+        type = META_BUTTON_TYPE_MAXIMIZE;
+        break;
+
+      case META_FRAME_CONTROL_SHADE:
+        type = META_BUTTON_TYPE_SHADE;
+        break;
+
+      case META_FRAME_CONTROL_UNSHADE:
+        type = META_BUTTON_TYPE_UNSHADE;
+        break;
+
+      case META_FRAME_CONTROL_ABOVE:
+        type = META_BUTTON_TYPE_ABOVE;
+        break;
+
+      case META_FRAME_CONTROL_UNABOVE:
+        type = META_BUTTON_TYPE_UNABOVE;
+        break;
+
+      case META_FRAME_CONTROL_STICK:
+        type = META_BUTTON_TYPE_STICK;
+        break;
+
+      case META_FRAME_CONTROL_UNSTICK:
+        type = META_BUTTON_TYPE_UNSTICK;
+        break;
+
+      case META_FRAME_CONTROL_CLIENT_AREA:
+      case META_FRAME_CONTROL_RESIZE_SE:
+      case META_FRAME_CONTROL_RESIZE_S:
+      case META_FRAME_CONTROL_RESIZE_SW:
+      case META_FRAME_CONTROL_RESIZE_N:
+      case META_FRAME_CONTROL_RESIZE_NE:
+      case META_FRAME_CONTROL_RESIZE_NW:
+      case META_FRAME_CONTROL_RESIZE_W:
+      case META_FRAME_CONTROL_RESIZE_E:
+      case META_FRAME_CONTROL_NONE:
+      default:
+        break;
+    }
+
+  theme = meta_ui_get_theme ();
+
+  if (type != META_BUTTON_TYPE_LAST &&
+      meta_theme_get_button (theme, x, y, &button))
+    {
+      if (type == button.type)
+        {
+          *rect = button.rect.clickable;
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
 meta_frames_class_init (MetaFramesClass *frames_class)
 {
   GObjectClass *object_class;
@@ -514,6 +775,8 @@ meta_frames_manage_window (MetaFrames *frames,
   frame->expose_delayed = FALSE;
   frame->shape_applied = FALSE;
   frame->prelit_control = META_FRAME_CONTROL_NONE;
+  frame->prelit_x = 0;
+  frame->prelit_y = 0;
 
   meta_core_grab_buttons (frames->xdisplay, frame->xwindow);
 
@@ -642,21 +905,6 @@ apply_cairo_region_to_window (Display        *display,
   g_free (rects);
 }
 
-/* The client rectangle surrounds client window; it subtracts both
- * the visible and invisible borders from the frame window's size.
- */
-static void
-get_client_rect (MetaFrameGeometry     *fgeom,
-                 int                    window_width,
-                 int                    window_height,
-                 cairo_rectangle_int_t *rect)
-{
-  rect->x = fgeom->borders.total.left;
-  rect->y = fgeom->borders.total.top;
-  rect->width = window_width - fgeom->borders.total.right - rect->x;
-  rect->height = window_height - fgeom->borders.total.bottom - rect->y;
-}
-
 /* The visible frame rectangle surrounds the visible portion of the
  * frame window; it subtracts only the invisible borders from the frame
  * window's size.
@@ -1120,12 +1368,13 @@ show_tip_now (MetaFrames *frames)
   if (tiptext)
     {
       MetaFrameGeometry fgeom;
-      GdkRectangle *rect;
+      GdkRectangle rect;
       int dx, dy;
 
       meta_frames_calc_geometry (frames, frame, &fgeom);
 
-      rect = control_rect (control, &fgeom);
+      if (!get_control_rect (control, &fgeom, x, y, &rect))
+        return;
 
       /* get conversion delta for root-to-frame coords */
       dx = root_x - x;
@@ -1133,10 +1382,10 @@ show_tip_now (MetaFrames *frames)
 
       /* Align the tooltip to the button right end if RTL */
       if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
-        dx += rect->width;
+        dx += rect.width;
 
-      meta_fixed_tip_show (rect->x + dx,
-                           rect->y + rect->height + 2 + dy,
+      meta_fixed_tip_show (rect.x + dx,
+                           rect.y + rect.height + 2 + dy,
                            tiptext);
     }
 }
@@ -1178,21 +1427,146 @@ clear_tip (MetaFrames *frames)
 }
 
 static void
-redraw_control (MetaFrames *frames,
-                MetaUIFrame *frame,
-                MetaFrameControl control)
+redraw_control (MetaFrames       *frames,
+                MetaUIFrame      *frame,
+                MetaFrameControl  control,
+                gint              x,
+                gint              y)
 {
   MetaFrameGeometry fgeom;
-  GdkRectangle *rect;
+  GdkRectangle rect;
 
   meta_frames_calc_geometry (frames, frame, &fgeom);
 
-  rect = control_rect (control, &fgeom);
+  if (!get_control_rect (control, &fgeom, x, y, &rect))
+    return;
 
-  gdk_window_invalidate_rect (frame->window, rect, FALSE);
+  gdk_window_invalidate_rect (frame->window, &rect, FALSE);
   invalidate_cache (frames, frame);
 }
 
+static void
+update_prelit_control (MetaFrames       *frames,
+                       MetaUIFrame      *frame,
+                       MetaFrameControl  control,
+                       gint              x,
+                       gint              y)
+{
+  MetaCursor cursor;
+  MetaFrameControl old_control;
+  gint old_x;
+  gint old_y;
+
+  meta_verbose ("Updating prelit control from %u to %u\n",
+                frame->prelit_control, control);
+
+  switch (control)
+    {
+      case META_FRAME_CONTROL_RESIZE_SE:
+        cursor = META_CURSOR_SE_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_S:
+        cursor = META_CURSOR_SOUTH_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_SW:
+        cursor = META_CURSOR_SW_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_N:
+        cursor = META_CURSOR_NORTH_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_NE:
+        cursor = META_CURSOR_NE_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_NW:
+        cursor = META_CURSOR_NW_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_W:
+        cursor = META_CURSOR_WEST_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_RESIZE_E:
+        cursor = META_CURSOR_EAST_RESIZE;
+        break;
+
+      case META_FRAME_CONTROL_CLIENT_AREA:
+      case META_FRAME_CONTROL_NONE:
+      case META_FRAME_CONTROL_TITLE:
+      case META_FRAME_CONTROL_DELETE:
+      case META_FRAME_CONTROL_MENU:
+      case META_FRAME_CONTROL_APPMENU:
+      case META_FRAME_CONTROL_MINIMIZE:
+      case META_FRAME_CONTROL_MAXIMIZE:
+      case META_FRAME_CONTROL_UNMAXIMIZE:
+      case META_FRAME_CONTROL_SHADE:
+      case META_FRAME_CONTROL_UNSHADE:
+      case META_FRAME_CONTROL_ABOVE:
+      case META_FRAME_CONTROL_UNABOVE:
+      case META_FRAME_CONTROL_STICK:
+      case META_FRAME_CONTROL_UNSTICK:
+      default:
+        cursor = META_CURSOR_DEFAULT;
+        break;
+    }
+
+  /* set/unset the prelight cursor */
+  meta_core_set_screen_cursor (frames->xdisplay, frame->xwindow, cursor);
+
+  switch (control)
+    {
+      case META_FRAME_CONTROL_MENU:
+      case META_FRAME_CONTROL_APPMENU:
+      case META_FRAME_CONTROL_MINIMIZE:
+      case META_FRAME_CONTROL_MAXIMIZE:
+      case META_FRAME_CONTROL_DELETE:
+      case META_FRAME_CONTROL_SHADE:
+      case META_FRAME_CONTROL_UNSHADE:
+      case META_FRAME_CONTROL_ABOVE:
+      case META_FRAME_CONTROL_UNABOVE:
+      case META_FRAME_CONTROL_STICK:
+      case META_FRAME_CONTROL_UNSTICK:
+      case META_FRAME_CONTROL_UNMAXIMIZE:
+        /* leave control set */
+        break;
+
+      case META_FRAME_CONTROL_NONE:
+      case META_FRAME_CONTROL_TITLE:
+      case META_FRAME_CONTROL_RESIZE_SE:
+      case META_FRAME_CONTROL_RESIZE_S:
+      case META_FRAME_CONTROL_RESIZE_SW:
+      case META_FRAME_CONTROL_RESIZE_N:
+      case META_FRAME_CONTROL_RESIZE_NE:
+      case META_FRAME_CONTROL_RESIZE_NW:
+      case META_FRAME_CONTROL_RESIZE_W:
+      case META_FRAME_CONTROL_RESIZE_E:
+      case META_FRAME_CONTROL_CLIENT_AREA:
+      default:
+        /* Only prelight buttons */
+        control = META_FRAME_CONTROL_NONE;
+        break;
+    }
+
+  if (control == frame->prelit_control)
+    return;
+
+  /* Save the old control so we can unprelight it */
+  old_control = frame->prelit_control;
+  old_x = frame->prelit_x;
+  old_y = frame->prelit_y;
+
+  frame->prelit_control = control;
+  frame->prelit_x = x;
+  frame->prelit_y = y;
+
+  redraw_control (frames, frame, old_control, old_x, old_y);
+  redraw_control (frames, frame, control, x, y);
+}
+
 static gboolean
 meta_frame_titlebar_event (MetaFrames     *frames,
                            MetaUIFrame    *frame,
@@ -1428,17 +1802,24 @@ meta_frames_button_press_event (GtkWidget      *widget,
                                event->y_root);
 
       frame->prelit_control = control;
-      redraw_control (frames, frame, control);
+      frame->prelit_x = event->x;
+      frame->prelit_y = event->y;
+
+      redraw_control (frames, frame, control, event->x, event->y);
 
       if (op == META_GRAB_OP_CLICKING_MENU)
         {
           MetaFrameGeometry fgeom;
-          GdkRectangle *rect;
+          GdkRectangle rect;
           int dx, dy;
 
           meta_frames_calc_geometry (frames, frame, &fgeom);
 
-          rect = control_rect (META_FRAME_CONTROL_MENU, &fgeom);
+          if (!get_control_rect (META_FRAME_CONTROL_MENU, &fgeom,
+                                 event->x, event->y, &rect))
+            {
+              return FALSE;
+            }
 
           /* get delta to convert to root coords */
           dx = event->x_root - event->x;
@@ -1446,12 +1827,12 @@ meta_frames_button_press_event (GtkWidget      *widget,
 
           /* Align to the right end of the menu rectangle if RTL */
           if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
-            dx += rect->width;
+            dx += rect.width;
 
           meta_core_show_window_menu (frames->xdisplay,
                                       frame->xwindow,
-                                      rect->x + dx,
-                                      rect->y + rect->height + dy,
+                                      rect.x + dx,
+                                      rect.y + rect.height + dy,
                                       event->button,
                                       event->time);
         }
@@ -1552,7 +1933,9 @@ meta_frames_notify_menu_hide (MetaFrames *frames)
 
           if (frame)
             {
-              redraw_control (frames, frame, META_FRAME_CONTROL_MENU);
+              redraw_control (frames, frame, META_FRAME_CONTROL_MENU,
+                              frame->prelit_x, frame->prelit_y);
+
               meta_core_end_grab_op (frames->xdisplay, CurrentTime);
             }
         }
@@ -1709,137 +2092,12 @@ meta_frames_button_release_event    (GtkWidget           *widget,
        * prelit so to let the user know that it can now be pressed.
        * :)
        */
-      meta_frames_update_prelit_control (frames, frame, control);
+      update_prelit_control (frames, frame, control, event->x, event->y);
     }
 
   return TRUE;
 }
 
-static void
-meta_frames_update_prelit_control (MetaFrames      *frames,
-                                   MetaUIFrame     *frame,
-                                   MetaFrameControl control)
-{
-  MetaFrameControl old_control;
-  MetaCursor cursor;
-
-
-  meta_verbose ("Updating prelit control from %u to %u\n",
-                frame->prelit_control, control);
-
-  cursor = META_CURSOR_DEFAULT;
-
-  switch (control)
-    {
-    case META_FRAME_CONTROL_CLIENT_AREA:
-      break;
-    case META_FRAME_CONTROL_NONE:
-      break;
-    case META_FRAME_CONTROL_TITLE:
-      break;
-    case META_FRAME_CONTROL_DELETE:
-      break;
-    case META_FRAME_CONTROL_MENU:
-      break;
-    case META_FRAME_CONTROL_APPMENU:
-      break;
-    case META_FRAME_CONTROL_MINIMIZE:
-      break;
-    case META_FRAME_CONTROL_MAXIMIZE:
-      break;
-    case META_FRAME_CONTROL_UNMAXIMIZE:
-      break;
-    case META_FRAME_CONTROL_SHADE:
-      break;
-    case META_FRAME_CONTROL_UNSHADE:
-      break;
-    case META_FRAME_CONTROL_ABOVE:
-      break;
-    case META_FRAME_CONTROL_UNABOVE:
-      break;
-    case META_FRAME_CONTROL_STICK:
-      break;
-    case META_FRAME_CONTROL_UNSTICK:
-      break;
-    case META_FRAME_CONTROL_RESIZE_SE:
-      cursor = META_CURSOR_SE_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_S:
-      cursor = META_CURSOR_SOUTH_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_SW:
-      cursor = META_CURSOR_SW_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_N:
-      cursor = META_CURSOR_NORTH_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_NE:
-      cursor = META_CURSOR_NE_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_NW:
-      cursor = META_CURSOR_NW_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_W:
-      cursor = META_CURSOR_WEST_RESIZE;
-      break;
-    case META_FRAME_CONTROL_RESIZE_E:
-      cursor = META_CURSOR_EAST_RESIZE;
-      break;
-    default:
-      break;
-    }
-
-  /* set/unset the prelight cursor */
-  meta_core_set_screen_cursor (frames->xdisplay, frame->xwindow, cursor);
-
-  switch (control)
-    {
-    case META_FRAME_CONTROL_MENU:
-    case META_FRAME_CONTROL_APPMENU:
-    case META_FRAME_CONTROL_MINIMIZE:
-    case META_FRAME_CONTROL_MAXIMIZE:
-    case META_FRAME_CONTROL_DELETE:
-    case META_FRAME_CONTROL_SHADE:
-    case META_FRAME_CONTROL_UNSHADE:
-    case META_FRAME_CONTROL_ABOVE:
-    case META_FRAME_CONTROL_UNABOVE:
-    case META_FRAME_CONTROL_STICK:
-    case META_FRAME_CONTROL_UNSTICK:
-    case META_FRAME_CONTROL_UNMAXIMIZE:
-      /* leave control set */
-      break;
-    case META_FRAME_CONTROL_NONE:
-    case META_FRAME_CONTROL_TITLE:
-    case META_FRAME_CONTROL_RESIZE_SE:
-    case META_FRAME_CONTROL_RESIZE_S:
-    case META_FRAME_CONTROL_RESIZE_SW:
-    case META_FRAME_CONTROL_RESIZE_N:
-    case META_FRAME_CONTROL_RESIZE_NE:
-    case META_FRAME_CONTROL_RESIZE_NW:
-    case META_FRAME_CONTROL_RESIZE_W:
-    case META_FRAME_CONTROL_RESIZE_E:
-    case META_FRAME_CONTROL_CLIENT_AREA:
-      /* Only prelight buttons */
-      control = META_FRAME_CONTROL_NONE;
-      break;
-    default:
-      /* Only prelight buttons */
-      control = META_FRAME_CONTROL_NONE;
-      break;
-    }
-
-  if (control == frame->prelit_control)
-    return;
-
-  /* Save the old control so we can unprelight it */
-  old_control = frame->prelit_control;
-
-  frame->prelit_control = control;
-
-  redraw_control (frames, frame, old_control);
-  redraw_control (frames, frame, control);
-}
-
 static gboolean
 meta_frames_motion_notify_event     (GtkWidget           *widget,
                                      GdkEventMotion      *event)
@@ -1912,7 +2170,7 @@ meta_frames_motion_notify_event     (GtkWidget           *widget,
            control = META_FRAME_CONTROL_NONE;
 
         /* Update prelit control and cursor */
-        meta_frames_update_prelit_control (frames, frame, control);
+        update_prelit_control (frames, frame, control, x, y);
 
         /* No tooltip while in the process of clicking */
       }
@@ -1928,7 +2186,7 @@ meta_frames_motion_notify_event     (GtkWidget           *widget,
         control = get_control (frames, frame, x, y);
 
         /* Update prelit control and cursor */
-        meta_frames_update_prelit_control (frames, frame, control);
+        update_prelit_control (frames, frame, control, x, y);
 
         queue_tip (frames);
       }
@@ -2284,6 +2542,11 @@ update_button_state (MetaButtonType type,
   Window grab_frame;
   MetaGrabOp grab_op;
   MetaFrameControl control;
+  GdkDisplay *display;
+  GdkSeat *seat;
+  GdkDevice *device;
+  gint x;
+  gint y;
 
   data = (ButtonStateData *) user_data;
 
@@ -2296,6 +2559,15 @@ update_button_state (MetaButtonType type,
 
   control = data->frame->prelit_control;
 
+  display = gdk_display_get_default ();
+  seat = gdk_display_get_default_seat (display);
+  device = gdk_seat_get_pointer (seat);
+
+  gdk_window_get_device_position (data->frame->window, device, &x, &y, NULL);
+
+  if (!POINT_IN_RECT (x, y, rect))
+    return state;
+
   /* Set prelight state */
   if (control == META_FRAME_CONTROL_MENU &&
       type == META_BUTTON_TYPE_MENU)
@@ -2441,7 +2713,7 @@ meta_frames_enter_notify_event      (GtkWidget           *widget,
     return FALSE;
 
   control = get_control (frames, frame, event->x, event->y);
-  meta_frames_update_prelit_control (frames, frame, control);
+  update_prelit_control (frames, frame, control, event->x, event->y);
 
   return TRUE;
 }
@@ -2459,253 +2731,14 @@ meta_frames_leave_notify_event      (GtkWidget           *widget,
   if (frame == NULL)
     return FALSE;
 
-  meta_frames_update_prelit_control (frames, frame, META_FRAME_CONTROL_NONE);
+  update_prelit_control (frames, frame, META_FRAME_CONTROL_NONE,
+                         event->x, event->y);
 
   clear_tip (frames);
 
   return TRUE;
 }
 
-static GdkRectangle*
-control_rect (MetaFrameControl control,
-              MetaFrameGeometry *fgeom)
-{
-  GdkRectangle *rect;
-
-  rect = NULL;
-  switch (control)
-    {
-    case META_FRAME_CONTROL_TITLE:
-      rect = &fgeom->title_rect;
-      break;
-    case META_FRAME_CONTROL_DELETE:
-      rect = &fgeom->close_rect.visible;
-      break;
-    case META_FRAME_CONTROL_MENU:
-      rect = &fgeom->menu_rect.visible;
-      break;
-    case META_FRAME_CONTROL_APPMENU:
-      rect = &fgeom->appmenu_rect.visible;
-      break;
-    case META_FRAME_CONTROL_MINIMIZE:
-      rect = &fgeom->min_rect.visible;
-      break;
-    case META_FRAME_CONTROL_MAXIMIZE:
-    case META_FRAME_CONTROL_UNMAXIMIZE:
-      rect = &fgeom->max_rect.visible;
-      break;
-    case META_FRAME_CONTROL_SHADE:
-      rect = &fgeom->shade_rect.visible;
-      break;
-    case META_FRAME_CONTROL_UNSHADE:
-      rect = &fgeom->unshade_rect.visible;
-      break;
-    case META_FRAME_CONTROL_ABOVE:
-      rect = &fgeom->above_rect.visible;
-      break;
-    case META_FRAME_CONTROL_UNABOVE:
-      rect = &fgeom->unabove_rect.visible;
-      break;
-    case META_FRAME_CONTROL_STICK:
-      rect = &fgeom->stick_rect.visible;
-      break;
-    case META_FRAME_CONTROL_UNSTICK:
-      rect = &fgeom->unstick_rect.visible;
-      break;
-    case META_FRAME_CONTROL_RESIZE_SE:
-      break;
-    case META_FRAME_CONTROL_RESIZE_S:
-      break;
-    case META_FRAME_CONTROL_RESIZE_SW:
-      break;
-    case META_FRAME_CONTROL_RESIZE_N:
-      break;
-    case META_FRAME_CONTROL_RESIZE_NE:
-      break;
-    case META_FRAME_CONTROL_RESIZE_NW:
-      break;
-    case META_FRAME_CONTROL_RESIZE_W:
-      break;
-    case META_FRAME_CONTROL_RESIZE_E:
-      break;
-    case META_FRAME_CONTROL_NONE:
-      break;
-    case META_FRAME_CONTROL_CLIENT_AREA:
-      break;
-    default:
-      break;
-    }
-
-  return rect;
-}
-
-#define RESIZE_EXTENDS 15
-#define TOP_RESIZE_HEIGHT 4
-static MetaFrameControl
-get_control (MetaFrames *frames,
-             MetaUIFrame *frame,
-             int x, int y)
-{
-  MetaFrameGeometry fgeom;
-  MetaFrameFlags flags;
-  MetaFrameType type;
-  gboolean has_vert, has_horiz;
-  gboolean has_north_resize;
-  GdkRectangle client;
-  MetaFrameBorders borders;
-
-  meta_frames_calc_geometry (frames, frame, &fgeom);
-  get_client_rect (&fgeom, fgeom.width, fgeom.height, &client);
-
-  borders = fgeom.borders;
-
-  if (x < borders.invisible.left - borders.resize.left ||
-      y < borders.invisible.top - borders.resize.top ||
-      x > fgeom.width - borders.invisible.right + borders.resize.right ||
-      y > fgeom.height - borders.invisible.bottom + borders.resize.bottom)
-    return META_FRAME_CONTROL_NONE;
-
-  if (POINT_IN_RECT (x, y, client))
-    return META_FRAME_CONTROL_CLIENT_AREA;
-
-  if (POINT_IN_RECT (x, y, fgeom.close_rect.clickable))
-    return META_FRAME_CONTROL_DELETE;
-
-  if (POINT_IN_RECT (x, y, fgeom.min_rect.clickable))
-    return META_FRAME_CONTROL_MINIMIZE;
-
-  if (POINT_IN_RECT (x, y, fgeom.menu_rect.clickable))
-    return META_FRAME_CONTROL_MENU;
-
-  if (POINT_IN_RECT (x, y, fgeom.appmenu_rect.clickable))
-    return META_FRAME_CONTROL_APPMENU;
-
-  meta_core_get (frames->xdisplay, frame->xwindow,
-                 META_CORE_GET_FRAME_FLAGS, &flags,
-                 META_CORE_GET_FRAME_TYPE, &type,
-                 META_CORE_GET_END);
-
-  has_north_resize = (type != META_FRAME_TYPE_ATTACHED);
-  has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0;
-  has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0;
-
-  if (POINT_IN_RECT (x, y, fgeom.title_rect))
-    {
-      if (has_vert && y <= (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT) && has_north_resize)
-        return META_FRAME_CONTROL_RESIZE_N;
-      else
-        return META_FRAME_CONTROL_TITLE;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.max_rect.clickable))
-    {
-      if (flags & META_FRAME_MAXIMIZED)
-        return META_FRAME_CONTROL_UNMAXIMIZE;
-      else
-        return META_FRAME_CONTROL_MAXIMIZE;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.shade_rect.clickable))
-    {
-      return META_FRAME_CONTROL_SHADE;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.unshade_rect.clickable))
-    {
-      return META_FRAME_CONTROL_UNSHADE;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.above_rect.clickable))
-    {
-      return META_FRAME_CONTROL_ABOVE;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.unabove_rect.clickable))
-    {
-      return META_FRAME_CONTROL_UNABOVE;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.stick_rect.clickable))
-    {
-      return META_FRAME_CONTROL_STICK;
-    }
-
-  if (POINT_IN_RECT (x, y, fgeom.unstick_rect.clickable))
-    {
-      return META_FRAME_CONTROL_UNSTICK;
-    }
-
-  /* South resize always has priority over north resize,
-   * in case of overlap.
-   */
-
-  if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) &&
-      x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS))
-    {
-      if (has_vert && has_horiz)
-        return META_FRAME_CONTROL_RESIZE_SE;
-      else if (has_vert)
-        return META_FRAME_CONTROL_RESIZE_S;
-      else if (has_horiz)
-        return META_FRAME_CONTROL_RESIZE_E;
-    }
-  else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS) &&
-           x <= (fgeom.borders.total.left + RESIZE_EXTENDS))
-    {
-      if (has_vert && has_horiz)
-        return META_FRAME_CONTROL_RESIZE_SW;
-      else if (has_vert)
-        return META_FRAME_CONTROL_RESIZE_S;
-      else if (has_horiz)
-        return META_FRAME_CONTROL_RESIZE_W;
-    }
-  else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) &&
-           x <= (fgeom.borders.total.left + RESIZE_EXTENDS) && has_north_resize)
-    {
-      if (has_vert && has_horiz)
-        return META_FRAME_CONTROL_RESIZE_NW;
-      else if (has_vert)
-        return META_FRAME_CONTROL_RESIZE_N;
-      else if (has_horiz)
-        return META_FRAME_CONTROL_RESIZE_W;
-    }
-  else if (y < (fgeom.borders.invisible.top + RESIZE_EXTENDS) &&
-           x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS) && has_north_resize)
-    {
-      if (has_vert && has_horiz)
-        return META_FRAME_CONTROL_RESIZE_NE;
-      else if (has_vert)
-        return META_FRAME_CONTROL_RESIZE_N;
-      else if (has_horiz)
-        return META_FRAME_CONTROL_RESIZE_E;
-    }
-  else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT))
-    {
-      if (has_vert && has_north_resize)
-        return META_FRAME_CONTROL_RESIZE_N;
-    }
-  else if (y >= (fgeom.height - fgeom.borders.total.bottom - RESIZE_EXTENDS))
-    {
-      if (has_vert)
-        return META_FRAME_CONTROL_RESIZE_S;
-    }
-  else if (x <= fgeom.borders.total.left + RESIZE_EXTENDS)
-    {
-      if (has_horiz)
-        return META_FRAME_CONTROL_RESIZE_W;
-    }
-  else if (x >= (fgeom.width - fgeom.borders.total.right - RESIZE_EXTENDS))
-    {
-      if (has_horiz)
-        return META_FRAME_CONTROL_RESIZE_E;
-    }
-
-  if (y >= fgeom.borders.total.top)
-    return META_FRAME_CONTROL_NONE;
-  else
-    return META_FRAME_CONTROL_TITLE;
-}
-
 void
 meta_frames_push_delay_exposes (MetaFrames *frames)
 {
diff --git a/src/ui/frames.h b/src/ui/frames.h
index 251e642..5d30c9a 100644
--- a/src/ui/frames.h
+++ b/src/ui/frames.h
@@ -74,6 +74,8 @@ struct _MetaUIFrame
 
   /* FIXME get rid of this, it can just be in the MetaFrames struct */
   MetaFrameControl prelit_control;
+  gint prelit_x;
+  gint prelit_y;
 };
 
 MetaFrames *meta_frames_new (void);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]