diff -r -N -u metacity-2.8.1/src/display.c metacity-2.8.1-new/src/display.c
--- metacity-2.8.1/src/display.c	2004-09-10 23:54:28.462615624 -0400
+++ metacity-2.8.1-new/src/display.c	2004-09-10 22:35:46.454268574 -0400
@@ -36,6 +36,7 @@
 #include "resizepopup.h"
 #include "xprops.h"
 #include "workspace.h"
+#include "history.h"
 #include "bell.h"
 #include "effects.h"
 #include "compositor.h"
@@ -3179,6 +3180,8 @@
       meta_ui_resize_popup_free (display->grab_resize_popup);
       display->grab_resize_popup = NULL;
     }
+  
+  meta_history_commit_window ();
 }
 
 static void
diff -r -N -u metacity-2.8.1/src/history.c metacity-2.8.1-new/src/history.c
--- metacity-2.8.1/src/history.c	1969-12-31 19:00:00.000000000 -0500
+++ metacity-2.8.1-new/src/history.c	2004-09-10 23:54:07.909320844 -0400
@@ -0,0 +1,1045 @@
+/* Metacity Window History */
+
+/* 
+ * Copyright (C) 2002 Benjamin Kahn
+ * based heavily on, and portions from Metacity Session Management
+ * Copyright (C) 2001 Havoc Pennington
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* A discussion on saving history:
+ *
+ * res_title contains the current window title.  orig_title
+ * stores the first title the window had.  We use this to track
+ * the life of the window.   That way, nautilus will start up in the
+ * same place if you start it in the same directory, but could show
+ * up in a different spot if started in a different directory (say
+ * start-here:/// and system:///home
+ *
+ * num starts out at -1 -- undefined.  In the placement of the window, 
+ * this value is updated to be one more than the number of similar windows.
+ * we look for res_class / orig_name / role / <num> from zero until we 
+ * can't find it anymore.  Then we assign num.
+ * 
+ * on move, (update) we check to renumber the window so if window
+ * numbers are 0, 1, 2, 5, 6, 8 and we move 6 it'll get renumbered
+ * to 3.
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include "main.h"
+#include "util.h"
+#include "display.h"
+#include "workspace.h"
+#include "window.h"
+#include "history.h"
+
+static GHashTable *window_history_list = NULL;
+
+/* We want to know when window was dropped, but metacity will have forgotten 
+ * the window at the drop, so we remember it here. 
+ */
+static MetaWindow *current_move = NULL;
+
+static MetaWindowHistoryInfo *history_info_new (void);
+static void save_history                       (void);
+static void history_info_free                  (MetaWindowHistoryInfo *info);
+static void load_state                         (void);
+static void save_history_entry                 (gpointer               key,
+			                        gpointer               value,
+			                        gpointer               userdata);
+static char *make_window_key                   (MetaWindow            *window);
+static char *make_window_key_history           (MetaWindowHistoryInfo *info);
+static char *make_window_key_full              (char                  *class, 
+						char                  *name, 
+						char                  *role, 
+						int                    num);
+GList *meta_history_find_windows               (MetaWindow            *window);
+void meta_history_remove_key                   (char                  *key);
+
+static void print_all_history   (void);
+static void print_history_entry (gpointer key, 
+				 gpointer value, 
+				 gpointer userdata);
+
+/* 
+ * When metacity is moving a window, the move routine gets called for each pixel 
+ * moved.  When the window is dropped, metacity no longer remembers which window
+ * was being moved.  These next three functions remember which window was being
+ * moved so we can only update our hashtable when the window is dropped.
+ */
+void meta_history_remember_window (MetaWindow *window)
+{
+  if (window == current_move) 
+    {
+      meta_topic (META_DEBUG_HISTORY, "Still moving window: '%s' (%p)\n", window->title, window);
+      return;
+    }
+
+  if (!current_move) 
+    {
+      current_move = window;
+     
+      meta_topic (META_DEBUG_HISTORY, "Started moving window: '%s' (%p)\n", window->title, window);
+ 
+      return;
+    }
+
+  meta_topic (META_DEBUG_HISTORY, "Another window '%s' (%p) is being moved while moving '%s' (%p)\n",
+  	      current_move ? current_move->title : "invalid", current_move, window->title, window);
+
+  meta_history_commit_window ();
+  current_move = window;
+}
+
+void meta_history_forget_window (void)
+{
+  if (current_move != NULL)
+    meta_topic (META_DEBUG_HISTORY, "A window was unmapped while moving window '%s' (%p).  Forgetting it.\n",
+		current_move ? current_move->title : "invalid", current_move);
+
+  current_move = NULL;
+
+  return;
+}
+
+void meta_history_commit_window (void)
+{
+  if (current_move == NULL)
+    return;
+
+  meta_topic (META_DEBUG_HISTORY, "Saving Information for window '%s' (%p).\n", 
+  	      current_move->title, current_move);
+
+  meta_history_next_window (current_move);
+  meta_history_update (current_move);
+  current_move = NULL;
+}
+
+
+void meta_history_clear_window (MetaWindow *window)
+{
+  if (window->save_id) 
+    {
+      meta_history_remove_key (window->save_id);
+      g_free (window->save_id);
+    }
+
+  /* When save_id is set at all we don't generate our own key. */
+  window->save_id = g_strdup ("");
+}
+
+void 
+meta_history_update_key (MetaWindow *window,
+			 gchar      *save_id)
+{
+  if (window->save_id) {
+    meta_history_remove_key (window->save_id);
+    g_free (window->save_id);
+  }
+  
+  window->save_id = save_id;
+  meta_history_update (window);
+  return;
+}
+
+/* 
+ * This function is a copy of a similar routine in place.c but
+ * doesn't ignore minimized windows.  It should probably be merged
+ * back at some point.
+ * The results of this function need to be freed by the caller.
+ */
+GList *
+meta_history_find_windows (MetaWindow *window)
+{
+  /* Find windows that matter (on same workspace
+   * as placed window, may be shaded - if shaded we pretend it isn't
+   * for placement purposes)
+   */
+  GSList *all_windows;
+  GList *windows = NULL;
+  GSList *tmp;
+  
+  all_windows = meta_display_list_windows (window->display);
+  
+  tmp = all_windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *w = tmp->data;
+      
+      if (w != window && 
+	  meta_window_shares_some_workspace (window, w))
+	windows = g_list_prepend (windows, w);
+      
+      tmp = tmp->next;
+    }
+  return windows;
+}
+
+int
+meta_history_next_window (MetaWindow *window)
+{
+  int ret;
+
+  GList *windows = meta_history_find_windows (window);
+  ret =  meta_history_next_window_full (windows, window);
+  
+  g_list_free (windows);
+
+  return ret;
+}
+
+/*
+ * Unique strings for windows are found by appending a number to the end.
+ * Class / Title / Role / Number
+ * if a window which was using a small number is closed, our number needs to 
+ * be lowered.  This function checks to see if that has happened. 
+ */
+int
+meta_history_next_window_full (GList *windows, 
+			       MetaWindow *window)
+{
+  GList *tmp;
+  int max = window->num;
+  int num;
+  int found;
+
+  /* This function has worst case of n^2, but n is usually small, and 
+   * worst case is very unlikely. 
+   */
+  
+  if (window->save_id) 
+    {
+      meta_topic (META_DEBUG_HISTORY, "Window (%p) save_id is being controlled by the application.\n",
+		  window);
+      return -1;
+    }
+  
+  /* max can be -1, in which case we need to scan until the first empty spot. */
+  for (num = 0; num != max; num++) 
+    {
+
+      found = 0;
+      for (tmp = windows; tmp != NULL; tmp = tmp->next) 
+	{
+	  MetaWindow *w;
+	  
+	  w = tmp->data;
+
+	  /* Does this window match? */
+	  if (both_null_or_matching (window->res_class, w->res_class) &&
+	      both_null_or_matching (window->orig_name, w->orig_name) &&
+	      both_null_or_matching (window->role, w->role)) 
+	    {
+
+	      if (num == w->num) 
+		{
+		  found = 1;
+		  break;
+		}
+	    }
+	}
+	      
+      if (!found) 
+	{
+	   meta_topic (META_DEBUG_HISTORY, "Window '%s' (%p) which was number %d is now %d.\n",
+		       window->title, window, window->num, num);
+	   window->num = num;
+	  
+	   return window->num;
+	}
+    }
+  
+  /* We didn't find anything below us, we're in the right order. */
+   meta_topic (META_DEBUG_HISTORY, "Window '%s' (%p) is in the right slot (%d).\n",
+	       window->title, window, window->num);
+
+  return window->num;
+}
+
+static char *
+make_window_key_full (char *class, char *name, char *role, int num)
+{
+  char *n = g_strdup ("0");
+  char *key = NULL;
+  char *r = role;
+
+  if (num > 0) 
+    {
+      g_free (n);
+      n = g_strdup_printf ("%d", num);
+    }
+
+  /* If we don't have a class or a name, we aren't unique enough.  Abort.*/
+  if (!class || !name) 
+    {
+      g_free (n);
+      return NULL;
+    }
+
+  if (!role)
+    r = " ";
+
+  key = g_strconcat (class, "/", 
+		     name, "/", 
+		     r, "/",  
+		     n, NULL);
+  g_free (n);
+
+  return key;
+}
+
+static char *
+make_window_key (MetaWindow *window)
+{
+  if (window->save_id)
+    return g_strdup (window->save_id);
+
+  return make_window_key_full (window->res_class, window->orig_name, window->role, window->num);
+}
+
+static char *
+make_window_key_history (MetaWindowHistoryInfo *hist)
+{
+  if (hist->save_id)
+    return g_strdup (hist->save_id);
+
+  return make_window_key_full (hist->res_class, hist->res_name, hist->role, hist->num);
+}
+
+const MetaWindowHistoryInfo* 
+meta_history_lookup  (MetaWindow *window)
+{
+  gchar *key;
+  const MetaWindowHistoryInfo *result = NULL;
+  
+  if (!window) return NULL;
+
+  key = make_window_key (window);
+
+  result = meta_history_lookup_key (key);
+  
+  g_free (key);
+  return result;
+}
+
+const MetaWindowHistoryInfo*
+meta_history_lookup_key (char *key)
+{
+ return (MetaWindowHistoryInfo *) g_hash_table_lookup (window_history_list,
+						       key); 
+}
+
+void
+meta_history_remove_key (char *key)
+{
+  g_hash_table_remove (window_history_list, key);
+}
+
+/* FIXME: This function scares me a little.  I'm not sure it's a good idea
+ * to restore this information, and I'm not sure that this file is the 
+ * right place to do it.  Also, I'm not sure this is the right way to 
+ * do this.  This function seems to fight with some applications.  (Nautilus)
+ */
+void meta_history_restore_window (MetaWindowHistoryInfo *hist,
+				  MetaWindow            *window)
+{
+  int tmp;
+
+  /* If placed is false when meta_window_resize is called, meta_window_place
+   * will get called, which might (will) call this function again.  That'll
+   * loop forever and cause a crash. 
+   */
+  tmp = window->placed;
+  window->placed = 1;
+  meta_window_resize (window, FALSE, hist->rect.width, hist->rect.height); 
+  window->placed = tmp;
+
+  window->rect.width = hist->rect.width;
+  window->rect.height = hist->rect.height;
+  window->rect.x = hist->rect.x;
+  window->rect.y = hist->rect.y;
+
+  if (hist->sticky)
+    meta_window_stick_internal (window);
+  else
+    meta_window_unstick_internal (window);
+
+  if (hist->fullscreen)
+    meta_window_make_fullscreen_internal (window);
+  else
+    meta_window_unmake_fullscreen_internal (window);
+
+  if (hist->maximized)
+    meta_window_maximize_real (window);
+  else
+    meta_window_unmaximize_internal (window);
+}
+
+/* This function updates a history item in place, but
+ * it assumes the item already exists.  
+ */
+void
+meta_history_update_inplace (MetaWindow *window)
+{
+  MetaWindowHistoryInfo *win;
+  gchar *key;
+
+  if (!window) return;
+
+  if (window->save_id && window->save_id[0] == '\0')
+    return;
+
+  key = make_window_key (window);
+
+  if (!key)
+    return;
+
+  win = g_hash_table_lookup (window_history_list, key);
+
+  g_free (key);
+
+  /* We can't save what we don't have. */
+  if (!win) return;
+
+  win->sticky      = window->on_all_workspaces;
+  win->maximized   = window->maximized;
+  win->fullscreen  = window->fullscreen;
+  win->updated     = time (NULL);
+  
+  return;
+}
+
+void
+meta_history_update (MetaWindow *window)
+{
+  MetaWindowHistoryInfo *win;
+  gchar *key;
+
+  if (!window) return;
+  if (!window->user_has_move_resized) return;
+
+  if (window->save_id && window->save_id[0] == '\0')
+    return;
+
+  key = make_window_key (window); 
+
+  meta_topic (META_DEBUG_HISTORY, "Updating window '%s' (%p) with key %s.\n", 
+	      window->title, window, key);
+
+  if (!key)
+    return;
+
+  win = history_info_new ();
+  
+  if (!win) {
+    g_free (key);
+    return;
+  }
+
+  win->res_class   = g_strdup (window->res_class);
+  win->res_name    = g_strdup (window->orig_name);
+  win->title       = g_strdup (window->title);
+  win->role        = g_strdup (window->role);
+  win->type        = window->type;
+  win->rect.x      = window->user_rect.x;
+  win->rect.y      = window->user_rect.y;
+  win->rect.width  = window->user_rect.width;
+  win->rect.height = window->user_rect.height;
+  win->num         = window->num; 
+  win->sticky      = window->on_all_workspaces;
+  win->maximized   = window->maximized;
+  win->fullscreen  = window->fullscreen;
+  win->updated     = time (NULL);
+  if (window->save_id)
+    win->save_id     = g_strdup (window->save_id);
+
+  g_hash_table_insert (window_history_list, (gpointer) key, (gpointer) win);
+
+  return;
+}
+
+void
+meta_history_init (void)
+{
+  /* Initialize the hash table of windows. */
+  window_history_list = g_hash_table_new_full (g_str_hash, 
+					       g_str_equal,
+					       g_free,
+					       (GDestroyNotify) history_info_free);
+  
+  meta_topic (META_DEBUG_HISTORY, "Initializing History...\n");
+
+  /* Now load the list of saved window positions and store them. */
+  load_state ();
+}
+
+void
+meta_history_shutdown (void)
+{
+  /* Save out the file. */
+  save_history ();
+
+  meta_topic (META_DEBUG_HISTORY, "Shutting Down Window History.\n");
+
+  return;
+}
+
+
+/* FIXME: This is stupid.  We should just use MetaWindowHistoryInfo instead
+ * wrapping it in a structure like this.
+ */
+typedef struct
+{
+  MetaWindowHistoryInfo *info;
+} ParseData;
+
+static void start_element_handler (GMarkupParseContext  *context,
+                                   const gchar          *element_name,
+                                   const gchar         **attribute_names,
+                                   const gchar         **attribute_values,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void end_element_handler   (GMarkupParseContext  *context,
+                                   const gchar          *element_name,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void text_handler          (GMarkupParseContext  *context,
+                                   const gchar          *text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+
+static GMarkupParser metacity_history_parser = {
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  NULL
+};
+
+static void
+history_info_free (MetaWindowHistoryInfo *info)
+{
+  g_free (info->res_class);
+  g_free (info->res_name);
+  g_free (info->title);
+  g_free (info->role);
+  g_free (info->save_id);
+
+  g_free (info);
+}
+
+static MetaWindowHistoryInfo *
+history_info_new (void)
+{
+  MetaWindowHistoryInfo *info;
+
+  info = g_new0 (MetaWindowHistoryInfo, 1);
+
+  info->type = META_WINDOW_NORMAL;
+  
+  return info;
+}
+
+static void 
+load_state (void)
+{
+  GMarkupParseContext *context;
+  GError *error;
+  ParseData parse_data;
+  char *text;
+  gsize length;
+  char *history_file;
+  
+  history_file = g_strconcat (g_get_home_dir (),
+                              "/.metacity/window_history",
+                              NULL);
+
+  error = NULL;
+  if (!g_file_get_contents (history_file,
+                            &text,
+                            &length,
+                            &error))
+    {
+      meta_warning (_("Failed to read saved history file %s: %s\n"),
+                    history_file, error->message);
+      g_error_free (error);
+      g_free (history_file);
+      history_file = NULL;
+      return;
+    }
+  
+  meta_topic (META_DEBUG_HISTORY, "Parsing saved history file %s\n", history_file);
+  g_free (history_file);
+  history_file = NULL;
+  
+  parse_data.info = NULL;
+  
+  context = g_markup_parse_context_new (&metacity_history_parser,
+                                        0, &parse_data, NULL);
+
+  error = NULL;
+  if (!g_markup_parse_context_parse (context,
+                                     text,
+                                     length,
+                                     &error))
+    goto error;
+  
+  
+  error = NULL;
+  if (!g_markup_parse_context_end_parse (context, &error))
+    goto error;
+
+  g_markup_parse_context_free (context);
+
+  goto out;
+
+ error:
+  
+  meta_warning (_("Failed to parse saved history file: %s\n"),
+                error->message);
+  g_error_free (error);
+
+  if (parse_data.info)
+    history_info_free (parse_data.info);
+
+ out:
+  
+  g_free (text);
+
+  return;
+}
+
+
+static void
+start_element_handler  (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        const gchar        **attribute_names,
+                        const gchar        **attribute_values,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+
+  if (strcmp (element_name, "metacity_history") == 0)
+    {
+      return; /* Right now this is a placeholder tag.  Probably not needed. */
+    }
+  else if (strcmp (element_name, "window") == 0)
+    {
+      int i;
+      
+      if (pd->info)
+        {
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_PARSE,
+                       _("nested <window> tag"));
+          return;
+        }
+      
+      pd->info = history_info_new ();
+
+      i = 0;
+      while (attribute_names[i])
+        {
+          const char *name;
+          const char *val;
+          
+          name = attribute_names[i];
+          val = attribute_values[i];
+          
+	  if (strcmp (name, "class") == 0)
+            {
+              if (*val)
+                pd->info->res_class = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "name") == 0)
+            {
+              if (*val)
+                pd->info->res_name = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "title") == 0)
+            {
+              if (*val)
+                pd->info->title = g_strdup (val);
+            }
+          else if (strcmp (name, "role") == 0)
+            {
+              if (*val)
+                pd->info->role = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "type") == 0)
+            {
+              if (*val)
+                pd->info->type = window_type_from_string (val);
+            }
+	  else if (strcmp (name, "num") == 0)
+	    {
+	      if (*val)
+		pd->info->num = atoi (val);
+	    }
+	  else if (strcmp (name, "save_id") == 0)
+	    {
+	      if (*val)
+		pd->info->save_id = g_strdup (val);
+	    }
+	  else if (strcmp (name, "updated") == 0)
+	    {
+	      if (*val)
+		pd->info->updated = atoi (val);
+	    }
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           _("Unknown attribute %s on <window> element"),
+                           name);
+              history_info_free (pd->info);
+              pd->info = NULL;
+              return;
+            }
+          
+          ++i;
+        }
+    }
+  else if (strcmp (element_name, "sticky") == 0)
+    {
+      pd->info->sticky = 1;
+    }
+  else if (strcmp (element_name, "maximized") == 0)
+    {
+      pd->info->maximized = 1;
+    }
+  else if (strcmp (element_name, "fullscreen") == 0)
+    {
+      pd->info->fullscreen = 1;
+    }
+  else if (strcmp (element_name, "geometry") == 0)
+    {
+      int i;
+
+      i = 0;
+      while (attribute_names[i])
+        {
+          const char *name;
+          const char *val;
+          
+          name = attribute_names[i];
+          val = attribute_values[i];
+          
+          if (strcmp (name, "x") == 0)
+            {
+              if (*val)
+                pd->info->rect.x = atoi (val);
+            }
+          else if (strcmp (name, "y") == 0)
+            {
+              if (*val)
+                pd->info->rect.y = atoi (val);
+            }
+          else if (strcmp (name, "width") == 0)
+            {
+              if (*val)
+                pd->info->rect.width = atoi (val);
+            }
+          else if (strcmp (name, "height") == 0)
+            {
+              if (*val)
+                pd->info->rect.height = atoi (val);
+            }
+          else if (strcmp (name, "gravity") == 0)
+            {
+              if (*val)
+                pd->info->gravity = window_gravity_from_string (val);
+            }
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           _("Unknown attribute %s on <geometry> element"),
+                           name);
+              return;
+            }
+          
+          ++i;
+        }
+
+      meta_topic (META_DEBUG_SM, "Loaded geometry %d,%d %dx%d gravity %s\n",
+		  pd->info->rect.x,
+		  pd->info->rect.y,
+		  pd->info->rect.width,
+		  pd->info->rect.height,
+		  window_gravity_to_string (pd->info->gravity));
+    }
+  else
+    {
+      g_set_error (error,
+                   G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                   _("Unknown element %s"),
+                   element_name);
+      return;
+    }
+}
+
+static void
+end_element_handler    (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+
+  if (strcmp (element_name, "window") == 0)
+    {
+      gchar *stored_name;
+
+      g_assert (pd->info);
+
+      stored_name = make_window_key_history (pd->info);
+      g_hash_table_insert (window_history_list, stored_name, pd->info);
+      
+      meta_topic (META_DEBUG_HISTORY, "Loaded window info from history with class: %s name: %s role: %s\n",
+		  pd->info->res_class ? pd->info->res_class : "(none)",
+		  pd->info->res_name ? pd->info->res_name : "(none)",
+		  pd->info->role ? pd->info->role : "(none)");
+      
+      
+      pd->info = NULL;
+    }
+}
+
+static void
+text_handler           (GMarkupParseContext *context,
+                        const gchar         *text,
+                        gsize                text_len,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+  
+  /* Right now we don't have any elements where we care about their
+   * content
+   */
+}
+
+static void
+save_history (void)
+{
+  char *metacity_dir;
+  char *history_file;
+  FILE *outfile;
+  
+  outfile = NULL;
+  
+  metacity_dir = g_strconcat (g_get_home_dir (), "/.metacity",
+                              NULL);
+  
+  history_file = g_strconcat (metacity_dir,
+			      "/window_history",
+			      NULL);
+
+  if (mkdir (metacity_dir, 0700) < 0 &&
+      errno != EEXIST)
+    {
+      meta_warning (_("Could not create directory '%s': %s\n"),
+                    metacity_dir, g_strerror (errno));
+    }
+
+  meta_topic (META_DEBUG_HISTORY, "Saving history to '%s'\n", history_file);
+  
+  outfile = fopen (history_file, "w");
+
+  if (outfile == NULL)
+    {
+      meta_warning (_("Could not open history file '%s' for writing: %s\n"),
+                    history_file, g_strerror (errno));
+      goto out;
+    }
+
+  /* The file format is:
+   * <metacity_history>
+   *   <window id="bar" class="XTerm" name="xterm" title="/foo/bar" role="blah" type="normal" num="0" updated="1039026079">
+   *     <geometry x="100" y="100" width="200" height="200" gravity="northwest"/>
+   *     <sticky/>
+   *     <maximized/>
+   *   </window>
+   *   <window save_id="Some String" updated="1039026093">
+   *     <fullscreen/>
+   *   </window>
+   *   :
+   * </metacity_session>
+   *
+   * Note that attributes on <window> are the match info we use to
+   * see if the saved state applies to a restored window, and
+   * child elements are the saved state to be applied.
+   * 
+   */
+  
+  fprintf (outfile, "<metacity_history>\n");
+  
+  g_hash_table_foreach (window_history_list, save_history_entry, (gpointer) outfile);
+
+  fputs ("</metacity_history>\n", outfile);
+  
+ out:
+  if (outfile)
+    {
+      /* FIXME need a dialog for this */
+      if (ferror (outfile))
+        {
+          meta_warning (_("Error writing history file '%s': %s\n"),
+                        history_file, g_strerror (errno));
+        }
+      if (fclose (outfile))
+        {
+          meta_warning (_("Error closing history file '%s': %s\n"),
+                        history_file, g_strerror (errno));
+        }
+    }
+  
+  g_free (metacity_dir);
+}
+
+static void
+save_history_entry (gpointer key, gpointer value, gpointer userdata)
+{
+  char *res_class;
+  char *res_name;
+  char *role;
+  char *save_id;
+  FILE *outfile = userdata;
+  MetaWindowHistoryInfo *window = value;
+  
+  /* client id, class, name, role are not expected to be
+   * in UTF-8 (I think they are in XPCS which is Latin-1?
+   * in practice they are always ascii though.)
+   */
+  
+  if (window->save_id)
+    {
+      save_id = encode_text_as_utf8_markup (window->save_id);
+
+      fprintf (outfile,
+	       "  <window save_id=\"%s\" updated=\"%d\">\n",
+	       save_id,
+	       (int) window->updated);
+
+      g_free (save_id);
+    }
+  else 
+    {
+      res_class = window->res_class ?
+	encode_text_as_utf8_markup (window->res_class) : NULL;
+      res_name = window->res_name ?
+	encode_text_as_utf8_markup (window->res_name) : NULL;
+      role = window->role ?
+	encode_text_as_utf8_markup (window->role) : NULL;
+
+      fprintf (outfile,
+	       "  <window class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\" num=\"%d\" updated=\"%d\">\n",
+	       res_class ? res_class : "",
+	       res_name ? res_name : "",
+	       window->title ? window->title : "",
+	       role ? role : "",
+	       window_type_to_string (window->type),
+	       window->num,
+	       (int) window->updated);
+
+      g_free (res_class);
+      g_free (res_name);
+      g_free (role);
+    }
+              
+  fprintf (outfile,
+	   "    <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n",
+	   window->rect.x, window->rect.y, window->rect.width, window->rect.height, "NorthWestGravity");
+
+  if (window->sticky) 
+    {
+      fprintf (outfile, "    <sticky/>\n");
+    }
+
+  if (window->maximized)
+    {
+      fprintf (outfile, "    <maximized/>\n");
+    }
+
+  if (window->fullscreen)
+    {
+      fprintf (outfile, "    <fullscreen/>\n");
+    }
+  
+  fputs ("  </window>\n", outfile);
+
+}
+
+static void
+print_all_history (void)
+{
+  meta_topic (META_DEBUG_HISTORY, "Printing History List.\n");
+
+  g_hash_table_foreach (window_history_list, print_history_entry, NULL);  
+}
+
+static void
+print_history_entry (gpointer key, gpointer value, gpointer userdata)
+{
+  MetaWindowHistoryInfo *window = value;
+
+  meta_topic (META_DEBUG_HISTORY, 
+	      "Key: %s, pointer %p\n"
+	      "  res_class : %s\n"
+	      "  res_name  : %s\n"
+	      "  title     : %s\n"
+	      "  role      : %s\n"
+	      "  num       : %d\n"
+	      "  gravity   : %d\n"
+	      "  sticky    : %d\n"
+	      "  maximized : %d\n"
+	      "  fullscreen: %d\n"
+	      "  save_id   : %d\n"
+	      "  updated   : %d\n"
+	      "  type      : %d\n"
+	      "  x: %d y: %d w: %d h: %d\n",
+	      (char *) key, window, window->res_class, window->res_name, 
+	      window->title,window->role, window->num, window->gravity, 
+	      window->sticky, window->maximized, window->fullscreen, window->save_id,
+	      window->updated, window->type, window->rect.x, window->rect.y, 
+	      window->rect.width, window->rect.height);
+}
+
diff -r -N -u metacity-2.8.1/src/history.c~ metacity-2.8.1-new/src/history.c~
--- metacity-2.8.1/src/history.c~	1969-12-31 19:00:00.000000000 -0500
+++ metacity-2.8.1-new/src/history.c~	2004-09-10 22:35:46.456268310 -0400
@@ -0,0 +1,1045 @@
+/* Metacity Window History */
+
+/* 
+ * Copyright (C) 2002 Benjamin Kahn
+ * based heavily on, and portions from Metacity Session Management
+ * Copyright (C) 2001 Havoc Pennington
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/* A discussion on saving history:
+ *
+ * res_title contains the current window title.  orig_title
+ * stores the first title the window had.  We use this to track
+ * the life of the window.   That way, nautilus will start up in the
+ * same place if you start it in the same directory, but could show
+ * up in a different spot if started in a different directory (say
+ * start-here:/// and system:///home
+ *
+ * num starts out at -1 -- undefined.  In the placement of the window, 
+ * this value is updated to be one more than the number of similar windows.
+ * we look for res_class / orig_name / role / <num> from zero until we 
+ * can't find it anymore.  Then we assign num.
+ * 
+ * on move, (update) we check to renumber the window so if window
+ * numbers are 0, 1, 2, 5, 6, 8 and we move 6 it'll get renumbered
+ * to 3.
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include "main.h"
+#include "util.h"
+#include "display.h"
+#include "workspace.h"
+#include "window.h"
+#include "history.h"
+
+static GHashTable *window_history_list = NULL;
+
+/* We want to know when window was dropped, but metacity will have forgotten 
+ * the window at the drop, so we remember it here. 
+ */
+static MetaWindow *current_move = NULL;
+
+static MetaWindowHistoryInfo *history_info_new (void);
+static void save_history                       (void);
+static void history_info_free                  (MetaWindowHistoryInfo *info);
+static void load_state                         (void);
+static void save_history_entry                 (gpointer               key,
+			                        gpointer               value,
+			                        gpointer               userdata);
+static char *make_window_key                   (MetaWindow            *window);
+static char *make_window_key_history           (MetaWindowHistoryInfo *info);
+static char *make_window_key_full              (char                  *class, 
+						char                  *name, 
+						char                  *role, 
+						int                    num);
+GList *meta_history_find_windows               (MetaWindow            *window);
+void meta_history_remove_key                   (char                  *key);
+
+static void print_all_history   (void);
+static void print_history_entry (gpointer key, 
+				 gpointer value, 
+				 gpointer userdata);
+
+/* 
+ * When metacity is moving a window, the move routine gets called for each pixel 
+ * moved.  When the window is dropped, metacity no longer remembers which window
+ * was being moved.  These next three functions remember which window was being
+ * moved so we can only update our hashtable when the window is dropped.
+ */
+void meta_history_remember_window (MetaWindow *window)
+{
+  if (window == current_move) 
+    {
+      meta_topic (META_DEBUG_HISTORY, "Still moving window: '%s' (%p)\n", window->title, window);
+      return;
+    }
+
+  if (!current_move) 
+    {
+      current_move = window;
+     
+      meta_topic (META_DEBUG_HISTORY, "Started moving window: '%s' (%p)\n", window->title, window);
+ 
+      return;
+    }
+
+  meta_topic (META_DEBUG_HISTORY, "Another window '%s' (%p) is being moved while moving '%s' (%p)\n",
+  	      current_move ? current_move->title : "invalid", current_move, window->title, window);
+
+  meta_history_commit_window ();
+  current_move = window;
+}
+
+void meta_history_forget_window (void)
+{
+  if (current_move != NULL)
+    meta_topic (META_DEBUG_HISTORY, "A window was unmapped while moving window '%s' (%p).  Forgetting it.\n",
+		current_move ? current_move->title : "invalid", current_move);
+
+  current_move = NULL;
+
+  return;
+}
+
+void meta_history_commit_window (void)
+{
+  if (current_move == NULL)
+    return;
+
+  meta_topic (META_DEBUG_HISTORY, "Saving Information for window '%s' (%p).\n", 
+  	      current_move->title, current_move);
+
+  meta_history_next_window (current_move);
+  meta_history_update (current_move);
+  current_move = NULL;
+}
+
+
+void meta_history_clear_window (MetaWindow *window)
+{
+  if (window->save_id) 
+    {
+      meta_history_remove_key (window->save_id);
+      g_free (window->save_id);
+    }
+
+  /* When save_id is set at all we don't generate our own key. */
+  window->save_id = g_strdup ("");
+}
+
+void 
+meta_history_update_key (MetaWindow *window,
+			 gchar      *save_id)
+{
+  if (window->save_id) {
+    meta_history_remove_key (window->save_id);
+    g_free (window->save_id);
+  }
+  
+  window->save_id = save_id;
+  meta_history_update (window);
+  return;
+}
+
+/* 
+ * This function is a copy of a similar routine in place.c but
+ * doesn't ignore minimized windows.  It should probably be merged
+ * back at some point.
+ * The results of this function need to be freed by the caller.
+ */
+GList *
+meta_history_find_windows (MetaWindow *window)
+{
+  /* Find windows that matter (on same workspace
+   * as placed window, may be shaded - if shaded we pretend it isn't
+   * for placement purposes)
+   */
+  GSList *all_windows;
+  GList *windows = NULL;
+  GSList *tmp;
+  
+  all_windows = meta_display_list_windows (window->display);
+  
+  tmp = all_windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *w = tmp->data;
+      
+      if (w != window && 
+	  meta_window_shares_some_workspace (window, w))
+	windows = g_list_prepend (windows, w);
+      
+      tmp = tmp->next;
+    }
+  return windows;
+}
+
+int
+meta_history_next_window (MetaWindow *window)
+{
+  int ret;
+
+  GList *windows = meta_history_find_windows (window);
+  ret =  meta_history_next_window_full (windows, window);
+  
+  g_list_free (windows);
+
+  return ret;
+}
+
+/*
+ * Unique strings for windows are found by appending a number to the end.
+ * Class / Title / Role / Number
+ * if a window which was using a small number is closed, our number needs to 
+ * be lowered.  This function checks to see if that has happened. 
+ */
+int
+meta_history_next_window_full (GList *windows, 
+			       MetaWindow *window)
+{
+  GList *tmp;
+  int max = window->num;
+  int num;
+  int found;
+
+  /* This function has worst case of n^2, but n is usually small, and 
+   * worst case is very unlikely. 
+   */
+  
+  if (window->save_id) 
+    {
+      meta_topic (META_DEBUG_HISTORY, "Window (%p) save_id is being controlled by the application.\n",
+		  window);
+      return -1;
+    }
+  
+  /* max can be -1, in which case we need to scan until the first empty spot. */
+  for (num = 0; num != max; num++) 
+    {
+
+      found = 0;
+      for (tmp = windows; tmp != NULL; tmp = tmp->next) 
+	{
+	  MetaWindow *w;
+	  
+	  w = tmp->data;
+
+	  /* Does this window match? */
+	  if (both_null_or_matching (window->res_class, w->res_class) &&
+	      both_null_or_matching (window->orig_name, w->orig_name) &&
+	      both_null_or_matching (window->role, w->role)) 
+	    {
+
+	      if (num == w->num) 
+		{
+		  found = 1;
+		  break;
+		}
+	    }
+	}
+	      
+      if (!found) 
+	{
+	   meta_topic (META_DEBUG_HISTORY, "Window '%s' (%p) which was number %d is now %d.\n",
+		       window->title, window, window->num, num);
+	   window->num = num;
+	  
+	   return window->num;
+	}
+    }
+  
+  /* We didn't find anything below us, we're in the right order. */
+   meta_topic (META_DEBUG_HISTORY, "Window '%s' (%p) is in the right slot (%d).\n",
+	       window->title, window, window->num);
+
+  return window->num;
+}
+
+static char *
+make_window_key_full (char *class, char *name, char *role, int num)
+{
+  char *n = g_strdup ("0");
+  char *key = NULL;
+  char *r = role;
+
+  if (num > 0) 
+    {
+      g_free (n);
+      n = g_strdup_printf ("%d", num);
+    }
+
+  /* If we don't have a class or a name, we aren't unique enough.  Abort.*/
+  if (!class || !name) 
+    {
+      g_free (n);
+      return NULL;
+    }
+
+  if (!role)
+    r = " ";
+
+  key = g_strconcat (class, "/", 
+		     name, "/", 
+		     r, "/",  
+		     n, NULL);
+  g_free (n);
+
+  return key;
+}
+
+static char *
+make_window_key (MetaWindow *window)
+{
+  if (window->save_id)
+    return g_strdup (window->save_id);
+
+  return make_window_key_full (window->res_class, window->orig_name, window->role, window->num);
+}
+
+static char *
+make_window_key_history (MetaWindowHistoryInfo *hist)
+{
+  if (hist->save_id)
+    return g_strdup (hist->save_id);
+
+  return make_window_key_full (hist->res_class, hist->res_name, hist->role, hist->num);
+}
+
+const MetaWindowHistoryInfo* 
+meta_history_lookup  (MetaWindow *window)
+{
+  gchar *key;
+  const MetaWindowHistoryInfo *result = NULL;
+  
+  if (!window) return NULL;
+
+  key = make_window_key (window);
+
+  result = meta_history_lookup_key (key);
+  
+  g_free (key);
+  return result;
+}
+
+const MetaWindowHistoryInfo*
+meta_history_lookup_key (char *key)
+{
+ return (MetaWindowHistoryInfo *) g_hash_table_lookup (window_history_list,
+						       key); 
+}
+
+void
+meta_history_remove_key (char *key)
+{
+  g_hash_table_remove (window_history_list, key);
+}
+
+/* FIXME: This function scares me a little.  I'm not sure it's a good idea
+ * to restore this information, and I'm not sure that this file is the 
+ * right place to do it.  Also, I'm not sure this is the right way to 
+ * do this.  This function seems to fight with some applications.  (Nautilus)
+ */
+void meta_history_restore_window (MetaWindowHistoryInfo *hist,
+				  MetaWindow            *window)
+{
+  int tmp;
+
+  /* If placed is false when meta_window_resize is called, meta_window_place
+   * will get called, which might (will) call this function again.  That'll
+   * loop forever and cause a crash. 
+   */
+  tmp = window->placed;
+  window->placed = 1;
+  meta_window_resize (window, FALSE, hist->rect.width, hist->rect.height); 
+  window->placed = tmp;
+
+  window->rect.width = hist->rect.width;
+  window->rect.height = hist->rect.height;
+  window->rect.x = hist->rect.x;
+  window->rect.y = hist->rect.y;
+
+  if (hist->sticky)
+    meta_window_stick_internal (window);
+  else
+    meta_window_unstick_internal (window);
+
+  if (hist->fullscreen)
+    meta_window_make_fullscreen_internal (window);
+  else
+    meta_window_unmake_fullscreen_internal (window);
+
+  if (hist->maximized)
+    meta_window_maximize_internal (window);
+  else
+    meta_window_unmaximize_internal (window);
+}
+
+/* This function updates a history item in place, but
+ * it assumes the item already exists.  
+ */
+void
+meta_history_update_inplace (MetaWindow *window)
+{
+  MetaWindowHistoryInfo *win;
+  gchar *key;
+
+  if (!window) return;
+
+  if (window->save_id && window->save_id[0] == '\0')
+    return;
+
+  key = make_window_key (window);
+
+  if (!key)
+    return;
+
+  win = g_hash_table_lookup (window_history_list, key);
+
+  g_free (key);
+
+  /* We can't save what we don't have. */
+  if (!win) return;
+
+  win->sticky      = window->on_all_workspaces;
+  win->maximized   = window->maximized;
+  win->fullscreen  = window->fullscreen;
+  win->updated     = time (NULL);
+  
+  return;
+}
+
+void
+meta_history_update (MetaWindow *window)
+{
+  MetaWindowHistoryInfo *win;
+  gchar *key;
+
+  if (!window) return;
+  if (!window->user_has_move_resized) return;
+
+  if (window->save_id && window->save_id[0] == '\0')
+    return;
+
+  key = make_window_key (window); 
+
+  meta_topic (META_DEBUG_HISTORY, "Updating window '%s' (%p) with key %s.\n", 
+	      window->title, window, key);
+
+  if (!key)
+    return;
+
+  win = history_info_new ();
+  
+  if (!win) {
+    g_free (key);
+    return;
+  }
+
+  win->res_class   = g_strdup (window->res_class);
+  win->res_name    = g_strdup (window->orig_name);
+  win->title       = g_strdup (window->title);
+  win->role        = g_strdup (window->role);
+  win->type        = window->type;
+  win->rect.x      = window->user_rect.x;
+  win->rect.y      = window->user_rect.y;
+  win->rect.width  = window->user_rect.width;
+  win->rect.height = window->user_rect.height;
+  win->num         = window->num; 
+  win->sticky      = window->on_all_workspaces;
+  win->maximized   = window->maximized;
+  win->fullscreen  = window->fullscreen;
+  win->updated     = time (NULL);
+  if (window->save_id)
+    win->save_id     = g_strdup (window->save_id);
+
+  g_hash_table_insert (window_history_list, (gpointer) key, (gpointer) win);
+
+  return;
+}
+
+void
+meta_history_init (void)
+{
+  /* Initialize the hash table of windows. */
+  window_history_list = g_hash_table_new_full (g_str_hash, 
+					       g_str_equal,
+					       g_free,
+					       (GDestroyNotify) history_info_free);
+  
+  meta_topic (META_DEBUG_HISTORY, "Initializing History...\n");
+
+  /* Now load the list of saved window positions and store them. */
+  load_state ();
+}
+
+void
+meta_history_shutdown (void)
+{
+  /* Save out the file. */
+  save_history ();
+
+  meta_topic (META_DEBUG_HISTORY, "Shutting Down Window History.\n");
+
+  return;
+}
+
+
+/* FIXME: This is stupid.  We should just use MetaWindowHistoryInfo instead
+ * wrapping it in a structure like this.
+ */
+typedef struct
+{
+  MetaWindowHistoryInfo *info;
+} ParseData;
+
+static void start_element_handler (GMarkupParseContext  *context,
+                                   const gchar          *element_name,
+                                   const gchar         **attribute_names,
+                                   const gchar         **attribute_values,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void end_element_handler   (GMarkupParseContext  *context,
+                                   const gchar          *element_name,
+                                   gpointer              user_data,
+                                   GError              **error);
+static void text_handler          (GMarkupParseContext  *context,
+                                   const gchar          *text,
+                                   gsize                 text_len,
+                                   gpointer              user_data,
+                                   GError              **error);
+
+static GMarkupParser metacity_history_parser = {
+  start_element_handler,
+  end_element_handler,
+  text_handler,
+  NULL,
+  NULL
+};
+
+static void
+history_info_free (MetaWindowHistoryInfo *info)
+{
+  g_free (info->res_class);
+  g_free (info->res_name);
+  g_free (info->title);
+  g_free (info->role);
+  g_free (info->save_id);
+
+  g_free (info);
+}
+
+static MetaWindowHistoryInfo *
+history_info_new (void)
+{
+  MetaWindowHistoryInfo *info;
+
+  info = g_new0 (MetaWindowHistoryInfo, 1);
+
+  info->type = META_WINDOW_NORMAL;
+  
+  return info;
+}
+
+static void 
+load_state (void)
+{
+  GMarkupParseContext *context;
+  GError *error;
+  ParseData parse_data;
+  char *text;
+  gsize length;
+  char *history_file;
+  
+  history_file = g_strconcat (g_get_home_dir (),
+                              "/.metacity/window_history",
+                              NULL);
+
+  error = NULL;
+  if (!g_file_get_contents (history_file,
+                            &text,
+                            &length,
+                            &error))
+    {
+      meta_warning (_("Failed to read saved history file %s: %s\n"),
+                    history_file, error->message);
+      g_error_free (error);
+      g_free (history_file);
+      history_file = NULL;
+      return;
+    }
+  
+  meta_topic (META_DEBUG_HISTORY, "Parsing saved history file %s\n", history_file);
+  g_free (history_file);
+  history_file = NULL;
+  
+  parse_data.info = NULL;
+  
+  context = g_markup_parse_context_new (&metacity_history_parser,
+                                        0, &parse_data, NULL);
+
+  error = NULL;
+  if (!g_markup_parse_context_parse (context,
+                                     text,
+                                     length,
+                                     &error))
+    goto error;
+  
+  
+  error = NULL;
+  if (!g_markup_parse_context_end_parse (context, &error))
+    goto error;
+
+  g_markup_parse_context_free (context);
+
+  goto out;
+
+ error:
+  
+  meta_warning (_("Failed to parse saved history file: %s\n"),
+                error->message);
+  g_error_free (error);
+
+  if (parse_data.info)
+    history_info_free (parse_data.info);
+
+ out:
+  
+  g_free (text);
+
+  return;
+}
+
+
+static void
+start_element_handler  (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        const gchar        **attribute_names,
+                        const gchar        **attribute_values,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+
+  if (strcmp (element_name, "metacity_history") == 0)
+    {
+      return; /* Right now this is a placeholder tag.  Probably not needed. */
+    }
+  else if (strcmp (element_name, "window") == 0)
+    {
+      int i;
+      
+      if (pd->info)
+        {
+          g_set_error (error,
+                       G_MARKUP_ERROR,
+                       G_MARKUP_ERROR_PARSE,
+                       _("nested <window> tag"));
+          return;
+        }
+      
+      pd->info = history_info_new ();
+
+      i = 0;
+      while (attribute_names[i])
+        {
+          const char *name;
+          const char *val;
+          
+          name = attribute_names[i];
+          val = attribute_values[i];
+          
+	  if (strcmp (name, "class") == 0)
+            {
+              if (*val)
+                pd->info->res_class = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "name") == 0)
+            {
+              if (*val)
+                pd->info->res_name = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "title") == 0)
+            {
+              if (*val)
+                pd->info->title = g_strdup (val);
+            }
+          else if (strcmp (name, "role") == 0)
+            {
+              if (*val)
+                pd->info->role = decode_text_from_utf8 (val);
+            }
+          else if (strcmp (name, "type") == 0)
+            {
+              if (*val)
+                pd->info->type = window_type_from_string (val);
+            }
+	  else if (strcmp (name, "num") == 0)
+	    {
+	      if (*val)
+		pd->info->num = atoi (val);
+	    }
+	  else if (strcmp (name, "save_id") == 0)
+	    {
+	      if (*val)
+		pd->info->save_id = g_strdup (val);
+	    }
+	  else if (strcmp (name, "updated") == 0)
+	    {
+	      if (*val)
+		pd->info->updated = atoi (val);
+	    }
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           _("Unknown attribute %s on <window> element"),
+                           name);
+              history_info_free (pd->info);
+              pd->info = NULL;
+              return;
+            }
+          
+          ++i;
+        }
+    }
+  else if (strcmp (element_name, "sticky") == 0)
+    {
+      pd->info->sticky = 1;
+    }
+  else if (strcmp (element_name, "maximized") == 0)
+    {
+      pd->info->maximized = 1;
+    }
+  else if (strcmp (element_name, "fullscreen") == 0)
+    {
+      pd->info->fullscreen = 1;
+    }
+  else if (strcmp (element_name, "geometry") == 0)
+    {
+      int i;
+
+      i = 0;
+      while (attribute_names[i])
+        {
+          const char *name;
+          const char *val;
+          
+          name = attribute_names[i];
+          val = attribute_values[i];
+          
+          if (strcmp (name, "x") == 0)
+            {
+              if (*val)
+                pd->info->rect.x = atoi (val);
+            }
+          else if (strcmp (name, "y") == 0)
+            {
+              if (*val)
+                pd->info->rect.y = atoi (val);
+            }
+          else if (strcmp (name, "width") == 0)
+            {
+              if (*val)
+                pd->info->rect.width = atoi (val);
+            }
+          else if (strcmp (name, "height") == 0)
+            {
+              if (*val)
+                pd->info->rect.height = atoi (val);
+            }
+          else if (strcmp (name, "gravity") == 0)
+            {
+              if (*val)
+                pd->info->gravity = window_gravity_from_string (val);
+            }
+          else
+            {
+              g_set_error (error,
+                           G_MARKUP_ERROR,
+                           G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+                           _("Unknown attribute %s on <geometry> element"),
+                           name);
+              return;
+            }
+          
+          ++i;
+        }
+
+      meta_topic (META_DEBUG_SM, "Loaded geometry %d,%d %dx%d gravity %s\n",
+		  pd->info->rect.x,
+		  pd->info->rect.y,
+		  pd->info->rect.width,
+		  pd->info->rect.height,
+		  window_gravity_to_string (pd->info->gravity));
+    }
+  else
+    {
+      g_set_error (error,
+                   G_MARKUP_ERROR,
+                   G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                   _("Unknown element %s"),
+                   element_name);
+      return;
+    }
+}
+
+static void
+end_element_handler    (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+
+  if (strcmp (element_name, "window") == 0)
+    {
+      gchar *stored_name;
+
+      g_assert (pd->info);
+
+      stored_name = make_window_key_history (pd->info);
+      g_hash_table_insert (window_history_list, stored_name, pd->info);
+      
+      meta_topic (META_DEBUG_HISTORY, "Loaded window info from history with class: %s name: %s role: %s\n",
+		  pd->info->res_class ? pd->info->res_class : "(none)",
+		  pd->info->res_name ? pd->info->res_name : "(none)",
+		  pd->info->role ? pd->info->role : "(none)");
+      
+      
+      pd->info = NULL;
+    }
+}
+
+static void
+text_handler           (GMarkupParseContext *context,
+                        const gchar         *text,
+                        gsize                text_len,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  ParseData *pd;
+
+  pd = user_data;
+  
+  /* Right now we don't have any elements where we care about their
+   * content
+   */
+}
+
+static void
+save_history (void)
+{
+  char *metacity_dir;
+  char *history_file;
+  FILE *outfile;
+  
+  outfile = NULL;
+  
+  metacity_dir = g_strconcat (g_get_home_dir (), "/.metacity",
+                              NULL);
+  
+  history_file = g_strconcat (metacity_dir,
+			      "/window_history",
+			      NULL);
+
+  if (mkdir (metacity_dir, 0700) < 0 &&
+      errno != EEXIST)
+    {
+      meta_warning (_("Could not create directory '%s': %s\n"),
+                    metacity_dir, g_strerror (errno));
+    }
+
+  meta_topic (META_DEBUG_HISTORY, "Saving history to '%s'\n", history_file);
+  
+  outfile = fopen (history_file, "w");
+
+  if (outfile == NULL)
+    {
+      meta_warning (_("Could not open history file '%s' for writing: %s\n"),
+                    history_file, g_strerror (errno));
+      goto out;
+    }
+
+  /* The file format is:
+   * <metacity_history>
+   *   <window id="bar" class="XTerm" name="xterm" title="/foo/bar" role="blah" type="normal" num="0" updated="1039026079">
+   *     <geometry x="100" y="100" width="200" height="200" gravity="northwest"/>
+   *     <sticky/>
+   *     <maximized/>
+   *   </window>
+   *   <window save_id="Some String" updated="1039026093">
+   *     <fullscreen/>
+   *   </window>
+   *   :
+   * </metacity_session>
+   *
+   * Note that attributes on <window> are the match info we use to
+   * see if the saved state applies to a restored window, and
+   * child elements are the saved state to be applied.
+   * 
+   */
+  
+  fprintf (outfile, "<metacity_history>\n");
+  
+  g_hash_table_foreach (window_history_list, save_history_entry, (gpointer) outfile);
+
+  fputs ("</metacity_history>\n", outfile);
+  
+ out:
+  if (outfile)
+    {
+      /* FIXME need a dialog for this */
+      if (ferror (outfile))
+        {
+          meta_warning (_("Error writing history file '%s': %s\n"),
+                        history_file, g_strerror (errno));
+        }
+      if (fclose (outfile))
+        {
+          meta_warning (_("Error closing history file '%s': %s\n"),
+                        history_file, g_strerror (errno));
+        }
+    }
+  
+  g_free (metacity_dir);
+}
+
+static void
+save_history_entry (gpointer key, gpointer value, gpointer userdata)
+{
+  char *res_class;
+  char *res_name;
+  char *role;
+  char *save_id;
+  FILE *outfile = userdata;
+  MetaWindowHistoryInfo *window = value;
+  
+  /* client id, class, name, role are not expected to be
+   * in UTF-8 (I think they are in XPCS which is Latin-1?
+   * in practice they are always ascii though.)
+   */
+  
+  if (window->save_id)
+    {
+      save_id = encode_text_as_utf8_markup (window->save_id);
+
+      fprintf (outfile,
+	       "  <window save_id=\"%s\" updated=\"%d\">\n",
+	       save_id,
+	       (int) window->updated);
+
+      g_free (save_id);
+    }
+  else 
+    {
+      res_class = window->res_class ?
+	encode_text_as_utf8_markup (window->res_class) : NULL;
+      res_name = window->res_name ?
+	encode_text_as_utf8_markup (window->res_name) : NULL;
+      role = window->role ?
+	encode_text_as_utf8_markup (window->role) : NULL;
+
+      fprintf (outfile,
+	       "  <window class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\" num=\"%d\" updated=\"%d\">\n",
+	       res_class ? res_class : "",
+	       res_name ? res_name : "",
+	       window->title ? window->title : "",
+	       role ? role : "",
+	       window_type_to_string (window->type),
+	       window->num,
+	       (int) window->updated);
+
+      g_free (res_class);
+      g_free (res_name);
+      g_free (role);
+    }
+              
+  fprintf (outfile,
+	   "    <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n",
+	   window->rect.x, window->rect.y, window->rect.width, window->rect.height, "NorthWestGravity");
+
+  if (window->sticky) 
+    {
+      fprintf (outfile, "    <sticky/>\n");
+    }
+
+  if (window->maximized)
+    {
+      fprintf (outfile, "    <maximized/>\n");
+    }
+
+  if (window->fullscreen)
+    {
+      fprintf (outfile, "    <fullscreen/>\n");
+    }
+  
+  fputs ("  </window>\n", outfile);
+
+}
+
+static void
+print_all_history (void)
+{
+  meta_topic (META_DEBUG_HISTORY, "Printing History List.\n");
+
+  g_hash_table_foreach (window_history_list, print_history_entry, NULL);  
+}
+
+static void
+print_history_entry (gpointer key, gpointer value, gpointer userdata)
+{
+  MetaWindowHistoryInfo *window = value;
+
+  meta_topic (META_DEBUG_HISTORY, 
+	      "Key: %s, pointer %p\n"
+	      "  res_class : %s\n"
+	      "  res_name  : %s\n"
+	      "  title     : %s\n"
+	      "  role      : %s\n"
+	      "  num       : %d\n"
+	      "  gravity   : %d\n"
+	      "  sticky    : %d\n"
+	      "  maximized : %d\n"
+	      "  fullscreen: %d\n"
+	      "  save_id   : %d\n"
+	      "  updated   : %d\n"
+	      "  type      : %d\n"
+	      "  x: %d y: %d w: %d h: %d\n",
+	      (char *) key, window, window->res_class, window->res_name, 
+	      window->title,window->role, window->num, window->gravity, 
+	      window->sticky, window->maximized, window->fullscreen, window->save_id,
+	      window->updated, window->type, window->rect.x, window->rect.y, 
+	      window->rect.width, window->rect.height);
+}
+
diff -r -N -u metacity-2.8.1/src/history.h metacity-2.8.1-new/src/history.h
--- metacity-2.8.1/src/history.h	1969-12-31 19:00:00.000000000 -0500
+++ metacity-2.8.1-new/src/history.h	2004-09-10 22:35:46.456268310 -0400
@@ -0,0 +1,86 @@
+/* Metacity Window History */
+
+/* 
+ * Copyright (C) 2002 Benjamin Kahn 
+ * portions Copyright (C) 2001 Havoc Pennington
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef META_HISTORY_H
+#define META_HISTORY_H
+
+#include "window.h"
+
+typedef struct _MetaWindowHistoryInfo MetaWindowHistoryInfo;
+
+struct _MetaWindowHistoryInfo
+{
+  /* Fields we use to match against */
+
+  char  *res_class;
+  char  *res_name;
+  char  *title;
+  char  *role;
+  char  *save_id;
+
+  int    gravity;
+
+  int    sticky;
+  int    maximized;
+  int    fullscreen;
+
+  int    num;
+
+  time_t updated;
+
+  MetaWindowType type;
+
+  /* Information we restore */
+  /* width/height should be multiplied by resize inc and
+   * added to base size; position should be interpreted in
+   * light of gravity. This preserves semantics of the
+   * window size/pos, even if fonts/themes change, etc.
+   */
+  MetaRectangle rect;
+};
+
+const MetaWindowHistoryInfo* meta_history_lookup           (MetaWindow *window);
+const MetaWindowHistoryInfo* meta_history_lookup_key       (char       *key);
+
+void meta_history_update          (MetaWindow *window);
+void meta_history_update_inplace  (MetaWindow *window);
+
+int meta_history_next_window      (MetaWindow *window);
+int meta_history_next_window_full (GList      *windows, 
+				   MetaWindow *window);
+
+void meta_history_remember_window (MetaWindow *window);
+void meta_history_forget_window   (void);
+void meta_history_commit_window   (void);
+
+void meta_history_init     (void);
+void meta_history_shutdown (void);
+
+void meta_history_restore_window (MetaWindowHistoryInfo *hist,
+				  MetaWindow            *window);
+
+void meta_history_save_id      (MetaWindow *window);
+void meta_history_clear_window (MetaWindow *window);
+void meta_history_update_key   (MetaWindow *window,
+				gchar      *save_id);
+
+#endif /* META_HISTORY_H */
diff -r -N -u metacity-2.8.1/src/main.c metacity-2.8.1-new/src/main.c
--- metacity-2.8.1/src/main.c	2003-12-21 19:43:53.000000000 -0500
+++ metacity-2.8.1-new/src/main.c	2004-09-10 22:35:46.457268179 -0400
@@ -25,6 +25,7 @@
 #include "display.h"
 #include "errors.h"
 #include "ui.h"
+#include "history.h"
 #include "session.h"
 #include "prefs.h"
 
@@ -390,6 +391,9 @@
   
   meta_ui_init (&argc, &argv);  
 
+  /* Load the window history so we can place windows. */
+  meta_history_init ();
+
   /* must be after UI init so we can override GDK handlers */
   meta_errors_init ();
 
@@ -503,6 +507,7 @@
   }
 
   meta_session_shutdown ();
+  meta_history_shutdown ();
   
   if (meta_restart_after_quit)
     {
diff -r -N -u metacity-2.8.1/src/Makefile.am metacity-2.8.1-new/src/Makefile.am
--- metacity-2.8.1/src/Makefile.am	2003-11-24 18:05:04.000000000 -0500
+++ metacity-2.8.1-new/src/Makefile.am	2004-09-10 22:35:46.457268179 -0400
@@ -64,6 +64,8 @@
 	resizepopup.h				\
 	screen.c				\
 	screen.h				\
+	history.c				\
+	history.h				\
 	session.c				\
 	session.h				\
 	stack.c					\
diff -r -N -u metacity-2.8.1/src/Makefile.in metacity-2.8.1-new/src/Makefile.in
--- metacity-2.8.1/src/Makefile.in	2004-05-04 21:16:37.000000000 -0400
+++ metacity-2.8.1-new/src/Makefile.in	2004-09-10 23:51:34.423522167 -0400
@@ -132,7 +132,7 @@
 EGGFILES =  	eggaccelerators.c		eggaccelerators.h
 
 
-metacity_SOURCES =  	async-getprop.c					async-getprop.h					bell.h						bell.c						common.h					compositor.c					compositor.h					constraints.c					constraints.h					core.c						core.h						delete.c					display.c					display.h					draw-workspace.c				draw-workspace.h				effects.c					effects.h					errors.c					errors.h					eventqueue.c					eventqueue.h					fixedtip.c					fixedtip.h					frame.c						frame.h						frames.c					frames.h					gradient.c					gradient.h					group.c						group.h						group-private.h					group-props.c					group-props.h					iconcache.c					iconcache.h					inlinepixbufs.h					keybindings.c					keybindings.h					main.c						main.h						menu.c						menu.h						metaaccellabel.c				metaaccellabel.h				metacity-Xatomtype.h				place.c						place.h						prefs.c						prefs.h						resizepopup.c					resizepopup.h					screen.c					screen.h					session.c					session.h					stack.c						stack.h						tabpopup.c					tabpopup.h					theme.c						theme.h						theme-parser.c					theme-parser.h					themewidget.c					themewidget.h					ui.c						ui.h						util.c						util.h						window.c					window.h					window-props.c					window-props.h					workspace.c					workspace.h					xprops.c					xprops.h					$(EGGFILES)
+metacity_SOURCES =  	async-getprop.c					async-getprop.h					bell.h						bell.c						common.h					compositor.c					compositor.h					constraints.c					constraints.h					core.c						core.h						delete.c					display.c					display.h					draw-workspace.c				draw-workspace.h				effects.c					effects.h					errors.c					errors.h					eventqueue.c					eventqueue.h					fixedtip.c					fixedtip.h					frame.c						frame.h						frames.c					frames.h					gradient.c					gradient.h					group.c						group.h						group-private.h					group-props.c					group-props.h					history.h					history.c					iconcache.c					iconcache.h					inlinepixbufs.h					keybindings.c					keybindings.h					main.c						main.h						menu.c						menu.h						metaaccellabel.c				metaaccellabel.h				metacity-Xatomtype.h				place.c						place.h						prefs.c						prefs.h						resizepopup.c					resizepopup.h					screen.c					screen.h					session.c					session.h					stack.c						stack.h						tabpopup.c					tabpopup.h					theme.c						theme.h						theme-parser.c					theme-parser.h					themewidget.c					themewidget.h					ui.c						ui.h						util.c						util.h						window.c					window.h					window-props.c					window-props.h					workspace.c					workspace.h					xprops.c					xprops.h					$(EGGFILES)
 
 
 libmetacity_private_la_SOURCES =  	gradient.c			gradient.h			preview-widget.c		preview-widget.h		theme.c				theme.h				theme-parser.c			theme-parser.h			util.c				util.h				common.h
@@ -219,7 +219,7 @@
 delete.$(OBJEXT) display.$(OBJEXT) draw-workspace.$(OBJEXT) \
 effects.$(OBJEXT) errors.$(OBJEXT) eventqueue.$(OBJEXT) \
 fixedtip.$(OBJEXT) frame.$(OBJEXT) frames.$(OBJEXT) gradient.$(OBJEXT) \
-group.$(OBJEXT) group-props.$(OBJEXT) iconcache.$(OBJEXT) \
+group.$(OBJEXT) group-props.$(OBJEXT) history.$(OBJEXT) iconcache.$(OBJEXT) \
 keybindings.$(OBJEXT) main.$(OBJEXT) menu.$(OBJEXT) \
 metaaccellabel.$(OBJEXT) place.$(OBJEXT) prefs.$(OBJEXT) \
 resizepopup.$(OBJEXT) screen.$(OBJEXT) session.$(OBJEXT) \
@@ -652,6 +652,8 @@
 group.o: group.c ../config.h util.h group-private.h group.h window.h \
 	screen.h display.h eventqueue.h common.h ui.h tabpopup.h \
 	stack.h iconcache.h group-props.h
+history.o: history.c ../config.h history.h main.h util.h display.h \
+	workspace.h window.h
 iconcache.o: iconcache.c ../config.h iconcache.h screen.h display.h \
 	eventqueue.h common.h ui.h tabpopup.h errors.h util.h
 keybindings.o: keybindings.c ../config.h keybindings.h display.h \
diff -r -N -u metacity-2.8.1/src/Makefile.in~ metacity-2.8.1-new/src/Makefile.in~
--- metacity-2.8.1/src/Makefile.in~	1969-12-31 19:00:00.000000000 -0500
+++ metacity-2.8.1-new/src/Makefile.in~	2004-05-04 21:16:37.000000000 -0400
@@ -0,0 +1,841 @@
+# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+AR = @AR@
+AS = @AS@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+DATADIRNAME = @DATADIRNAME@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+GCJ = @GCJ@
+GCJFLAGS = @GCJFLAGS@
+GCONFTOOL = @GCONFTOOL@
+GCONF_SCHEMA_CONFIG_SOURCE = @GCONF_SCHEMA_CONFIG_SOURCE@
+GCONF_SCHEMA_FILE_DIR = @GCONF_SCHEMA_FILE_DIR@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+HOST_ALIAS = @HOST_ALIAS@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_CAVES_RULE = @INTLTOOL_CAVES_RULE@
+INTLTOOL_DESKTOP_RULE = @INTLTOOL_DESKTOP_RULE@
+INTLTOOL_DIRECTORY_RULE = @INTLTOOL_DIRECTORY_RULE@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_KBD_RULE = @INTLTOOL_KBD_RULE@
+INTLTOOL_KEYS_RULE = @INTLTOOL_KEYS_RULE@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_OAF_RULE = @INTLTOOL_OAF_RULE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_PONG_RULE = @INTLTOOL_PONG_RULE@
+INTLTOOL_PROP_RULE = @INTLTOOL_PROP_RULE@
+INTLTOOL_SCHEMAS_RULE = @INTLTOOL_SCHEMAS_RULE@
+INTLTOOL_SERVER_RULE = @INTLTOOL_SERVER_RULE@
+INTLTOOL_SHEET_RULE = @INTLTOOL_SHEET_RULE@
+INTLTOOL_SOUNDLIST_RULE = @INTLTOOL_SOUNDLIST_RULE@
+INTLTOOL_THEME_RULE = @INTLTOOL_THEME_RULE@
+INTLTOOL_UI_RULE = @INTLTOOL_UI_RULE@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_XAM_RULE = @INTLTOOL_XAM_RULE@
+INTLTOOL_XML_RULE = @INTLTOOL_XML_RULE@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PKG_CONFIG = @PKG_CONFIG@
+POFILES = @POFILES@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+RANLIB = @RANLIB@
+RC = @RC@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+
+lib_LTLIBRARIES = libmetacity-private.la
+
+SUBDIRS = wm-tester tools themes
+
+INCLUDES = @METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DMETACITY_LIBDIR=\"$(libdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" -DMETACITY_PKGDATADIR=\"$(pkgdatadir)\" -DMETACITY_DATADIR=\"$(datadir)\" -DG_LOG_DOMAIN=\"metacity\" -DSN_API_NOT_YET_FROZEN=1
+
+EGGFILES =  	eggaccelerators.c		eggaccelerators.h
+
+
+metacity_SOURCES =  	async-getprop.c					async-getprop.h					bell.h						bell.c						common.h					compositor.c					compositor.h					constraints.c					constraints.h					core.c						core.h						delete.c					display.c					display.h					draw-workspace.c				draw-workspace.h				effects.c					effects.h					errors.c					errors.h					eventqueue.c					eventqueue.h					fixedtip.c					fixedtip.h					frame.c						frame.h						frames.c					frames.h					gradient.c					gradient.h					group.c						group.h						group-private.h					group-props.c					group-props.h					iconcache.c					iconcache.h					inlinepixbufs.h					keybindings.c					keybindings.h					main.c						main.h						menu.c						menu.h						metaaccellabel.c				metaaccellabel.h				metacity-Xatomtype.h				place.c						place.h						prefs.c						prefs.h						resizepopup.c					resizepopup.h					screen.c					screen.h					session.c					session.h					stack.c						stack.h						tabpopup.c					tabpopup.h					theme.c						theme.h						theme-parser.c					theme-parser.h					themewidget.c					themewidget.h					ui.c						ui.h						util.c						util.h						window.c					window.h					window-props.c					window-props.h					workspace.c					workspace.h					xprops.c					xprops.h					$(EGGFILES)
+
+
+libmetacity_private_la_SOURCES =  	gradient.c			gradient.h			preview-widget.c		preview-widget.h		theme.c				theme.h				theme-parser.c			theme-parser.h			util.c				util.h				common.h
+
+
+libmetacity_private_la_LDFLAGS = -no-undefined
+libmetacity_private_la_LIBADD = @METACITY_LIBS@
+
+libmetacityincludedir = $(includedir)/metacity-1/metacity-private
+
+libmetacityinclude_HEADERS =  	common.h			gradient.h			preview-widget.h		theme.h				theme-parser.h			util.h
+
+
+metacity_theme_viewer_SOURCES =  	theme-viewer.c
+
+
+metacity_dialog_SOURCES =  	metacity-dialog.c
+
+
+bin_PROGRAMS = metacity metacity-theme-viewer
+libexec_PROGRAMS = metacity-dialog
+
+EFENCE = 
+metacity_LDADD = @METACITY_LIBS@ $(EFENCE)
+metacity_theme_viewer_LDADD = @METACITY_LIBS@ libmetacity-private.la
+metacity_dialog_LDADD = @METACITY_LIBS@
+
+testgradient_SOURCES = gradient.h gradient.c testgradient.c
+testasyncgetprop_SOURCES = async-getprop.h async-getprop.c testasyncgetprop.c
+
+noinst_PROGRAMS = testgradient testasyncgetprop
+
+testgradient_LDADD = @METACITY_LIBS@
+testasyncgetprop_LDADD = @METACITY_LIBS@
+
+desktopfilesdir = $(datadir)/gnome/wm-properties
+desktopfiles_in_files = metacity.desktop.in
+desktopfiles_files = $(desktopfiles_in_files:.desktop.in=.desktop)
+desktopfiles_DATA = $(desktopfiles_files)
+
+schemadir = @GCONF_SCHEMA_FILE_DIR@
+schema_in_files = metacity.schemas.in
+schema_DATA = $(schema_in_files:.schemas.in=.schemas)
+
+IMAGES = default_icon.png stock_maximize.png stock_minimize.png stock_delete.png
+VARIABLES = default_icon_data $(srcdir)/default_icon.png           stock_maximize_data $(srcdir)/stock_maximize.png           stock_minimize_data $(srcdir)/stock_minimize.png           stock_delete_data $(srcdir)/stock_delete.png
+
+
+BUILT_SOURCES = inlinepixbufs.h
+CLEANFILES = inlinepixbufs.h
+
+pkgconfigdir = $(libdir)/pkgconfig
+
+pkgconfig_DATA = libmetacity-private.pc
+
+EXTRA_DIST = $(desktopfiles_files) 		$(IMAGES) $(schema_DATA) 		update-from-egg.sh			$(desktopfiles_in_files)		$(schema_in_files)			libmetacity-private.pc.in
+
+
+EGGDIR = $(srcdir)/../../libegg/libegg
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = ../config.h
+CONFIG_CLEAN_FILES =  libmetacity-private.pc
+LTLIBRARIES =  $(lib_LTLIBRARIES)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir) -I..
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+X_CFLAGS = @X_CFLAGS@
+X_LIBS = @X_LIBS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+libmetacity_private_la_DEPENDENCIES = 
+libmetacity_private_la_OBJECTS =  gradient.lo preview-widget.lo theme.lo \
+theme-parser.lo util.lo
+bin_PROGRAMS =  metacity$(EXEEXT) metacity-theme-viewer$(EXEEXT)
+libexec_PROGRAMS =  metacity-dialog$(EXEEXT)
+noinst_PROGRAMS =  testgradient$(EXEEXT) testasyncgetprop$(EXEEXT)
+PROGRAMS =  $(bin_PROGRAMS) $(libexec_PROGRAMS) $(noinst_PROGRAMS)
+
+metacity_OBJECTS =  async-getprop.$(OBJEXT) bell.$(OBJEXT) \
+compositor.$(OBJEXT) constraints.$(OBJEXT) core.$(OBJEXT) \
+delete.$(OBJEXT) display.$(OBJEXT) draw-workspace.$(OBJEXT) \
+effects.$(OBJEXT) errors.$(OBJEXT) eventqueue.$(OBJEXT) \
+fixedtip.$(OBJEXT) frame.$(OBJEXT) frames.$(OBJEXT) gradient.$(OBJEXT) \
+group.$(OBJEXT) group-props.$(OBJEXT) iconcache.$(OBJEXT) \
+keybindings.$(OBJEXT) main.$(OBJEXT) menu.$(OBJEXT) \
+metaaccellabel.$(OBJEXT) place.$(OBJEXT) prefs.$(OBJEXT) \
+resizepopup.$(OBJEXT) screen.$(OBJEXT) session.$(OBJEXT) \
+stack.$(OBJEXT) tabpopup.$(OBJEXT) theme.$(OBJEXT) \
+theme-parser.$(OBJEXT) themewidget.$(OBJEXT) ui.$(OBJEXT) \
+util.$(OBJEXT) window.$(OBJEXT) window-props.$(OBJEXT) \
+workspace.$(OBJEXT) xprops.$(OBJEXT) eggaccelerators.$(OBJEXT)
+metacity_DEPENDENCIES = 
+metacity_LDFLAGS = 
+metacity_theme_viewer_OBJECTS =  theme-viewer.$(OBJEXT)
+metacity_theme_viewer_DEPENDENCIES =  libmetacity-private.la
+metacity_theme_viewer_LDFLAGS = 
+metacity_dialog_OBJECTS =  metacity-dialog.$(OBJEXT)
+metacity_dialog_DEPENDENCIES = 
+metacity_dialog_LDFLAGS = 
+testgradient_OBJECTS =  gradient.$(OBJEXT) testgradient.$(OBJEXT)
+testgradient_DEPENDENCIES = 
+testgradient_LDFLAGS = 
+testasyncgetprop_OBJECTS =  async-getprop.$(OBJEXT) \
+testasyncgetprop.$(OBJEXT)
+testasyncgetprop_DEPENDENCIES = 
+testasyncgetprop_LDFLAGS = 
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+DATA =  $(desktopfiles_DATA) $(pkgconfig_DATA) $(schema_DATA)
+
+HEADERS =  $(libmetacityinclude_HEADERS)
+
+DIST_COMMON =  Makefile.am Makefile.in libmetacity-private.pc.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+SOURCES = $(libmetacity_private_la_SOURCES) $(metacity_SOURCES) $(metacity_theme_viewer_SOURCES) $(metacity_dialog_SOURCES) $(testgradient_SOURCES) $(testasyncgetprop_SOURCES)
+OBJECTS = $(libmetacity_private_la_OBJECTS) $(metacity_OBJECTS) $(metacity_theme_viewer_OBJECTS) $(metacity_dialog_OBJECTS) $(testgradient_OBJECTS) $(testasyncgetprop_OBJECTS)
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .lo .o .obj .s
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) 
+	cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps src/Makefile
+
+Makefile: $(srcdir)/Makefile.in  $(top_builddir)/config.status
+	cd $(top_builddir) \
+	  && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+libmetacity-private.pc: $(top_builddir)/config.status libmetacity-private.pc.in
+	cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+mostlyclean-libLTLIBRARIES:
+
+clean-libLTLIBRARIES:
+	-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+
+distclean-libLTLIBRARIES:
+
+maintainer-clean-libLTLIBRARIES:
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(libdir)
+	@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo "$(LIBTOOL)  --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p"; \
+	    $(LIBTOOL)  --mode=install $(INSTALL) $$p $(DESTDIR)$(libdir)/$$p; \
+	  else :; fi; \
+	done
+
+uninstall-libLTLIBRARIES:
+	@$(NORMAL_UNINSTALL)
+	list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+	  $(LIBTOOL)  --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \
+	done
+
+.c.o:
+	$(COMPILE) -c $<
+
+# FIXME: We should only use cygpath when building on Windows,
+# and only if it is available.
+.c.obj:
+	$(COMPILE) -c `cygpath -w $<`
+
+.s.o:
+	$(COMPILE) -c $<
+
+.S.o:
+	$(COMPILE) -c $<
+
+mostlyclean-compile:
+	-rm -f *.o core *.core
+	-rm -f *.$(OBJEXT)
+
+clean-compile:
+
+distclean-compile:
+	-rm -f *.tab.c
+
+maintainer-clean-compile:
+
+.c.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+.s.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+.S.lo:
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+distclean-libtool:
+
+maintainer-clean-libtool:
+
+libmetacity-private.la: $(libmetacity_private_la_OBJECTS) $(libmetacity_private_la_DEPENDENCIES)
+	$(LINK) -rpath $(libdir) $(libmetacity_private_la_LDFLAGS) $(libmetacity_private_la_OBJECTS) $(libmetacity_private_la_LIBADD) $(LIBS)
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+	-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(bindir)
+	@list='$(bin_PROGRAMS)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo " $(LIBTOOL)  --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+	    $(LIBTOOL)  --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+	  else :; fi; \
+	done
+
+uninstall-binPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	list='$(bin_PROGRAMS)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+	done
+
+mostlyclean-libexecPROGRAMS:
+
+clean-libexecPROGRAMS:
+	-test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS)
+
+distclean-libexecPROGRAMS:
+
+maintainer-clean-libexecPROGRAMS:
+
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(libexecdir)
+	@list='$(libexec_PROGRAMS)'; for p in $$list; do \
+	  if test -f $$p; then \
+	    echo " $(LIBTOOL)  --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(libexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+	    $(LIBTOOL)  --mode=install $(INSTALL_PROGRAM) $$p $(DESTDIR)$(libexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+	  else :; fi; \
+	done
+
+uninstall-libexecPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	list='$(libexec_PROGRAMS)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(libexecdir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+	done
+
+mostlyclean-noinstPROGRAMS:
+
+clean-noinstPROGRAMS:
+	-test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS)
+
+distclean-noinstPROGRAMS:
+
+maintainer-clean-noinstPROGRAMS:
+
+metacity$(EXEEXT): $(metacity_OBJECTS) $(metacity_DEPENDENCIES)
+	@rm -f metacity$(EXEEXT)
+	$(LINK) $(metacity_LDFLAGS) $(metacity_OBJECTS) $(metacity_LDADD) $(LIBS)
+
+metacity-theme-viewer$(EXEEXT): $(metacity_theme_viewer_OBJECTS) $(metacity_theme_viewer_DEPENDENCIES)
+	@rm -f metacity-theme-viewer$(EXEEXT)
+	$(LINK) $(metacity_theme_viewer_LDFLAGS) $(metacity_theme_viewer_OBJECTS) $(metacity_theme_viewer_LDADD) $(LIBS)
+
+metacity-dialog$(EXEEXT): $(metacity_dialog_OBJECTS) $(metacity_dialog_DEPENDENCIES)
+	@rm -f metacity-dialog$(EXEEXT)
+	$(LINK) $(metacity_dialog_LDFLAGS) $(metacity_dialog_OBJECTS) $(metacity_dialog_LDADD) $(LIBS)
+
+testgradient$(EXEEXT): $(testgradient_OBJECTS) $(testgradient_DEPENDENCIES)
+	@rm -f testgradient$(EXEEXT)
+	$(LINK) $(testgradient_LDFLAGS) $(testgradient_OBJECTS) $(testgradient_LDADD) $(LIBS)
+
+testasyncgetprop$(EXEEXT): $(testasyncgetprop_OBJECTS) $(testasyncgetprop_DEPENDENCIES)
+	@rm -f testasyncgetprop$(EXEEXT)
+	$(LINK) $(testasyncgetprop_LDFLAGS) $(testasyncgetprop_OBJECTS) $(testasyncgetprop_LDADD) $(LIBS)
+
+install-desktopfilesDATA: $(desktopfiles_DATA)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(desktopfilesdir)
+	@list='$(desktopfiles_DATA)'; for p in $$list; do \
+	  if test -f $(srcdir)/$$p; then \
+	    echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(desktopfilesdir)/$$p"; \
+	    $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(desktopfilesdir)/$$p; \
+	  else if test -f $$p; then \
+	    echo " $(INSTALL_DATA) $$p $(DESTDIR)$(desktopfilesdir)/$$p"; \
+	    $(INSTALL_DATA) $$p $(DESTDIR)$(desktopfilesdir)/$$p; \
+	  fi; fi; \
+	done
+
+uninstall-desktopfilesDATA:
+	@$(NORMAL_UNINSTALL)
+	list='$(desktopfiles_DATA)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(desktopfilesdir)/$$p; \
+	done
+
+install-pkgconfigDATA: $(pkgconfig_DATA)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(pkgconfigdir)
+	@list='$(pkgconfig_DATA)'; for p in $$list; do \
+	  if test -f $(srcdir)/$$p; then \
+	    echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgconfigdir)/$$p"; \
+	    $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(pkgconfigdir)/$$p; \
+	  else if test -f $$p; then \
+	    echo " $(INSTALL_DATA) $$p $(DESTDIR)$(pkgconfigdir)/$$p"; \
+	    $(INSTALL_DATA) $$p $(DESTDIR)$(pkgconfigdir)/$$p; \
+	  fi; fi; \
+	done
+
+uninstall-pkgconfigDATA:
+	@$(NORMAL_UNINSTALL)
+	list='$(pkgconfig_DATA)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(pkgconfigdir)/$$p; \
+	done
+
+install-schemaDATA: $(schema_DATA)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(schemadir)
+	@list='$(schema_DATA)'; for p in $$list; do \
+	  if test -f $(srcdir)/$$p; then \
+	    echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(schemadir)/$$p"; \
+	    $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(schemadir)/$$p; \
+	  else if test -f $$p; then \
+	    echo " $(INSTALL_DATA) $$p $(DESTDIR)$(schemadir)/$$p"; \
+	    $(INSTALL_DATA) $$p $(DESTDIR)$(schemadir)/$$p; \
+	  fi; fi; \
+	done
+
+uninstall-schemaDATA:
+	@$(NORMAL_UNINSTALL)
+	list='$(schema_DATA)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(schemadir)/$$p; \
+	done
+
+install-libmetacityincludeHEADERS: $(libmetacityinclude_HEADERS)
+	@$(NORMAL_INSTALL)
+	$(mkinstalldirs) $(DESTDIR)$(libmetacityincludedir)
+	@list='$(libmetacityinclude_HEADERS)'; for p in $$list; do \
+	  if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \
+	  echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(libmetacityincludedir)/$$p"; \
+	  $(INSTALL_DATA) $$d$$p $(DESTDIR)$(libmetacityincludedir)/$$p; \
+	done
+
+uninstall-libmetacityincludeHEADERS:
+	@$(NORMAL_UNINSTALL)
+	list='$(libmetacityinclude_HEADERS)'; for p in $$list; do \
+	  rm -f $(DESTDIR)$(libmetacityincludedir)/$$p; \
+	done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+
+@SET_MAKE@
+
+all-recursive install-data-recursive install-exec-recursive \
+installdirs-recursive install-recursive uninstall-recursive  \
+check-recursive installcheck-recursive info-recursive dvi-recursive:
+	@set fnord $(MAKEFLAGS); amf=$$2; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+	@set fnord $(MAKEFLAGS); amf=$$2; \
+	dot_seen=no; \
+	rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \
+	  rev="$$subdir $$rev"; \
+	  test "$$subdir" != "." || dot_seen=yes; \
+	done; \
+	test "$$dot_seen" = "no" && rev=". $$rev"; \
+	target=`echo $@ | sed s/-recursive//`; \
+	for subdir in $$rev; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	   || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+	done && test -z "$$fail"
+tags-recursive:
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+	done
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	here=`pwd` && cd $(srcdir) \
+	  && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES)  $(TAGS_DEPENDENCIES) $(LISP)
+	tags=; \
+	here=`pwd`; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+   if test "$$subdir" = .; then :; else \
+	    test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \
+   fi; \
+	done; \
+	list='$(SOURCES) $(HEADERS)'; \
+	unique=`for i in $$list; do echo $$i; done | \
+	  awk '    { files[$$0] = 1; } \
+	       END { for (i in files) print i; }'`; \
+	test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+	  || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags  $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+	-rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = src
+
+distdir: $(DISTFILES)
+	@for file in $(DISTFILES); do \
+	  d=$(srcdir); \
+	  if test -d $$d/$$file; then \
+	    cp -pr $$d/$$file $(distdir)/$$file; \
+	  else \
+	    test -f $(distdir)/$$file \
+	    || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+	    || cp -p $$d/$$file $(distdir)/$$file || :; \
+	  fi; \
+	done
+	for subdir in $(SUBDIRS); do \
+	  if test "$$subdir" = .; then :; else \
+	    test -d $(distdir)/$$subdir \
+	    || mkdir $(distdir)/$$subdir \
+	    || exit 1; \
+	    chmod 777 $(distdir)/$$subdir; \
+	    (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(top_distdir) distdir=../$(distdir)/$$subdir distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+async-getprop.o: async-getprop.c async-getprop.h
+bell.o: bell.c ../config.h bell.h display.h eventqueue.h common.h \
+	frame.h window.h screen.h ui.h tabpopup.h util.h stack.h \
+	iconcache.h prefs.h
+compositor.o: compositor.c ../config.h compositor.h util.h display.h \
+	eventqueue.h common.h screen.h ui.h tabpopup.h errors.h \
+	window.h stack.h iconcache.h frame.h
+constraints.o: constraints.c ../config.h constraints.h util.h window.h \
+	screen.h display.h eventqueue.h common.h ui.h tabpopup.h \
+	stack.h iconcache.h frame.h workspace.h place.h
+core.o: core.c ../config.h core.h common.h frame.h window.h screen.h \
+	display.h eventqueue.h ui.h tabpopup.h util.h stack.h \
+	iconcache.h workspace.h prefs.h
+delete.o: delete.c ../config.h util.h window.h screen.h display.h \
+	eventqueue.h common.h ui.h tabpopup.h stack.h iconcache.h \
+	errors.h workspace.h
+display.o: display.c ../config.h display.h eventqueue.h common.h util.h \
+	main.h screen.h ui.h tabpopup.h window.h stack.h iconcache.h \
+	window-props.h group-props.h group.h frame.h errors.h \
+	keybindings.h prefs.h resizepopup.h xprops.h workspace.h bell.h \
+	effects.h compositor.h
+draw-workspace.o: draw-workspace.c draw-workspace.h
+effects.o: effects.c ../config.h effects.h util.h screen.h display.h \
+	eventqueue.h common.h ui.h tabpopup.h
+eggaccelerators.o: eggaccelerators.c eggaccelerators.h
+errors.o: errors.c ../config.h errors.h util.h display.h eventqueue.h \
+	common.h
+eventqueue.o: eventqueue.c eventqueue.h
+fixedtip.o: fixedtip.c ../config.h fixedtip.h
+frame.o: frame.c ../config.h frame.h window.h screen.h display.h \
+	eventqueue.h common.h ui.h tabpopup.h util.h stack.h \
+	iconcache.h bell.h errors.h keybindings.h
+frames.o: frames.c ../config.h frames.h common.h theme.h gradient.h \
+	util.h core.h menu.h fixedtip.h prefs.h
+gradient.o: gradient.c gradient.h util.h
+group-props.o: group-props.c ../config.h group-props.h group.h window.h \
+	screen.h display.h eventqueue.h common.h ui.h tabpopup.h util.h \
+	stack.h iconcache.h group-private.h xprops.h
+group.o: group.c ../config.h util.h group-private.h group.h window.h \
+	screen.h display.h eventqueue.h common.h ui.h tabpopup.h \
+	stack.h iconcache.h group-props.h
+iconcache.o: iconcache.c ../config.h iconcache.h screen.h display.h \
+	eventqueue.h common.h ui.h tabpopup.h errors.h util.h
+keybindings.o: keybindings.c ../config.h keybindings.h display.h \
+	eventqueue.h common.h window.h screen.h ui.h tabpopup.h util.h \
+	stack.h iconcache.h workspace.h errors.h frame.h place.h \
+	prefs.h effects.h
+main.o: main.c ../config.h main.h util.h display.h eventqueue.h common.h \
+	errors.h ui.h tabpopup.h session.h window.h screen.h stack.h \
+	iconcache.h prefs.h
+menu.o: menu.c ../config.h menu.h frames.h common.h theme.h gradient.h \
+	main.h util.h core.h themewidget.h metaaccellabel.h
+metaaccellabel.o: metaaccellabel.c ../config.h metaaccellabel.h common.h \
+	util.h
+metacity-dialog.o: metacity-dialog.c ../config.h
+place.o: place.c ../config.h place.h window.h screen.h display.h \
+	eventqueue.h common.h ui.h tabpopup.h util.h stack.h \
+	iconcache.h frame.h workspace.h prefs.h
+prefs.o: prefs.c ../config.h prefs.h common.h ui.h tabpopup.h util.h
+preview-widget.lo preview-widget.o : preview-widget.c preview-widget.h \
+	theme.h gradient.h common.h inlinepixbufs.h
+resizepopup.o: resizepopup.c ../config.h resizepopup.h common.h util.h
+screen.o: screen.c ../config.h screen.h display.h eventqueue.h common.h \
+	ui.h tabpopup.h util.h errors.h window.h stack.h iconcache.h \
+	frame.h prefs.h workspace.h keybindings.h xprops.h compositor.h
+session.o: session.c ../config.h session.h window.h screen.h display.h \
+	eventqueue.h common.h ui.h tabpopup.h util.h stack.h \
+	iconcache.h main.h workspace.h
+stack.o: stack.c ../config.h stack.h screen.h display.h eventqueue.h \
+	common.h ui.h tabpopup.h window.h util.h iconcache.h errors.h \
+	frame.h group.h prefs.h workspace.h
+tabpopup.o: tabpopup.c ../config.h util.h core.h common.h tabpopup.h \
+	workspace.h window.h screen.h display.h eventqueue.h ui.h \
+	stack.h iconcache.h draw-workspace.h frame.h
+testasyncgetprop.o: testasyncgetprop.c async-getprop.h
+testgradient.o: testgradient.c gradient.h
+theme-parser.o: theme-parser.c ../config.h theme-parser.h theme.h \
+	gradient.h common.h util.h
+theme-viewer.o: theme-viewer.c ../config.h util.h theme.h gradient.h \
+	common.h theme-parser.h preview-widget.h
+theme.o: theme.c ../config.h theme.h gradient.h common.h theme-parser.h \
+	util.h
+themewidget.o: themewidget.c themewidget.h theme.h gradient.h common.h
+ui.o: ui.c ../config.h prefs.h common.h ui.h tabpopup.h frames.h theme.h \
+	gradient.h util.h menu.h core.h eggaccelerators.h \
+	inlinepixbufs.h
+util.o: util.c ../config.h util.h main.h
+window-props.o: window-props.c ../config.h window-props.h window.h \
+	screen.h display.h eventqueue.h common.h ui.h tabpopup.h util.h \
+	stack.h iconcache.h xprops.h frame.h group.h
+window.o: window.c ../config.h window.h screen.h display.h eventqueue.h \
+	common.h ui.h tabpopup.h util.h stack.h iconcache.h frame.h \
+	errors.h workspace.h keybindings.h place.h session.h effects.h \
+	prefs.h resizepopup.h xprops.h group.h window-props.h \
+	constraints.h
+workspace.o: workspace.c ../config.h workspace.h window.h screen.h \
+	display.h eventqueue.h common.h ui.h tabpopup.h util.h stack.h \
+	iconcache.h errors.h prefs.h
+xprops.o: xprops.c ../config.h xprops.h display.h eventqueue.h common.h \
+	errors.h util.h async-getprop.h ui.h tabpopup.h \
+	metacity-Xatomtype.h window.h screen.h stack.h iconcache.h
+
+info-am:
+info: info-recursive
+dvi-am:
+dvi: dvi-recursive
+check-am: all-am
+check: check-recursive
+installcheck-am:
+installcheck: installcheck-recursive
+install-exec-am: install-libLTLIBRARIES install-binPROGRAMS \
+		install-libexecPROGRAMS
+install-exec: install-exec-recursive
+
+install-data-am: install-desktopfilesDATA install-pkgconfigDATA \
+		install-schemaDATA install-libmetacityincludeHEADERS \
+		install-data-local
+install-data: install-data-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-recursive
+uninstall-am: uninstall-libLTLIBRARIES uninstall-binPROGRAMS \
+		uninstall-libexecPROGRAMS uninstall-desktopfilesDATA \
+		uninstall-pkgconfigDATA uninstall-schemaDATA \
+		uninstall-libmetacityincludeHEADERS
+uninstall: uninstall-recursive
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) $(HEADERS)
+all-redirect: all-recursive
+install-strip:
+	$(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs: installdirs-recursive
+installdirs-am:
+	$(mkinstalldirs)  $(DESTDIR)$(libdir) $(DESTDIR)$(bindir) \
+		$(DESTDIR)$(libexecdir) $(DESTDIR)$(desktopfilesdir) \
+		$(DESTDIR)$(pkgconfigdir) $(DESTDIR)$(schemadir) \
+		$(DESTDIR)$(libmetacityincludedir)
+
+
+mostlyclean-generic:
+
+clean-generic:
+	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+	-rm -f Makefile $(CONFIG_CLEAN_FILES)
+	-rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+mostlyclean-am:  mostlyclean-libLTLIBRARIES mostlyclean-compile \
+		mostlyclean-libtool mostlyclean-binPROGRAMS \
+		mostlyclean-libexecPROGRAMS mostlyclean-noinstPROGRAMS \
+		mostlyclean-tags mostlyclean-generic
+
+mostlyclean: mostlyclean-recursive
+
+clean-am:  clean-libLTLIBRARIES clean-compile clean-libtool \
+		clean-binPROGRAMS clean-libexecPROGRAMS \
+		clean-noinstPROGRAMS clean-tags clean-generic \
+		mostlyclean-am
+
+clean: clean-recursive
+
+distclean-am:  distclean-libLTLIBRARIES distclean-compile \
+		distclean-libtool distclean-binPROGRAMS \
+		distclean-libexecPROGRAMS distclean-noinstPROGRAMS \
+		distclean-tags distclean-generic clean-am
+	-rm -f libtool
+
+distclean: distclean-recursive
+
+maintainer-clean-am:  maintainer-clean-libLTLIBRARIES \
+		maintainer-clean-compile maintainer-clean-libtool \
+		maintainer-clean-binPROGRAMS \
+		maintainer-clean-libexecPROGRAMS \
+		maintainer-clean-noinstPROGRAMS maintainer-clean-tags \
+		maintainer-clean-generic distclean-am
+	@echo "This command is intended for maintainers to use;"
+	@echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-recursive
+
+.PHONY: mostlyclean-libLTLIBRARIES distclean-libLTLIBRARIES \
+clean-libLTLIBRARIES maintainer-clean-libLTLIBRARIES \
+uninstall-libLTLIBRARIES install-libLTLIBRARIES mostlyclean-compile \
+distclean-compile clean-compile maintainer-clean-compile \
+mostlyclean-libtool distclean-libtool clean-libtool \
+maintainer-clean-libtool mostlyclean-binPROGRAMS distclean-binPROGRAMS \
+clean-binPROGRAMS maintainer-clean-binPROGRAMS uninstall-binPROGRAMS \
+install-binPROGRAMS mostlyclean-libexecPROGRAMS \
+distclean-libexecPROGRAMS clean-libexecPROGRAMS \
+maintainer-clean-libexecPROGRAMS uninstall-libexecPROGRAMS \
+install-libexecPROGRAMS mostlyclean-noinstPROGRAMS \
+distclean-noinstPROGRAMS clean-noinstPROGRAMS \
+maintainer-clean-noinstPROGRAMS uninstall-desktopfilesDATA \
+install-desktopfilesDATA uninstall-pkgconfigDATA install-pkgconfigDATA \
+uninstall-schemaDATA install-schemaDATA \
+uninstall-libmetacityincludeHEADERS install-libmetacityincludeHEADERS \
+install-data-recursive uninstall-data-recursive install-exec-recursive \
+uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \
+all-recursive check-recursive installcheck-recursive info-recursive \
+dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \
+maintainer-clean-recursive tags tags-recursive mostlyclean-tags \
+distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-local install-data-am install-data install-am \
+install uninstall-am uninstall all-redirect all-am all installdirs-am \
+installdirs mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+@INTLTOOL_DESKTOP_RULE@
+
+@INTLTOOL_SCHEMAS_RULE@
+
+@GCONF_SCHEMAS_INSTALL_TRUE@install-data-local:
+@GCONF_SCHEMAS_INSTALL_TRUE@	GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(schema_DATA)
+@GCONF_SCHEMAS_INSTALL_FALSE@install-data-local:
+
+inlinepixbufs.h: $(IMAGES)
+	   $(GDK_PIXBUF_CSOURCE) --raw --build-list $(VARIABLES) >$(srcdir)/inlinepixbufs.h
+
+regenerate-built-sources:
+	EGGFILES="$(EGGFILES)" EGGDIR="$(EGGDIR)" $(srcdir)/update-from-egg.sh
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff -r -N -u metacity-2.8.1/src/place.c metacity-2.8.1-new/src/place.c
--- metacity-2.8.1/src/place.c	2004-03-08 10:22:08.000000000 -0500
+++ metacity-2.8.1-new/src/place.c	2004-09-10 22:35:46.459267915 -0400
@@ -24,6 +24,7 @@
 #include <config.h>
 
 #include "place.h"
+#include "history.h"
 #include "workspace.h"
 #include "prefs.h"
 #include <gdk/gdkregion.h>
@@ -391,6 +392,97 @@
 	  (rect->y + rect->height <= work_area->y + work_area->height));
 }
 
+/* Lookup the window position used before and try to use that
+ * again.  
+ * 
+ * We need to watch out for windows already placed in that
+ * position and fail if we find one.
+ */
+static gboolean
+find_last_placement (MetaWindow *window,
+		     MetaFrameGeometry *fgeom,
+		     /* visible windows on relevant workspaces */
+		     GList      *windows,
+		     int         x,
+		     int         y,
+		     int        *new_x,
+		     int        *new_y)
+{
+  MetaWindowHistoryInfo *old_place = NULL;
+  int place_x, place_y;
+  int x_threshold, y_threshold;
+  GList *tmp;
+
+  meta_history_next_window_full (windows, window);
+  old_place = meta_history_lookup (window);
+  
+  /* If we can't find any data, just return. */
+  if (!old_place) return FALSE;
+
+  place_x = old_place->rect.x;
+  place_y = old_place->rect.y;
+
+  /* Now we need to scan through all the windows looking 
+   * for those which are close to us, return FALSE if we
+   * find something like that.
+   */
+
+  if (fgeom)
+    {
+      x_threshold = MAX (fgeom->left_width, 15);
+      y_threshold = MAX (fgeom->top_height, 15);
+    }
+  else
+    {
+      x_threshold = 15;
+      y_threshold = 15;
+    }
+
+  tmp = windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *w;
+      int wx, wy;
+
+      w = tmp->data;
+      
+      /* we want frame position, not window position */
+      if (w->frame)
+        {
+          wx = w->frame->rect.x;
+          wy = w->frame->rect.y;
+        }
+      else
+        {
+          wx = w->rect.x;
+          wy = w->rect.y;
+        }
+      
+      if (ABS (wx - place_x) <= x_threshold &&
+          ABS (wy - place_y) <= y_threshold)
+        {
+	  /* Oops.  Something is already there.  Abort! */
+	  return FALSE;
+	}
+      tmp = tmp->next;
+    }
+
+  *new_x = place_x;
+  *new_y = place_y;
+
+#if 1
+  /* This is probably the wrong place to be doing this, but we have 
+   * all the information here.  Now we restore window size and state.
+   */
+  meta_history_restore_window (old_place, window);
+
+#endif
+
+  /* FIXME: Should we change the size of the window as well? */
+
+  return TRUE;
+}
+
 /* Find the leftmost, then topmost, empty area on the workspace
  * that can contain the new window.
  *
@@ -733,7 +825,7 @@
       goto done;
     }
   
-  /* Find windows that matter (not minimized, on same workspace
+  /* Find windows that matter (on same workspace
    * as placed window, may be shaded - if shaded we pretend it isn't
    * for placement purposes)
    */
@@ -748,8 +840,7 @@
       {
         MetaWindow *w = tmp->data;
 
-        if (!w->minimized &&
-            w != window && 
+        if (w != window && 
             meta_window_shares_some_workspace (window, w))
           windows = g_list_prepend (windows, w);
 
@@ -768,6 +859,10 @@
 					 &xineramas_list,
 					 &n_xineramas);
 
+  /* If we already know where the user likes their window, use that. */
+  if (find_last_placement (window, fgeom, windows, x, y, &x, &y))
+    goto done;
+  
   if (find_first_fit (window, fgeom, windows,
                       xineramas_list, n_xineramas,
                       x, y, &x, &y))
diff -r -N -u metacity-2.8.1/src/session.c metacity-2.8.1-new/src/session.c
--- metacity-2.8.1/src/session.c	2004-09-10 23:54:28.449617335 -0400
+++ metacity-2.8.1-new/src/session.c	2004-09-10 22:35:46.460267784 -0400
@@ -680,182 +680,6 @@
  * session manager.
  */
 
-static const char*
-window_type_to_string (MetaWindowType type)
-{
-  switch (type)
-    {
-    case META_WINDOW_NORMAL:
-      return "normal";
-      break;
-    case META_WINDOW_DESKTOP:
-      return "desktop";
-      break;
-    case META_WINDOW_DOCK:
-      return "dock";
-      break;
-    case META_WINDOW_DIALOG:
-      return "dialog";
-      break;
-    case META_WINDOW_MODAL_DIALOG:
-      return "modal_dialog";
-      break;
-    case META_WINDOW_TOOLBAR:
-      return "toolbar";
-      break;
-    case META_WINDOW_MENU:
-      return "menu";
-      break;
-    case META_WINDOW_SPLASHSCREEN:
-      return "splashscreen";
-      break;
-    case META_WINDOW_UTILITY:
-      return "utility";
-      break;
-    }
-
-  return "";
-} 
-
-static MetaWindowType
-window_type_from_string (const char *str)
-{
-  if (strcmp (str, "normal") == 0)
-    return META_WINDOW_NORMAL;
-  else if (strcmp (str, "desktop") == 0)
-    return META_WINDOW_DESKTOP;
-  else if (strcmp (str, "dock") == 0)
-    return META_WINDOW_DOCK;
-  else if (strcmp (str, "dialog") == 0)
-    return META_WINDOW_DIALOG;
-  else if (strcmp (str, "modal_dialog") == 0)
-    return META_WINDOW_MODAL_DIALOG;
-  else if (strcmp (str, "toolbar") == 0)
-    return META_WINDOW_TOOLBAR;
-  else if (strcmp (str, "menu") == 0)
-    return META_WINDOW_MENU;
-  else if (strcmp (str, "utility") == 0)
-    return META_WINDOW_UTILITY;
-  else if (strcmp (str, "splashscreen") == 0)
-    return META_WINDOW_SPLASHSCREEN;
-  else
-    return META_WINDOW_NORMAL;
-}
-
-static const char*
-window_gravity_to_string (int gravity)
-{
-  switch (gravity)
-    {
-    case NorthWestGravity:
-      return "NorthWestGravity";
-      break;
-    case NorthGravity:
-      return "NorthGravity";
-      break;
-    case NorthEastGravity:
-      return "NorthEastGravity";
-      break;
-    case WestGravity:
-      return "WestGravity";
-      break;
-    case CenterGravity:
-      return "CenterGravity";
-      break;
-    case EastGravity:
-      return "EastGravity";
-      break;
-    case SouthWestGravity:
-      return "SouthWestGravity";
-      break;
-    case SouthGravity:
-      return "SouthGravity";
-      break;
-    case SouthEastGravity:
-      return "SouthEastGravity";
-      break;
-    case StaticGravity:
-      return "StaticGravity";
-      break;
-    default:
-      return "NorthWestGravity";
-      break;
-    }
-}
-  
-static int
-window_gravity_from_string (const char *str)
-{
-  if (strcmp (str, "NorthWestGravity") == 0)
-    return NorthWestGravity;
-  else if (strcmp (str, "NorthGravity") == 0)
-    return NorthGravity;
-  else if (strcmp (str, "NorthEastGravity") == 0)
-    return NorthEastGravity;
-  else if (strcmp (str, "WestGravity") == 0)
-    return WestGravity;
-  else if (strcmp (str, "CenterGravity") == 0)
-    return CenterGravity;
-  else if (strcmp (str, "EastGravity") == 0)
-    return EastGravity;
-  else if (strcmp (str, "SouthWestGravity") == 0)
-    return SouthWestGravity;
-  else if (strcmp (str, "SouthGravity") == 0)
-    return SouthGravity;
-  else if (strcmp (str, "SouthEastGravity") == 0)
-    return SouthEastGravity;
-  else if (strcmp (str, "StaticGravity") == 0)
-    return StaticGravity;
-  else
-    return NorthWestGravity;
-}
-
-static char*
-encode_text_as_utf8_markup (const char *text)
-{
-  /* text can be any encoding, and is nul-terminated.
-   * we pretend it's Latin-1 and encode as UTF-8
-   */
-  GString *str;
-  const char *p;
-  char *escaped;
-  
-  str = g_string_new ("");
-
-  p = text;
-  while (*p)
-    {
-      g_string_append_unichar (str, *p);
-      ++p;
-    }
-
-  escaped = g_markup_escape_text (str->str, str->len);
-  g_string_free (str, TRUE);
-  
-  return escaped;
-}
-
-static char*
-decode_text_from_utf8 (const char *text)
-{
-  /* Convert back from the encoded (but not escaped) UTF-8 */
-  GString *str;
-  const char *p;
-
-  str = g_string_new ("");
-  
-  p = text;
-  while (*p)
-    {
-      /* obviously this barfs if the UTF-8 contains chars > 255 */
-      g_string_append_c (str, g_utf8_get_char (p));
-
-      p = g_utf8_next_char (p);
-    }
-
-  return g_string_free (str, FALSE);
-}
-
 static int
 stack_cmp (const void *a,
            const void *b)
@@ -1556,18 +1380,6 @@
    */
 }
 
-static gboolean
-both_null_or_matching (const char *a,
-                       const char *b)
-{
-  if (a == NULL && b == NULL)
-    return TRUE;
-  else if (a && b && strcmp (a, b) == 0)
-    return TRUE;
-  else
-    return FALSE;
-}
-
 static GSList*
 get_possible_matches (MetaWindow *window)
 {
diff -r -N -u metacity-2.8.1/src/util.c metacity-2.8.1-new/src/util.c
--- metacity-2.8.1/src/util.c	2003-12-25 19:05:26.000000000 -0500
+++ metacity-2.8.1-new/src/util.c	2004-09-10 22:35:46.461267652 -0400
@@ -286,6 +286,8 @@
       return "SHAPES";
     case META_DEBUG_COMPOSITOR:
       return "COMPOSITOR";
+    case META_DEBUG_HISTORY:
+      return "HISTORY";
     }
 
   return "WM";
@@ -449,3 +451,62 @@
   return val;
 #endif
 }
+
+gboolean
+both_null_or_matching (const char *a,
+                       const char *b)
+{
+  if (a == NULL && b == NULL)
+    return TRUE;
+  else if (a && b && strcmp (a, b) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+char*
+encode_text_as_utf8_markup (const char *text)
+{
+  /* text can be any encoding, and is nul-terminated.
+   * we pretend it's Latin-1 and encode as UTF-8
+   */
+  GString *str;
+  const char *p;
+  char *escaped;
+  
+  str = g_string_new ("");
+
+  p = text;
+  while (*p)
+    {
+      g_string_append_unichar (str, *p);
+      ++p;
+    }
+
+  escaped = g_markup_escape_text (str->str, str->len);
+  g_string_free (str, TRUE);
+  
+  return escaped;
+}
+
+char*
+decode_text_from_utf8 (const char *text)
+{
+  /* Convert back from the encoded (but not escaped) UTF-8 */
+  GString *str;
+  const char *p;
+
+  str = g_string_new ("");
+  
+  p = text;
+  while (*p)
+    {
+      /* obviously this barfs if the UTF-8 contains chars > 255 */
+      g_string_append_c (str, g_utf8_get_char (p));
+
+      p = g_utf8_next_char (p);
+    }
+
+  return g_string_free (str, FALSE);
+}
+
diff -r -N -u metacity-2.8.1/src/util.h metacity-2.8.1-new/src/util.h
--- metacity-2.8.1/src/util.h	2003-12-25 19:05:26.000000000 -0500
+++ metacity-2.8.1-new/src/util.h	2004-09-10 22:35:46.461267652 -0400
@@ -67,7 +67,8 @@
   META_DEBUG_GROUPS       = 1 << 17,
   META_DEBUG_RESIZING     = 1 << 18,
   META_DEBUG_SHAPES       = 1 << 19,
-  META_DEBUG_COMPOSITOR   = 1 << 20
+  META_DEBUG_COMPOSITOR   = 1 << 20,
+  META_DEBUG_HISTORY      = 1 << 21
 
 } MetaDebugTopic;
 
@@ -82,6 +83,12 @@
                                 gconstpointer v2);
 guint meta_unsigned_long_hash  (gconstpointer v);
 
+gboolean both_null_or_matching (const char            *a,
+				const char            *b);
+
+char* encode_text_as_utf8_markup (const char *text);
+char* decode_text_from_utf8      (const char *text);
+
 void meta_print_backtrace (void);
 
 #include <libintl.h>
diff -r -N -u metacity-2.8.1/src/window.c metacity-2.8.1-new/src/window.c
--- metacity-2.8.1/src/window.c	2004-04-14 14:58:08.000000000 -0400
+++ metacity-2.8.1-new/src/window.c	2004-09-10 22:40:00.734794618 -0400
@@ -31,6 +31,7 @@
 #include "keybindings.h"
 #include "ui.h"
 #include "place.h"
+#include "history.h"
 #include "session.h"
 #include "effects.h"
 #include "prefs.h"
@@ -65,6 +66,7 @@
 static void     update_role               (MetaWindow     *window);
 static void     update_net_wm_type        (MetaWindow     *window);
 static void     update_net_frame_extents  (MetaWindow     *window);
+static void     update_geometry_save_id   (MetaWindow     *window);
 static void     recalc_window_type        (MetaWindow     *window);
 static void     recalc_window_features    (MetaWindow     *window);
 static void     invalidate_work_areas     (MetaWindow     *window);
@@ -483,6 +485,11 @@
   window->role = NULL;
   window->sm_client_id = NULL;
   window->wm_client_machine = NULL;
+
+  window->orig_name = NULL;
+  window->num = -1;
+  window->save_id = NULL;
+
   window->startup_id = NULL;
   
   window->net_wm_pid = -1;
@@ -545,6 +552,7 @@
   update_role (window);
   update_net_wm_type (window);
   meta_window_update_icon_now (window);
+  update_geometry_save_id (window);
 
   if (window->initially_iconic)
     {
@@ -880,6 +888,8 @@
   
   window->unmanaging = TRUE;
 
+  meta_history_forget_window ();
+
   if (window->fullscreen)
     {
       MetaGroup *group;
@@ -1070,6 +1080,7 @@
   g_free (window->role);
   g_free (window->res_class);
   g_free (window->res_name);
+  g_free (window->orig_name);
   g_free (window->title);
   g_free (window->icon_name);
   g_free (window->desc);
@@ -1802,6 +1813,14 @@
 void
 meta_window_minimize (MetaWindow  *window)
 {
+  meta_window_minimize_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_minimize_internal (MetaWindow  *window)
+{
   if (!window->minimized)
     {
       window->minimized = TRUE;
@@ -1830,6 +1849,14 @@
 void
 meta_window_unminimize (MetaWindow  *window)
 {
+  meta_window_unminimize_internal (window);
+  
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unminimize_internal (MetaWindow  *window)
+{
   if (window->minimized)
     {
       window->minimized = FALSE;
@@ -1877,6 +1904,14 @@
 void
 meta_window_maximize (MetaWindow  *window)
 {
+  meta_window_maximize_real (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_maximize_real (MetaWindow  *window)
+{
   if (!window->maximized)
     {
       if (window->shaded)
@@ -1901,6 +1936,14 @@
 void
 meta_window_unmaximize (MetaWindow  *window)
 {
+  meta_window_unmaximize_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unmaximize_internal (MetaWindow  *window)
+{
   if (window->maximized)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
@@ -1952,6 +1995,14 @@
 void
 meta_window_make_fullscreen (MetaWindow  *window)
 {
+  meta_window_make_fullscreen_internal (window);
+  
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_make_fullscreen_internal (MetaWindow  *window)
+{
   if (!window->fullscreen)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
@@ -1982,6 +2033,14 @@
 void
 meta_window_unmake_fullscreen (MetaWindow  *window)
 {
+  meta_window_unmake_fullscreen_internal (window);
+  
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unmake_fullscreen_internal (MetaWindow  *window)
+{
   if (window->fullscreen)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
@@ -2662,12 +2721,18 @@
       need_move_client || need_resize_client)
     {
       int newx, newy;
+
       meta_window_get_position (window, &newx, &newy);
       meta_topic (META_DEBUG_GEOMETRY,
                   "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
                   newx, newy, window->rect.width, window->rect.height,
                   window->user_rect.x, window->user_rect.y,
                   window->user_rect.width, window->user_rect.height);
+
+      if (is_user_action)
+	meta_history_remember_window (window);
+      if (!is_user_action)
+	meta_history_commit_window ();
     }
   else
     {
@@ -2697,6 +2762,7 @@
                                     user_op ? META_USER_MOVE_RESIZE : 0,
                                     NorthWestGravity,
                                     x, y, w, h);
+
 }
 
 void
@@ -3249,6 +3315,14 @@
 void
 meta_window_stick (MetaWindow  *window)
 {
+  meta_window_stick_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_stick_internal (MetaWindow  *window)
+{
   GList *tmp; 
   MetaWorkspace *workspace;
 
@@ -3284,6 +3358,14 @@
 void
 meta_window_unstick (MetaWindow  *window)
 {
+  meta_window_unstick_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unstick_internal (MetaWindow  *window)
+{
   GList *tmp;
   MetaWorkspace *workspace;
 
@@ -4371,6 +4453,12 @@
       meta_window_reload_property (window,
                                    window->display->atom_metacity_update_counter);
     }
+  else if (event->atom == window->display->atom_net_wm_geometry_save_id)
+    {
+      meta_verbose ("Property notify on %s for _NET_WM_GEOMETRY_SAVE_ID\n", window->desc);
+      
+      update_geometry_save_id (window);
+    }
 
   return TRUE;
 }
@@ -4579,6 +4667,34 @@
   recalc_window_features (window);
 }
 
+static void
+update_geometry_save_id (MetaWindow *window)
+{
+  gchar *save_id = NULL;
+
+  if (!meta_prop_get_save_id (window->display, window->xwindow,
+				       window->display->atom_net_wm_geometry_save_id,
+				       &save_id))
+    {
+      meta_verbose ("Window Geometry Save ID wasn't set.  We manage this window.\n");
+      /*      meta_history_clear_window (window);*/
+      return;
+
+    }
+
+ 
+  if (!save_id || strlen (save_id) == 0) 
+    {
+      meta_verbose ("Window Geometry Save ID is empty.  Clearing Window History.\n");
+      meta_history_clear_window (window);
+      return;      
+    }
+
+  meta_history_update_key (window, save_id);
+  
+  meta_verbose ("Window Geometry Save ID updated.\n");
+}
+
 gboolean
 meta_window_get_icon_geometry (MetaWindow    *window,
                                MetaRectangle *rect)
@@ -4639,6 +4755,12 @@
   if (ch.res_name)
     {
       window->res_name = g_strdup (ch.res_name);
+
+      /* This is used by the history system. */
+      if (window->orig_name == NULL)
+	{
+	  window->orig_name = g_strdup (ch.res_name);
+	}
       XFree (ch.res_name);
     }
 
@@ -6913,6 +7035,136 @@
                      &x, &y);
 }
 
+const char*
+window_type_to_string (MetaWindowType type)
+{
+  switch (type)
+    {
+    case META_WINDOW_NORMAL:
+      return "normal";
+      break;
+    case META_WINDOW_DESKTOP:
+      return "desktop";
+      break;
+    case META_WINDOW_DOCK:
+      return "dock";
+      break;
+    case META_WINDOW_DIALOG:
+      return "dialog";
+      break;
+    case META_WINDOW_MODAL_DIALOG:
+      return "modal_dialog";
+      break;
+    case META_WINDOW_TOOLBAR:
+      return "toolbar";
+      break;
+    case META_WINDOW_MENU:
+      return "menu";
+      break;
+    case META_WINDOW_SPLASHSCREEN:
+      return "splashscreen";
+      break;
+    case META_WINDOW_UTILITY:
+      return "utility";
+      break;
+    }
+
+  return "";
+} 
+
+MetaWindowType
+window_type_from_string (const char *str)
+{
+  if (strcmp (str, "normal") == 0)
+    return META_WINDOW_NORMAL;
+  else if (strcmp (str, "desktop") == 0)
+    return META_WINDOW_DESKTOP;
+  else if (strcmp (str, "dock") == 0)
+    return META_WINDOW_DOCK;
+  else if (strcmp (str, "dialog") == 0)
+    return META_WINDOW_DIALOG;
+  else if (strcmp (str, "modal_dialog") == 0)
+    return META_WINDOW_MODAL_DIALOG;
+  else if (strcmp (str, "toolbar") == 0)
+    return META_WINDOW_TOOLBAR;
+  else if (strcmp (str, "menu") == 0)
+    return META_WINDOW_MENU;
+  else if (strcmp (str, "utility") == 0)
+    return META_WINDOW_UTILITY;
+  else if (strcmp (str, "splashscreen") == 0)
+    return META_WINDOW_SPLASHSCREEN;
+  else
+    return META_WINDOW_NORMAL;
+}
+
+const char*
+window_gravity_to_string (int gravity)
+{
+  switch (gravity)
+    {
+    case NorthWestGravity:
+      return "NorthWestGravity";
+      break;
+    case NorthGravity:
+      return "NorthGravity";
+      break;
+    case NorthEastGravity:
+      return "NorthEastGravity";
+      break;
+    case WestGravity:
+      return "WestGravity";
+      break;
+    case CenterGravity:
+      return "CenterGravity";
+      break;
+    case EastGravity:
+      return "EastGravity";
+      break;
+    case SouthWestGravity:
+      return "SouthWestGravity";
+      break;
+    case SouthGravity:
+      return "SouthGravity";
+      break;
+    case SouthEastGravity:
+      return "SouthEastGravity";
+      break;
+    case StaticGravity:
+      return "StaticGravity";
+      break;
+    default:
+      return "NorthWestGravity";
+      break;
+    }
+}
+  
+int
+window_gravity_from_string (const char *str)
+{
+  if (strcmp (str, "NorthWestGravity") == 0)
+    return NorthWestGravity;
+  else if (strcmp (str, "NorthGravity") == 0)
+    return NorthGravity;
+  else if (strcmp (str, "NorthEastGravity") == 0)
+    return NorthEastGravity;
+  else if (strcmp (str, "WestGravity") == 0)
+    return WestGravity;
+  else if (strcmp (str, "CenterGravity") == 0)
+    return CenterGravity;
+  else if (strcmp (str, "EastGravity") == 0)
+    return EastGravity;
+  else if (strcmp (str, "SouthWestGravity") == 0)
+    return SouthWestGravity;
+  else if (strcmp (str, "SouthGravity") == 0)
+    return SouthGravity;
+  else if (strcmp (str, "SouthEastGravity") == 0)
+    return SouthEastGravity;
+  else if (strcmp (str, "StaticGravity") == 0)
+    return StaticGravity;
+  else
+    return NorthWestGravity;
+}
+
 void
 meta_window_update_layer (MetaWindow *window)
 {
diff -r -N -u metacity-2.8.1/src/window.c~ metacity-2.8.1-new/src/window.c~
--- metacity-2.8.1/src/window.c~	1969-12-31 19:00:00.000000000 -0500
+++ metacity-2.8.1-new/src/window.c~	2004-09-10 22:35:46.466266994 -0400
@@ -0,0 +1,7180 @@
+/* Metacity X managed windows */
+
+/* 
+ * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Rob Adams
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "window.h"
+#include "util.h"
+#include "frame.h"
+#include "errors.h"
+#include "workspace.h"
+#include "stack.h"
+#include "keybindings.h"
+#include "ui.h"
+#include "place.h"
+#include "history.h"
+#include "session.h"
+#include "effects.h"
+#include "prefs.h"
+#include "resizepopup.h"
+#include "xprops.h"
+#include "group.h"
+#include "window-props.h"
+#include "constraints.h"
+
+#include <X11/Xatom.h>
+#include <string.h>
+
+#ifdef HAVE_SHAPE
+#include <X11/extensions/shape.h>
+#endif
+
+typedef enum
+{
+  META_IS_CONFIGURE_REQUEST = 1 << 0,
+  META_DO_GRAVITY_ADJUST    = 1 << 1,
+  META_USER_MOVE_RESIZE     = 1 << 2
+} MetaMoveResizeFlags;
+
+static int destroying_windows_disallowed = 0;
+
+
+static void     update_net_wm_state       (MetaWindow     *window);
+static void     update_mwm_hints          (MetaWindow     *window);
+static void     update_wm_class           (MetaWindow     *window);
+static void     update_transient_for      (MetaWindow     *window);
+static void     update_sm_hints           (MetaWindow     *window);
+static void     update_role               (MetaWindow     *window);
+static void     update_net_wm_type        (MetaWindow     *window);
+static void     update_net_frame_extents  (MetaWindow     *window);
+static void     update_geometry_save_id   (MetaWindow     *window);
+static void     recalc_window_type        (MetaWindow     *window);
+static void     recalc_window_features    (MetaWindow     *window);
+static void     invalidate_work_areas     (MetaWindow     *window);
+static void     set_wm_state              (MetaWindow     *window,
+                                           int             state);
+static void     set_net_wm_state          (MetaWindow     *window);
+
+static void     send_configure_notify     (MetaWindow     *window);
+static gboolean process_property_notify   (MetaWindow     *window,
+                                           XPropertyEvent *event);
+static void     meta_window_show          (MetaWindow     *window);
+static void     meta_window_hide          (MetaWindow     *window);
+
+static void     meta_window_save_rect         (MetaWindow    *window);
+
+static void meta_window_move_resize_internal (MetaWindow         *window,
+                                              MetaMoveResizeFlags flags,
+                                              int                 resize_gravity,
+                                              int                 root_x_nw,
+                                              int                 root_y_nw,
+                                              int                 w,
+                                              int                 h);
+
+
+void meta_window_move_resize_now (MetaWindow  *window);
+
+/* FIXME we need an abstraction that covers all these queues. */   
+
+void meta_window_unqueue_calc_showing (MetaWindow *window);
+void meta_window_flush_calc_showing   (MetaWindow *window);
+
+void meta_window_unqueue_move_resize  (MetaWindow *window);
+void meta_window_flush_move_resize    (MetaWindow *window);
+
+static void meta_window_update_icon_now (MetaWindow *window);
+void meta_window_unqueue_update_icon    (MetaWindow *window);
+void meta_window_flush_update_icon      (MetaWindow *window);
+
+static void meta_window_apply_session_info (MetaWindow                  *window,
+                                            const MetaWindowSessionInfo *info);
+
+#ifdef WITH_VERBOSE_MODE
+static const char*
+wm_state_to_string (int state)
+{
+  switch (state)
+    {
+    case NormalState:
+      return "NormalState";      
+    case IconicState:
+      return "IconicState";
+    case WithdrawnState:
+      return "WithdrawnState";
+    }
+
+  return "Unknown";
+}
+#endif
+
+static gboolean
+is_desktop_or_dock_foreach (MetaWindow *window,
+                            void       *data)
+{
+  gboolean *result = data;
+
+  *result =
+    window->type == META_WINDOW_DESKTOP ||
+    window->type == META_WINDOW_DOCK;
+  if (*result)
+    return FALSE; /* stop as soon as we find one */
+  else
+    return TRUE;
+}
+
+/* window is the window that's newly mapped provoking
+ * the possible change
+ */
+static void
+maybe_leave_show_desktop_mode (MetaWindow *window)
+{
+  gboolean is_desktop_or_dock;
+
+  if (!window->screen->showing_desktop)
+    return;
+
+  /* If the window is a transient for the dock or desktop, don't
+   * leave show desktop mode when the window opens. That's
+   * so you can e.g. hide all windows, manipulate a file on
+   * the desktop via a dialog, then unshow windows again.
+   */
+  is_desktop_or_dock = FALSE;
+  is_desktop_or_dock_foreach (window,
+                              &is_desktop_or_dock);
+
+  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
+                                &is_desktop_or_dock);
+
+  if (!is_desktop_or_dock)
+    {
+      meta_screen_minimize_all_except (window->screen, window);
+      meta_screen_unshow_desktop (window->screen);      
+    }
+}
+
+MetaWindow*
+meta_window_new (MetaDisplay *display,
+                 Window       xwindow,
+                 gboolean     must_be_viewable)
+{
+  XWindowAttributes attrs;
+  MetaWindow *window;
+  
+  meta_display_grab (display);
+  meta_error_trap_push (display); /* Push a trap over all of window
+                                   * creation, to reduce XSync() calls
+                                   */
+  
+  meta_error_trap_push_with_return (display);
+  
+  XGetWindowAttributes (display->xdisplay,
+                        xwindow, &attrs);
+  
+  if (meta_error_trap_pop_with_return (display, TRUE) != Success)
+    {
+      meta_verbose ("Failed to get attributes for window 0x%lx\n",
+                    xwindow);
+      meta_error_trap_pop (display, TRUE);
+      meta_display_ungrab (display);
+      return NULL;
+    }
+  window = meta_window_new_with_attrs (display, xwindow,
+                                       must_be_viewable, &attrs);
+
+
+  meta_error_trap_pop (display, FALSE);
+  meta_display_ungrab (display);
+
+  return window;
+}
+
+MetaWindow*
+meta_window_new_with_attrs (MetaDisplay       *display,
+                            Window             xwindow,
+                            gboolean           must_be_viewable,
+                            XWindowAttributes *attrs)
+{
+  MetaWindow *window;
+  GSList *tmp;
+  MetaWorkspace *space;
+  gulong existing_wm_state;
+  gulong event_mask;
+#define N_INITIAL_PROPS 12
+  Atom initial_props[N_INITIAL_PROPS];
+  int i;
+  gboolean has_shape;
+
+  g_assert (attrs != NULL);
+  g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props));
+  
+  meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
+
+  if (xwindow == display->no_focus_window)
+    {
+      meta_verbose ("Not managing no_focus_window 0x%lx\n",
+                    xwindow);
+      return NULL;
+    }
+
+  if (attrs->override_redirect)
+    {
+      meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
+      return NULL;
+    }
+  
+  /* Grab server */
+  meta_display_grab (display);
+  meta_error_trap_push (display); /* Push a trap over all of window
+                                   * creation, to reduce XSync() calls
+                                   */
+
+  meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
+                must_be_viewable,
+                attrs->map_state,
+                (attrs->map_state == IsUnmapped) ?
+                "IsUnmapped" :
+                (attrs->map_state == IsViewable) ?
+                "IsViewable" :
+                (attrs->map_state == IsUnviewable) ?
+                "IsUnviewable" :
+                "(unknown)");
+  
+  existing_wm_state = WithdrawnState;
+  if (must_be_viewable && attrs->map_state != IsViewable)
+    {
+      /* Only manage if WM_STATE is IconicState or NormalState */
+      gulong state;
+
+      /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
+      if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
+                                                   display->atom_wm_state,
+                                                   display->atom_wm_state,
+                                                   &state) &&
+            (state == IconicState || state == NormalState)))
+        {
+          meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
+          meta_error_trap_pop (display, TRUE);
+          meta_display_ungrab (display);
+          return NULL;
+        }
+
+      existing_wm_state = state;
+      meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
+                    wm_state_to_string (existing_wm_state));
+    }
+  
+  meta_error_trap_push_with_return (display);
+  
+  XAddToSaveSet (display->xdisplay, xwindow);
+
+  event_mask =
+    PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
+    FocusChangeMask | ColormapChangeMask;
+
+  XSelectInput (display->xdisplay, xwindow, event_mask);
+
+  has_shape = FALSE;
+#ifdef HAVE_SHAPE
+  if (META_DISPLAY_HAS_SHAPE (display))
+    {
+      int x_bounding, y_bounding, x_clip, y_clip;
+      unsigned w_bounding, h_bounding, w_clip, h_clip;
+      int bounding_shaped, clip_shaped;
+
+      XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
+
+      XShapeQueryExtents (display->xdisplay, xwindow,
+                          &bounding_shaped, &x_bounding, &y_bounding,
+                          &w_bounding, &h_bounding,
+                          &clip_shaped, &x_clip, &y_clip,
+                          &w_clip, &h_clip);
+
+      has_shape = bounding_shaped != FALSE;
+
+      meta_topic (META_DEBUG_SHAPES,
+                  "Window has_shape = %d extents %d,%d %d x %d\n",
+                  has_shape, x_bounding, y_bounding,
+                  w_bounding, h_bounding);
+    }
+#endif
+
+  /* Get rid of any borders */
+  if (attrs->border_width != 0)
+    XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
+
+  /* Get rid of weird gravities */
+  if (attrs->win_gravity != NorthWestGravity)
+    {
+      XSetWindowAttributes set_attrs;
+      
+      set_attrs.win_gravity = NorthWestGravity;
+      
+      XChangeWindowAttributes (display->xdisplay,
+                               xwindow,
+                               CWWinGravity,
+                               &set_attrs);
+    }
+  
+  if (meta_error_trap_pop_with_return (display, FALSE) != Success)
+    {
+      meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
+                    xwindow);
+      meta_error_trap_pop (display, FALSE);
+      meta_display_ungrab (display);
+      return NULL;
+    }
+
+  g_assert (!attrs->override_redirect);
+  
+  window = g_new (MetaWindow, 1);
+
+  window->dialog_pid = -1;
+  window->dialog_pipe = -1;
+  
+  window->xwindow = xwindow;
+  
+  /* this is in window->screen->display, but that's too annoying to
+   * type
+   */
+  window->display = display;
+  window->workspaces = NULL;
+
+#ifdef HAVE_XSYNC
+  window->update_counter = None;
+#endif
+  
+  window->screen = NULL;
+  tmp = display->screens;
+  while (tmp != NULL)
+    {
+      MetaScreen *scr = tmp->data;
+
+      if (scr->xroot == attrs->root)
+        {
+          window->screen = tmp->data;
+          break;
+        }
+      
+      tmp = tmp->next;
+    }
+  
+  g_assert (window->screen);
+
+  window->desc = g_strdup_printf ("0x%lx", window->xwindow);
+
+  /* avoid tons of stack updates */
+  meta_stack_freeze (window->screen->stack);
+
+  window->has_shape = has_shape;
+  
+  /* Remember this rect is the actual window size */
+  window->rect.x = attrs->x;
+  window->rect.y = attrs->y;
+  window->rect.width = attrs->width;
+  window->rect.height = attrs->height;
+
+  /* And border width, size_hints are the "request" */
+  window->border_width = attrs->border_width;
+  window->size_hints.x = attrs->x;
+  window->size_hints.y = attrs->y;
+  window->size_hints.width = attrs->width;
+  window->size_hints.height = attrs->height;
+  /* initialize the remaining size_hints as if size_hints.flags were zero */
+  meta_set_normal_hints (window, NULL);
+
+  /* And this is our unmaximized size */
+  window->saved_rect = window->rect;
+  window->user_rect = window->rect;
+  
+  window->depth = attrs->depth;
+  window->xvisual = attrs->visual;
+  window->colormap = attrs->colormap;
+  
+  window->title = NULL;
+  window->icon_name = NULL;
+  window->icon = NULL;
+  window->mini_icon = NULL;
+  meta_icon_cache_init (&window->icon_cache);
+  window->wm_hints_pixmap = None;
+  window->wm_hints_mask = None;
+
+  window->frame = NULL;
+  window->has_focus = FALSE;
+
+  window->user_has_move_resized = FALSE;
+  
+  window->maximized = FALSE;
+  window->maximize_after_placement = FALSE;
+  window->fullscreen = FALSE;
+  window->on_all_workspaces = FALSE;
+  window->shaded = FALSE;
+  window->initially_iconic = FALSE;
+  window->minimized = FALSE;
+  window->iconic = FALSE;
+  window->mapped = attrs->map_state != IsUnmapped;
+  /* if already mapped we don't want to do the placement thing */
+  window->placed = window->mapped;
+  if (window->placed)
+    meta_topic (META_DEBUG_PLACEMENT,
+                "Not placing window 0x%lx since it's already mapped\n",
+                xwindow);
+  window->unmanaging = FALSE;
+  window->calc_showing_queued = FALSE;
+  window->move_resize_queued = FALSE;
+  window->keys_grabbed = FALSE;
+  window->grab_on_frame = FALSE;
+  window->all_keys_grabbed = FALSE;
+  window->withdrawn = FALSE;
+  window->initial_workspace_set = FALSE;
+  window->calc_placement = FALSE;
+  window->shaken_loose = FALSE;
+  window->have_focus_click_grab = FALSE;
+  
+  window->unmaps_pending = 0;
+  
+  window->mwm_decorated = TRUE;
+  window->mwm_border_only = FALSE;
+  window->mwm_has_close_func = TRUE;
+  window->mwm_has_minimize_func = TRUE;
+  window->mwm_has_maximize_func = TRUE;
+  window->mwm_has_move_func = TRUE;
+  window->mwm_has_resize_func = TRUE;
+  
+  window->decorated = TRUE;
+  window->has_close_func = TRUE;
+  window->has_minimize_func = TRUE;
+  window->has_maximize_func = TRUE;
+  window->has_move_func = TRUE;
+  window->has_resize_func = TRUE;
+
+  window->has_shade_func = TRUE;
+
+  window->has_fullscreen_func = TRUE;
+
+  window->always_sticky = FALSE;
+  
+  window->wm_state_modal = FALSE;
+  window->skip_taskbar = FALSE;
+  window->skip_pager = FALSE;
+  window->wm_state_skip_taskbar = FALSE;
+  window->wm_state_skip_pager = FALSE;
+  window->wm_state_above = FALSE;
+  window->wm_state_below = FALSE;
+  
+  window->res_class = NULL;
+  window->res_name = NULL;
+  window->role = NULL;
+  window->sm_client_id = NULL;
+  window->wm_client_machine = NULL;
+
+  window->orig_name = NULL;
+  window->num = -1;
+  window->save_id = NULL;
+
+  window->startup_id = NULL;
+  
+  window->net_wm_pid = -1;
+  
+  window->xtransient_for = None;
+  window->xclient_leader = None;
+  window->transient_parent_is_root_window = FALSE;
+  
+  window->type = META_WINDOW_NORMAL;
+  window->type_atom = None;
+
+  window->struts = NULL;
+
+  window->using_net_wm_name = FALSE;
+  window->using_net_wm_icon_name = FALSE;
+
+  window->need_reread_icon = TRUE;
+  window->update_icon_queued = FALSE;
+  
+  window->layer = META_LAYER_LAST; /* invalid value */
+  window->stack_position = -1;
+  window->initial_workspace = 0; /* not used */
+  
+  meta_display_register_x_window (display, &window->xwindow, window);
+
+
+  /* assign the window to its group, or create a new group if needed
+   */
+  window->group = NULL;
+  window->xgroup_leader = None;
+  meta_window_compute_group (window);
+  
+  /* Fill these in the order we want them to be gotten.
+   * we want to get window name and class first
+   * so we can use them in error messages and such.
+   */
+  i = 0;
+  initial_props[i++] = display->atom_net_wm_name;
+  initial_props[i++] = display->atom_wm_client_machine;
+  initial_props[i++] = display->atom_net_wm_pid;
+  initial_props[i++] = XA_WM_NAME;
+  initial_props[i++] = display->atom_net_wm_icon_name;
+  initial_props[i++] = XA_WM_ICON_NAME;
+  initial_props[i++] = display->atom_net_wm_desktop;
+  initial_props[i++] = display->atom_net_startup_id;
+  initial_props[i++] = display->atom_metacity_update_counter;
+  initial_props[i++] = XA_WM_NORMAL_HINTS;
+  initial_props[i++] = display->atom_wm_protocols;
+  initial_props[i++] = XA_WM_HINTS;
+  g_assert (N_INITIAL_PROPS == i);
+  
+  meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
+
+  update_net_wm_state (window);
+  
+  update_mwm_hints (window);
+  update_wm_class (window);
+  update_transient_for (window);
+  update_sm_hints (window); /* must come after transient_for */
+  update_role (window);
+  update_net_wm_type (window);
+  meta_window_update_icon_now (window);
+  update_geometry_save_id (window);
+
+  if (window->initially_iconic)
+    {
+      /* WM_HINTS said minimized */
+      window->minimized = TRUE;
+      meta_verbose ("Window %s asked to start out minimized\n", window->desc);
+    }
+
+  if (existing_wm_state == IconicState)
+    {
+      /* WM_STATE said minimized */
+      window->minimized = TRUE;
+      meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n",
+                    window->desc);
+
+      /* Assume window was previously placed, though perhaps it's
+       * been iconic its whole life, we have no way of knowing.
+       */
+      window->placed = TRUE;
+    }
+
+  /* Apply any window attributes such as initial workspace
+   * based on startup notification
+   */
+  meta_screen_apply_startup_properties (window->screen, window);
+  
+  /* FIXME we have a tendency to set this then immediately
+   * change it again.
+   */
+  set_wm_state (window, window->iconic ? IconicState : NormalState);
+  set_net_wm_state (window);
+
+  if (window->decorated)
+    meta_window_ensure_frame (window);
+
+  meta_window_grab_keys (window);
+  meta_display_grab_window_buttons (window->display, window->xwindow);
+  meta_display_grab_focus_window_button (window->display, window);
+
+  if (window->type == META_WINDOW_DESKTOP ||
+      window->type == META_WINDOW_DOCK)
+    {
+      /* Change the default, but don't enforce this if the user
+       * focuses the dock/desktop and unsticks it using key shortcuts.
+       * Need to set this before adding to the workspaces so the MRU
+       * lists will be updated.
+       */
+      window->on_all_workspaces = TRUE;
+    }
+  
+  /* For the workspace, first honor hints,
+   * if that fails put transients with parents,
+   * otherwise put window on active space
+   */
+  
+  if (window->initial_workspace_set)
+    {
+      if (window->initial_workspace == (int) 0xFFFFFFFF)
+        {
+          meta_topic (META_DEBUG_PLACEMENT,
+                      "Window %s is initially on all spaces\n",
+                      window->desc);
+          
+	  /* need to set on_all_workspaces first so that it will be
+	   * added to all the MRU lists
+	   */
+          window->on_all_workspaces = TRUE;
+          meta_workspace_add_window (window->screen->active_workspace, window);
+        }
+      else
+        {
+          meta_topic (META_DEBUG_PLACEMENT,
+                      "Window %s is initially on space %d\n",
+                      window->desc, window->initial_workspace);
+
+          space =
+            meta_screen_get_workspace_by_index (window->screen,
+                                                window->initial_workspace);
+          
+          if (space)
+            meta_workspace_add_window (space, window);
+        }
+    }
+  
+  if (window->workspaces == NULL && 
+      window->xtransient_for != None)
+    {
+      /* Try putting dialog on parent's workspace */
+      MetaWindow *parent;
+
+      parent = meta_display_lookup_x_window (window->display,
+                                             window->xtransient_for);
+
+      if (parent)
+        {
+          GList *tmp_list;
+
+          meta_topic (META_DEBUG_PLACEMENT,
+                      "Putting window %s on some workspaces as parent %s\n",
+                      window->desc, parent->desc);
+          
+          if (parent->on_all_workspaces)
+            window->on_all_workspaces = TRUE;
+          
+          tmp_list = parent->workspaces;
+          while (tmp_list != NULL)
+            {
+	      /* this will implicitly add to the appropriate MRU lists
+	       */
+              meta_workspace_add_window (tmp_list->data, window);
+              
+              tmp_list = tmp_list->next;
+            }
+        }
+    }
+  
+  if (window->workspaces == NULL)
+    {
+      meta_topic (META_DEBUG_PLACEMENT,
+                  "Putting window %s on active workspace\n",
+                  window->desc);
+      
+      space = window->screen->active_workspace;
+
+      meta_workspace_add_window (space, window);
+    }
+  
+  /* for the various on_all_workspaces = TRUE possible above */
+  meta_window_set_current_workspace_hint (window);
+  
+  meta_window_update_struts (window);
+
+  /* Put our state back where it should be,
+   * passing TRUE for is_configure_request, ICCCM says
+   * initial map is handled same as configure request
+   */
+  meta_window_move_resize_internal (window,
+                                    META_IS_CONFIGURE_REQUEST,
+                                    NorthWestGravity,
+                                    window->size_hints.x,
+                                    window->size_hints.y,
+                                    window->size_hints.width,
+                                    window->size_hints.height);
+
+  meta_stack_add (window->screen->stack, 
+                  window);
+
+  /* Now try applying saved stuff from the session */
+  {
+    const MetaWindowSessionInfo *info;
+
+    info = meta_window_lookup_saved_state (window);
+
+    if (info)
+      {
+        meta_window_apply_session_info (window, info);
+        meta_window_release_saved_state (info);
+      }
+  }
+  
+  /* Sync stack changes */
+  meta_stack_thaw (window->screen->stack);
+
+  /* disable show desktop mode unless we're a desktop component */
+  maybe_leave_show_desktop_mode (window);
+  
+  meta_window_queue_calc_showing (window);
+
+  meta_error_trap_pop (display, FALSE); /* pop the XSync()-reducing trap */
+  meta_display_ungrab (display);
+  
+  return window;
+}
+
+/* This function should only be called from the end of meta_window_new_with_attrs () */
+static void
+meta_window_apply_session_info (MetaWindow *window,
+                                const MetaWindowSessionInfo *info)
+{
+  if (info->stack_position_set)
+    {
+      meta_topic (META_DEBUG_SM,
+                  "Restoring stack position %d for window %s\n",
+                  info->stack_position, window->desc);
+
+      /* FIXME well, I'm not sure how to do this. */
+    }
+
+  if (info->minimized_set)
+    {
+      meta_topic (META_DEBUG_SM,
+                  "Restoring minimized state %d for window %s\n",
+                  info->minimized, window->desc);
+
+      if (window->has_minimize_func && info->minimized)
+        meta_window_minimize (window);
+    }
+
+  if (info->maximized_set)
+    {
+      meta_topic (META_DEBUG_SM,
+                  "Restoring maximized state %d for window %s\n",
+                  info->maximized, window->desc);
+      
+      if (window->has_maximize_func && info->maximized)
+	{
+	  meta_window_maximize (window);
+
+          if (info->saved_rect_set)
+            {
+              meta_topic (META_DEBUG_SM,
+                          "Restoring saved rect %d,%d %dx%d for window %s\n",
+                          info->saved_rect.x,
+                          info->saved_rect.y,
+                          info->saved_rect.width,
+                          info->saved_rect.height,
+                          window->desc);
+              
+              window->saved_rect.x = info->saved_rect.x;
+              window->saved_rect.y = info->saved_rect.y;
+              window->saved_rect.width = info->saved_rect.width;
+              window->saved_rect.height = info->saved_rect.height;
+            }
+	}
+    }
+  
+  if (info->on_all_workspaces_set)
+    {
+      window->on_all_workspaces = info->on_all_workspaces;
+      meta_topic (META_DEBUG_SM,
+                  "Restoring sticky state %d for window %s\n",
+                  window->on_all_workspaces, window->desc);
+    }
+  
+  if (info->workspace_indices)
+    {
+      GSList *tmp;
+      GSList *spaces;      
+
+      spaces = NULL;
+      
+      tmp = info->workspace_indices;
+      while (tmp != NULL)
+        {
+          MetaWorkspace *space;          
+
+          space =
+            meta_screen_get_workspace_by_index (window->screen,
+                                                GPOINTER_TO_INT (tmp->data));
+          
+          if (space)
+            spaces = g_slist_prepend (spaces, space);
+          
+          tmp = tmp->next;
+        }
+
+      if (spaces)
+        {
+          /* This briefly breaks the invariant that we are supposed
+           * to always be on some workspace. But we paranoically
+           * ensured that one of the workspaces from the session was
+           * indeed valid, so we know we'll go right back to one.
+           */
+          while (window->workspaces)
+            meta_workspace_remove_window (window->workspaces->data, window);
+
+          tmp = spaces;
+          while (tmp != NULL)
+            {
+              MetaWorkspace *space;
+
+              space = tmp->data;
+              
+              meta_workspace_add_window (space, window);              
+
+              meta_topic (META_DEBUG_SM,
+                          "Restoring saved window %s to workspace %d\n",
+                          window->desc,
+                          meta_workspace_index (space));
+              
+              tmp = tmp->next;
+            }
+
+          g_slist_free (spaces);
+        }
+    }
+
+  if (info->geometry_set)
+    {
+      int x, y, w, h;
+      
+      window->placed = TRUE; /* don't do placement algorithms later */
+
+      x = info->rect.x;
+      y = info->rect.y;
+
+      w = window->size_hints.base_width +
+        info->rect.width * window->size_hints.width_inc;
+      h = window->size_hints.base_height +
+        info->rect.height * window->size_hints.height_inc;
+
+      /* Force old gravity, ignoring anything now set */
+      window->size_hints.win_gravity = info->gravity;
+      
+      meta_topic (META_DEBUG_SM,
+                  "Restoring pos %d,%d size %d x %d for %s\n",
+                  x, y, w, h, window->desc);
+      
+      meta_window_move_resize_internal (window,
+                                        META_DO_GRAVITY_ADJUST,
+                                        NorthWestGravity,
+                                        x, y, w, h);
+    }
+}
+
+void
+meta_window_free (MetaWindow  *window)
+{
+  GList *tmp;
+  
+  meta_verbose ("Unmanaging 0x%lx\n", window->xwindow);
+
+  if (window->display->window_with_menu == window)
+    {
+      meta_ui_window_menu_free (window->display->window_menu);
+      window->display->window_menu = NULL;
+      window->display->window_with_menu = NULL;
+    }
+  
+  if (destroying_windows_disallowed > 0)
+    meta_bug ("Tried to destroy window %s while destruction was not allowed\n",
+              window->desc);
+  
+  window->unmanaging = TRUE;
+
+  meta_history_forget_window ();
+
+  if (window->fullscreen)
+    {
+      MetaGroup *group;
+
+      /* If the window is fullscreen, it may be forcing
+       * other windows in its group to a higher layer
+       */
+
+      meta_stack_freeze (window->screen->stack);
+      group = meta_window_get_group (window);
+      if (group)
+        meta_group_update_layers (group);
+      meta_stack_thaw (window->screen->stack);
+    }
+
+  meta_window_shutdown_group (window); /* safe to do this early as
+                                        * group.c won't re-add to the
+                                        * group if window->unmanaging
+                                        */
+  
+  /* If we have the focus, focus some other window.
+   * This is done first, so that if the unmap causes
+   * an EnterNotify the EnterNotify will have final say
+   * on what gets focused, maintaining sloppy focus
+   * invariants.
+   */
+  if (window->has_focus)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing top window since we're unmanaging %s\n",
+                  window->desc);
+      meta_workspace_focus_mru_window (window->screen->active_workspace, window);
+    }
+  else if (window->display->expected_focus_window == window)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing top window since expected focus window freed %s\n",
+                  window->desc);
+      window->display->expected_focus_window = NULL;
+      meta_workspace_focus_mru_window (window->screen->active_workspace, window);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Unmanaging window %s which doesn't currently have focus\n",
+                  window->desc);
+    }
+
+  if (window->struts)
+    {
+      g_free (window->struts);
+      window->struts = NULL;
+
+      meta_topic (META_DEBUG_WORKAREA,
+                  "Unmanaging window %s which has struts, so invalidating work areas\n",
+                  window->desc);
+      invalidate_work_areas (window);
+    }
+  
+  if (window->display->grab_window == window)
+    meta_display_end_grab_op (window->display,
+                              meta_display_get_current_time (window->display));
+
+  g_assert (window->display->grab_window != window);
+  
+  if (window->display->focus_window == window)
+    window->display->focus_window = NULL;
+
+  meta_window_unqueue_calc_showing (window);
+  meta_window_unqueue_move_resize (window);
+  meta_window_unqueue_update_icon (window);
+  meta_window_free_delete_dialog (window);
+  
+  tmp = window->workspaces;
+  while (tmp != NULL)
+    {
+      GList *next;
+
+      next = tmp->next;
+
+      /* pops front of list */
+      meta_workspace_remove_window (tmp->data, window);
+
+      tmp = next;
+    }
+
+  g_assert (window->workspaces == NULL);
+
+#ifndef G_DISABLE_CHECKS
+  tmp = window->screen->workspaces;
+  while (tmp != NULL)
+    {
+      MetaWorkspace *workspace = tmp->data;
+
+      g_assert (g_list_find (workspace->windows, window) == NULL);
+      g_assert (g_list_find (workspace->mru_list, window) == NULL);
+
+      tmp = tmp->next;
+    }
+#endif
+  
+  meta_stack_remove (window->screen->stack, window);
+  
+  /* FIXME restore original size if window has maximized */
+  
+  if (window->frame)
+    meta_window_destroy_frame (window);
+  
+  if (window->withdrawn)
+    {
+      /* We need to clean off the window's state so it
+       * won't be restored if the app maps it again.
+       */
+      meta_error_trap_push (window->display);
+      meta_verbose ("Cleaning state from window %s\n", window->desc);
+      XDeleteProperty (window->display->xdisplay,
+                       window->xwindow,
+                       window->display->atom_net_wm_desktop);
+      XDeleteProperty (window->display->xdisplay,
+                       window->xwindow,
+                       window->display->atom_net_wm_state);
+      set_wm_state (window, WithdrawnState);
+      meta_error_trap_pop (window->display, FALSE);
+    }
+  else
+    {
+      /* We need to put WM_STATE so that others will understand it on
+       * restart.
+       */
+      if (!window->minimized)
+        {
+          meta_error_trap_push (window->display);
+          set_wm_state (window, NormalState);
+          meta_error_trap_pop (window->display, FALSE);
+        }
+
+      /* And we need to be sure the window is mapped so other WMs
+       * know that it isn't Withdrawn
+       */
+      meta_error_trap_push (window->display);
+      XMapWindow (window->display->xdisplay,
+                  window->xwindow);
+      meta_error_trap_pop (window->display, FALSE);
+    }
+
+  meta_window_ungrab_keys (window);
+  meta_display_ungrab_window_buttons (window->display, window->xwindow);
+  meta_display_ungrab_focus_window_button (window->display, window);
+  
+  meta_display_unregister_x_window (window->display, window->xwindow);
+  
+
+  meta_error_trap_push (window->display);
+
+  /* Put back anything we messed up */
+  if (window->border_width != 0)
+    XSetWindowBorderWidth (window->display->xdisplay,
+                           window->xwindow,
+                           window->border_width);
+
+  /* No save set */
+  XRemoveFromSaveSet (window->display->xdisplay,
+                      window->xwindow);
+
+  /* Don't get events on not-managed windows */
+  XSelectInput (window->display->xdisplay,
+                window->xwindow,
+                NoEventMask);
+
+#ifdef HAVE_SHAPE
+  if (META_DISPLAY_HAS_SHAPE (window->display))
+    XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
+#endif
+  
+  meta_error_trap_pop (window->display, FALSE);
+
+  if (window->icon)
+    g_object_unref (G_OBJECT (window->icon));
+
+  if (window->mini_icon)
+    g_object_unref (G_OBJECT (window->mini_icon));
+
+  meta_icon_cache_free (&window->icon_cache);
+  
+  g_free (window->sm_client_id);
+  g_free (window->wm_client_machine);
+  g_free (window->startup_id);
+  g_free (window->role);
+  g_free (window->res_class);
+  g_free (window->res_name);
+  g_free (window->orig_name);
+  g_free (window->title);
+  g_free (window->icon_name);
+  g_free (window->desc);
+  g_free (window);
+}
+
+static void
+set_wm_state (MetaWindow *window,
+              int         state)
+{
+  unsigned long data[2];
+  
+  meta_verbose ("Setting wm state %s on %s\n",
+                wm_state_to_string (state), window->desc);
+  
+  /* Metacity doesn't use icon windows, so data[1] should be None
+   * according to the ICCCM 2.0 Section 4.1.3.1.
+   */
+  data[0] = state;
+  data[1] = None;
+
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_wm_state,
+                   window->display->atom_wm_state,
+                   32, PropModeReplace, (guchar*) data, 2);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+static void
+set_net_wm_state (MetaWindow *window)
+{
+  int i;
+  unsigned long data[10];
+  
+  i = 0;
+  if (window->shaded)
+    {
+      data[i] = window->display->atom_net_wm_state_shaded;
+      ++i;
+    }
+  if (window->wm_state_modal)
+    {
+      data[i] = window->display->atom_net_wm_state_modal;
+      ++i;
+    }
+  if (window->skip_pager)
+    {
+      data[i] = window->display->atom_net_wm_state_skip_pager;
+      ++i;
+    }
+  if (window->skip_taskbar)
+    {
+      data[i] = window->display->atom_net_wm_state_skip_taskbar;
+      ++i;
+    }
+  if (window->maximized)
+    {
+      data[i] = window->display->atom_net_wm_state_maximized_horz;
+      ++i;
+      data[i] = window->display->atom_net_wm_state_maximized_vert;
+      ++i;
+    }
+  if (window->fullscreen)
+    {
+      data[i] = window->display->atom_net_wm_state_fullscreen;
+      ++i;
+    }
+  if (window->shaded || window->minimized)
+    {
+      data[i] = window->display->atom_net_wm_state_hidden;
+      ++i;
+    }
+  if (window->wm_state_above)
+    {
+      data[i] = window->display->atom_net_wm_state_above;
+      ++i;
+    }
+  if (window->wm_state_below)
+    {
+      data[i] = window->display->atom_net_wm_state_below;
+      ++i;
+    }
+  
+  meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
+  
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_wm_state,
+                   XA_ATOM,
+                   32, PropModeReplace, (guchar*) data, i);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+/* FIXME rename this, it makes it sound like map state is relevant */
+gboolean
+meta_window_visible_on_workspace (MetaWindow    *window,
+                                  MetaWorkspace *workspace)
+{
+  return (window->on_all_workspaces && window->screen == workspace->screen) ||
+    meta_workspace_contains_window (workspace, window);
+}
+
+static gboolean
+is_minimized_foreach (MetaWindow *window,
+                      void       *data)
+{
+  gboolean *result = data;
+
+  *result = window->minimized;
+  if (*result)
+    return FALSE; /* stop as soon as we find one */
+  else
+    return TRUE;
+}
+
+static gboolean
+ancestor_is_minimized (MetaWindow *window)
+{
+  gboolean is_minimized;
+
+  is_minimized = FALSE;
+
+  meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized);
+
+  return is_minimized;
+}
+
+static gboolean
+window_should_be_showing (MetaWindow  *window)
+{
+  gboolean showing, on_workspace;
+  gboolean is_desktop_or_dock;
+
+  meta_verbose ("Should be showing for window %s\n", window->desc);
+
+  /* 1. See if we're on the workspace */
+  
+  on_workspace = meta_window_visible_on_workspace (window, 
+                                                   window->screen->active_workspace);
+
+  showing = on_workspace;
+  
+  if (!on_workspace)
+    meta_verbose ("Window %s is not on workspace %d\n",
+                  window->desc,
+                  meta_workspace_index (window->screen->active_workspace));
+  else
+    meta_verbose ("Window %s is on the active workspace %d\n",
+                  window->desc,
+                  meta_workspace_index (window->screen->active_workspace));
+
+  if (window->on_all_workspaces)
+    meta_verbose ("Window %s is on all workspaces\n", window->desc);
+
+  /* 2. See if we're minimized */
+  if (window->minimized)
+    showing = FALSE;
+  
+  /* 3. See if we're in "show desktop" mode */
+  is_desktop_or_dock = FALSE;
+  is_desktop_or_dock_foreach (window,
+                              &is_desktop_or_dock);
+
+  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
+                                &is_desktop_or_dock);
+
+  if (showing &&      
+      window->screen->showing_desktop &&
+      !is_desktop_or_dock)
+    {
+      meta_verbose ("Window %s is on current workspace, but we're showing the desktop\n",
+                    window->desc);
+      showing = FALSE;
+    }
+
+  /* 4. See if an ancestor is minimized (note that
+   *    ancestor's "mapped" field may not be up to date
+   *    since it's being computed in this same idle queue)
+   */
+  
+  if (showing)
+    {
+      if (ancestor_is_minimized (window))
+        showing = FALSE;
+    }
+
+#if 0
+  /* 5. See if we're drawing wireframe
+   */
+  if (window->display->grab_window == window &&
+      window->display->grab_wireframe_active)    
+    showing = FALSE;
+#endif
+  
+  return showing;
+}
+
+static void
+implement_showing (MetaWindow *window,
+                   gboolean    showing)
+{
+  /* Actually show/hide the window */
+  meta_verbose ("Implement showing = %d for window %s\n",
+                showing, window->desc);
+  
+  if (!showing)
+    {
+      gboolean on_workspace;
+
+      on_workspace = meta_window_visible_on_workspace (window, 
+                                                       window->screen->active_workspace);
+  
+      /* Really this effects code should probably
+       * be in meta_window_hide so the window->mapped
+       * test isn't duplicated here. Anyhow, we animate
+       * if we are mapped now, we are supposed to
+       * be minimized, and we are on the current workspace.
+       */
+      if (on_workspace && window->minimized && window->mapped &&
+          !meta_prefs_get_reduced_resources ())
+        {
+	  MetaRectangle icon_rect, window_rect;
+	  gboolean result;
+	  
+	  /* Check if the window has an icon geometry */
+	  result = meta_window_get_icon_geometry (window, &icon_rect);
+          
+          if (!result)
+            {
+              /* just animate into the corner somehow - maybe
+               * not a good idea...
+               */              
+              icon_rect.x = window->screen->width;
+              icon_rect.y = window->screen->height;
+              icon_rect.width = 1;
+              icon_rect.height = 1;
+            }
+
+          meta_window_get_outer_rect (window, &window_rect);
+          
+          /* Draw a nice cool animation */
+          meta_effects_draw_box_animation (window->screen,
+                                           &window_rect,
+                                           &icon_rect,
+                                           META_MINIMIZE_ANIMATION_LENGTH,
+                                           META_BOX_ANIM_SCALE);
+	}
+
+      meta_window_hide (window);
+    }
+  else
+    {
+      meta_window_show (window);
+    }
+}
+
+void
+meta_window_calc_showing (MetaWindow  *window)
+{
+  implement_showing (window, window_should_be_showing (window));
+}
+
+static guint calc_showing_idle = 0;
+static GSList *calc_showing_pending = NULL;
+
+static int
+stackcmp (gconstpointer a, gconstpointer b)
+{
+  MetaWindow *aw = (gpointer) a;
+  MetaWindow *bw = (gpointer) b;
+
+  if (aw->screen != bw->screen)
+    return 0; /* don't care how they sort with respect to each other */
+  else
+    return meta_stack_windows_cmp (aw->screen->stack,
+                                   aw, bw);
+}
+
+static gboolean
+idle_calc_showing (gpointer data)
+{
+  GSList *tmp;
+  GSList *copy;
+  GSList *should_show;
+  GSList *should_hide;
+  GSList *unplaced;
+  GSList *displays;
+  
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Clearing the calc_showing queue\n");
+
+  /* Work with a copy, for reentrancy. The allowed reentrancy isn't
+   * complete; destroying a window while we're in here would result in
+   * badness. But it's OK to queue/unqueue calc_showings.
+   */
+  copy = g_slist_copy (calc_showing_pending);
+  g_slist_free (calc_showing_pending);
+  calc_showing_pending = NULL;
+  calc_showing_idle = 0;
+
+  destroying_windows_disallowed += 1;
+  
+  /* We map windows from top to bottom and unmap from bottom to
+   * top, to avoid extra expose events. The exception is
+   * for unplaced windows, which have to be mapped from bottom to
+   * top so placement works.
+   */
+  should_show = NULL;
+  should_hide = NULL;
+  unplaced = NULL;
+  displays = NULL;
+
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      if (!window->placed)
+        unplaced = g_slist_prepend (unplaced, window);
+      else if (window_should_be_showing (window))
+        should_show = g_slist_prepend (should_show, window);
+      else
+        should_hide = g_slist_prepend (should_hide, window);
+      
+      tmp = tmp->next;
+    }
+
+  /* bottom to top */
+  unplaced = g_slist_sort (unplaced, stackcmp);
+  should_hide = g_slist_sort (should_hide, stackcmp);
+  /* top to bottom */
+  should_show = g_slist_sort (should_show, stackcmp);
+  should_show = g_slist_reverse (should_show);
+  
+  tmp = unplaced;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      meta_window_calc_showing (window);
+      
+      tmp = tmp->next;
+    }
+
+  tmp = should_hide;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      implement_showing (window, FALSE);
+      
+      tmp = tmp->next;
+    }
+  
+  tmp = should_show;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      implement_showing (window, TRUE);
+      
+      tmp = tmp->next;
+    }
+  
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      /* important to set this here for reentrancy -
+       * if we queue a window again while it's in "copy",
+       * then queue_calc_showing will just return since
+       * calc_showing_queued = TRUE still
+       */
+      window->calc_showing_queued = FALSE;
+      
+      tmp = tmp->next;
+    }
+
+  /* for all displays used in the queue, set a sentinel property on
+   * the root window so that we can ignore EnterNotify events that
+   * occur before the window maps occur.  This avoids a race
+   * condition. */
+  tmp = should_show;
+  while (tmp != NULL)
+    {
+      MetaWindow *window = tmp->data;
+      
+      if (g_slist_find (displays, window->display) == NULL)
+        {
+          displays = g_slist_prepend (displays, window->display);
+          meta_display_increment_focus_sentinel (window->display);
+        }
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (copy);
+
+  g_slist_free (unplaced);
+  g_slist_free (should_show);
+  g_slist_free (should_hide);
+  g_slist_free (displays);
+  
+  destroying_windows_disallowed -= 1;
+  
+  return FALSE;
+}
+
+void
+meta_window_unqueue_calc_showing (MetaWindow *window)
+{
+  if (!window->calc_showing_queued)
+    return;
+
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Removing %s from the calc_showing queue\n",
+              window->desc);
+
+  /* Note that window may not actually be in move_resize_pending
+   * because it may have been in "copy" inside the idle handler
+   */  
+  calc_showing_pending = g_slist_remove (calc_showing_pending, window);
+  window->calc_showing_queued = FALSE;
+  
+  if (calc_showing_pending == NULL &&
+      calc_showing_idle != 0)
+    {
+      g_source_remove (calc_showing_idle);
+      calc_showing_idle = 0;
+    }
+}
+
+void
+meta_window_flush_calc_showing (MetaWindow *window)
+{
+  if (window->calc_showing_queued)
+    {
+      meta_window_unqueue_calc_showing (window);
+      meta_window_calc_showing (window);
+    }
+}
+
+void
+meta_window_queue_calc_showing (MetaWindow  *window)
+{
+  /* if withdrawn = TRUE then unmanaging should also be TRUE,
+   * really.
+   */
+  if (window->unmanaging || window->withdrawn)
+    return;
+
+  if (window->calc_showing_queued)
+    return;
+
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Putting %s in the calc_showing queue\n",
+              window->desc);
+  
+  window->calc_showing_queued = TRUE;
+  
+  if (calc_showing_idle == 0)
+    calc_showing_idle = g_idle_add (idle_calc_showing, NULL);
+
+  calc_showing_pending = g_slist_prepend (calc_showing_pending, window);
+}
+
+static gboolean
+window_takes_focus_on_map (MetaWindow *window)
+{
+  /* don't initially focus windows that are intended to not accept
+   * focus
+   */
+  if (!(window->input || window->take_focus))
+    return FALSE;
+
+  switch (window->type)
+    {
+    case META_WINDOW_DOCK:
+    case META_WINDOW_DESKTOP:
+    case META_WINDOW_UTILITY:
+    case META_WINDOW_SPLASHSCREEN:
+    case META_WINDOW_TOOLBAR:
+    case META_WINDOW_MENU:
+      /* don't focus these */
+      break;
+    case META_WINDOW_NORMAL:
+
+      /* Always focus new windows */
+      return TRUE;
+
+      /* Old Windows-XP style rule for reference */
+      /* Focus only if the current focus is on a desktop element
+       * or nonexistent.
+       *
+       * (using display->focus_window is a bit of a race condition,
+       *  but I have no idea how to avoid it)
+       */
+      if (window->display->focus_window == NULL ||
+          (window->display->focus_window &&
+           (window->display->focus_window->type == META_WINDOW_DOCK ||
+            window->display->focus_window->type == META_WINDOW_DESKTOP)))
+        return TRUE;
+      break;
+    case META_WINDOW_DIALOG:
+    case META_WINDOW_MODAL_DIALOG:
+
+      /* Always focus */
+      return TRUE;
+
+      /* Old Windows-XP style rule for reference */
+      /* Focus only if the transient parent has focus */
+      /* (using display->focus_window is a bit of a race condition,
+       *  but I have no idea how to avoid it)
+       */
+      if (window->display->focus_window == NULL ||
+          (window->display->focus_window &&
+           meta_window_is_ancestor_of_transient (window->display->focus_window,
+                                                 window)) ||
+          (window->display->focus_window->type == META_WINDOW_DOCK ||
+           window->display->focus_window->type == META_WINDOW_DESKTOP))
+        return TRUE;
+      break;
+    }
+
+  return FALSE;
+}
+
+void
+meta_window_show (MetaWindow *window)
+{
+  gboolean did_placement;
+  gboolean did_show;
+  
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Showing window %s, shaded: %d iconic: %d placed: %d\n",
+              window->desc, window->shaded, window->iconic, window->placed);
+
+  did_show = FALSE;
+  did_placement = FALSE;
+  if (!window->placed)
+    {
+      /* We have to recalc the placement here since other windows may
+       * have been mapped/placed since we last did constrain_position
+       */
+
+      /* calc_placement is an efficiency hack to avoid
+       * multiple placement calculations before we finally
+       * show the window.
+       */
+      window->calc_placement = TRUE;
+      meta_window_move_resize_now (window);
+      window->calc_placement = FALSE;
+
+      /* don't ever do the initial position constraint thing again.
+       * This is toggled here so that initially-iconified windows
+       * still get placed when they are ultimately shown.
+       */
+      window->placed = TRUE;
+      did_placement = TRUE;
+    }
+  
+  /* Shaded means the frame is mapped but the window is not */
+  
+  if (window->frame && !window->frame->mapped)
+    {
+      meta_topic (META_DEBUG_WINDOW_STATE,
+                  "Frame actually needs map\n");
+      window->frame->mapped = TRUE;
+      meta_ui_map_frame (window->screen->ui, window->frame->xwindow);
+      did_show = TRUE;
+    }
+
+  if (window->shaded)
+    {
+      if (window->mapped)
+        {          
+          meta_topic (META_DEBUG_WINDOW_STATE,
+                      "%s actually needs unmap (shaded)\n", window->desc);
+          meta_topic (META_DEBUG_WINDOW_STATE,
+                      "Incrementing unmaps_pending on %s for shade\n",
+                      window->desc);
+          window->mapped = FALSE;
+          window->unmaps_pending += 1;
+          meta_error_trap_push (window->display);
+          XUnmapWindow (window->display->xdisplay, window->xwindow);
+          meta_error_trap_pop (window->display, FALSE);
+        }
+
+      if (!window->iconic)
+        {
+          window->iconic = TRUE;
+          set_wm_state (window, IconicState);
+        }
+    }
+  else
+    {
+      if (!window->mapped)
+        {
+          meta_topic (META_DEBUG_WINDOW_STATE,
+                      "%s actually needs map\n", window->desc);
+          window->mapped = TRUE;
+          meta_error_trap_push (window->display);
+          XMapWindow (window->display->xdisplay, window->xwindow);
+          meta_error_trap_pop (window->display, FALSE);
+          did_show = TRUE;
+        }      
+      
+      if (window->iconic)
+        {
+          window->iconic = FALSE;
+          set_wm_state (window, NormalState);
+        }
+    }
+
+  if (did_placement)
+    {
+      if (window->xtransient_for != None)
+        {
+          MetaWindow *parent;
+
+          parent =
+            meta_display_lookup_x_window (window->display,
+                                          window->xtransient_for);
+          
+          if (parent && parent->has_focus &&
+              (window->input || window->take_focus))
+            {
+              meta_topic (META_DEBUG_FOCUS,
+                          "Focusing transient window '%s' since parent had focus\n",
+                          window->desc);
+              meta_window_focus (window,
+                                 meta_display_get_current_time (window->display));
+            }
+        }
+
+      if (window_takes_focus_on_map (window))
+        {                
+          meta_window_focus (window,
+                             meta_display_get_current_time (window->display));
+        }
+    }
+
+  if (did_show)
+    {
+      set_net_wm_state (window);
+      
+      if (window->struts)
+        {
+          meta_topic (META_DEBUG_WORKAREA,
+                      "Mapped window %s with struts, so invalidating work areas\n",
+                      window->desc);
+          invalidate_work_areas (window);
+        }
+    }
+}
+
+void
+meta_window_hide (MetaWindow *window)
+{
+  gboolean did_hide;
+  
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Hiding window %s\n", window->desc);
+
+  did_hide = FALSE;
+  
+  if (window->frame && window->frame->mapped)
+    {
+      meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n");
+      window->frame->mapped = FALSE;
+      meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
+      did_hide = TRUE;
+    }
+
+  if (window->mapped)
+    {
+      meta_topic (META_DEBUG_WINDOW_STATE,
+                  "%s actually needs unmap\n", window->desc);
+      meta_topic (META_DEBUG_WINDOW_STATE,
+                  "Incrementing unmaps_pending on %s for hide\n",
+                  window->desc);
+      window->mapped = FALSE;
+      window->unmaps_pending += 1;
+      meta_error_trap_push (window->display);      
+      XUnmapWindow (window->display->xdisplay, window->xwindow);
+      meta_error_trap_pop (window->display, FALSE);
+      did_hide = TRUE;
+    }
+
+  if (!window->iconic)
+    {
+      window->iconic = TRUE;
+      set_wm_state (window, IconicState);
+    }
+  
+  if (did_hide)
+    {
+      set_net_wm_state (window);
+      
+      if (window->struts)
+        {
+          meta_topic (META_DEBUG_WORKAREA,
+                      "Unmapped window %s with struts, so invalidating work areas\n",
+                      window->desc);
+          invalidate_work_areas (window);
+        }
+    }
+}
+
+static gboolean
+queue_calc_showing_func (MetaWindow *window,
+                         void       *data)
+{
+  meta_window_queue_calc_showing (window);
+  return TRUE;
+}
+
+void
+meta_window_minimize (MetaWindow  *window)
+{
+  meta_window_minimize_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_minimize_internal (MetaWindow  *window)
+{
+  if (!window->minimized)
+    {
+      window->minimized = TRUE;
+      meta_window_queue_calc_showing (window);
+
+      meta_window_foreach_transient (window,
+                                     queue_calc_showing_func,
+                                     NULL);
+      
+      if (window->has_focus)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Focusing top window due to minimization of focus window %s\n",
+                      window->desc);
+          meta_workspace_focus_mru_window (window->screen->active_workspace, window);
+        }
+      else
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Minimizing window %s which doesn't have the focus\n",
+                      window->desc);
+        }
+    }
+}
+
+void
+meta_window_unminimize (MetaWindow  *window)
+{
+  meta_window_unminimize_internal (window);
+  
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unminimize_internal (MetaWindow  *window)
+{
+  if (window->minimized)
+    {
+      window->minimized = FALSE;
+      meta_window_queue_calc_showing (window);
+
+      meta_window_foreach_transient (window,
+                                     queue_calc_showing_func,
+                                     NULL);
+    }
+}
+
+static void
+meta_window_save_rect (MetaWindow *window)
+{
+  if (!(window->maximized || window->fullscreen))
+    {
+      /* save size/pos as appropriate args for move_resize */
+      window->saved_rect = window->rect;
+      if (window->frame)
+        {
+          window->saved_rect.x += window->frame->rect.x;
+          window->saved_rect.y += window->frame->rect.y;
+        }
+    }
+}
+
+void
+meta_window_maximize_internal (MetaWindow    *window,
+                               MetaRectangle *saved_rect)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Maximizing %s\n", window->desc);
+  
+  if (saved_rect != NULL)
+    window->saved_rect = *saved_rect;
+  else
+    meta_window_save_rect (window);
+  
+  window->maximized = TRUE;
+  
+  recalc_window_features (window);
+  set_net_wm_state (window);
+}
+
+void
+meta_window_maximize (MetaWindow  *window)
+{
+  meta_window_maximize_real (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_maximize_real (MetaWindow  *window)
+{
+  if (!window->maximized)
+    {
+      if (window->shaded)
+        meta_window_unshade (window);
+      
+      /* if the window hasn't been placed yet, we'll maximize it then
+       */
+      if (!window->placed)
+	{
+	  window->maximize_after_placement = TRUE;
+	  return;
+	}
+
+      meta_window_maximize_internal (window, NULL);
+
+      /* move_resize with new maximization constraints
+       */
+      meta_window_queue_move_resize (window);
+    }
+}
+
+void
+meta_window_unmaximize (MetaWindow  *window)
+{
+  meta_window_unmaximize_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unmaximize_internal (MetaWindow  *window)
+{
+  if (window->maximized)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Unmaximizing %s\n", window->desc);
+      
+      window->maximized = FALSE;
+
+      /* When we unmaximize, if we're doing a mouse move also we could
+       * get the window suddenly jumping to the upper left corner of
+       * the workspace, since that's where it was when the grab op
+       * started.  So we need to update the grab state.
+       */
+      if (meta_grab_op_is_moving (window->display->grab_op) &&
+          window->display->grab_window == window)
+        {
+          window->display->grab_anchor_window_pos = window->saved_rect;
+        }
+
+      meta_window_move_resize (window,
+                               TRUE,
+                               window->saved_rect.x,
+                               window->saved_rect.y,
+                               window->saved_rect.width,
+                               window->saved_rect.height);
+
+      recalc_window_features (window);
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_make_above (MetaWindow  *window)
+{
+  window->wm_state_above = TRUE;
+  meta_window_update_layer (window);
+  meta_window_raise (window);
+  set_net_wm_state (window);
+}
+
+void
+meta_window_unmake_above (MetaWindow  *window)
+{
+  window->wm_state_above = FALSE;
+  meta_window_raise (window);
+  meta_window_update_layer (window);
+  set_net_wm_state (window);
+}
+
+void
+meta_window_make_fullscreen (MetaWindow  *window)
+{
+  meta_window_make_fullscreen_internal (window);
+  
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_make_fullscreen_internal (MetaWindow  *window)
+{
+  if (!window->fullscreen)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Fullscreening %s\n", window->desc);
+
+      if (window->shaded)
+        meta_window_unshade (window);
+
+      meta_window_save_rect (window);
+      
+      window->fullscreen = TRUE;
+
+      meta_stack_freeze (window->screen->stack);
+      meta_window_update_layer (window);
+      
+      meta_window_raise (window);
+      meta_stack_thaw (window->screen->stack);
+      
+      /* move_resize with new constraints
+       */
+      meta_window_queue_move_resize (window);
+
+      recalc_window_features (window);
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_unmake_fullscreen (MetaWindow  *window)
+{
+  meta_window_unmake_fullscreen_internal (window);
+  
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unmake_fullscreen_internal (MetaWindow  *window)
+{
+  if (window->fullscreen)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Unfullscreening %s\n", window->desc);
+      
+      window->fullscreen = FALSE;
+
+      meta_window_update_layer (window);
+      
+      meta_window_move_resize (window,
+                               TRUE,
+                               window->saved_rect.x,
+                               window->saved_rect.y,
+                               window->saved_rect.width,
+                               window->saved_rect.height);
+
+      recalc_window_features (window);
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_shade (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Shading %s\n", window->desc);
+  if (!window->shaded)
+    {
+#if 0
+      if (window->mapped)
+        {
+          /* Animation */
+          MetaRectangle starting_size;
+          MetaRectangle titlebar_size;
+          
+          meta_window_get_outer_rect (window, &starting_size);
+          if (window->frame)
+            {
+              starting_size.y += window->frame->child_y;
+              starting_size.height -= window->frame->child_y;
+            }
+          titlebar_size = starting_size;
+          titlebar_size.height = 0;
+          
+          meta_effects_draw_box_animation (window->screen,
+                                           &starting_size,
+                                           &titlebar_size,
+                                           META_SHADE_ANIMATION_LENGTH,
+                                           META_BOX_ANIM_SLIDE_UP);
+        }
+#endif
+      
+      window->shaded = TRUE;
+
+      meta_window_queue_move_resize (window);
+      meta_window_queue_calc_showing (window);
+
+      /* After queuing the calc showing, since _focus flushes it,
+       * and we need to focus the frame
+       */
+      meta_topic (META_DEBUG_FOCUS,
+                  "Re-focusing window %s after shading it\n",
+                  window->desc);
+      meta_window_focus (window,
+                         meta_display_get_current_time (window->display));
+      
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_unshade (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Unshading %s\n", window->desc);
+  if (window->shaded)
+    {
+      window->shaded = FALSE;
+      meta_window_queue_move_resize (window);
+      meta_window_queue_calc_showing (window);
+
+      /* focus the window */
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing window %s after unshading it\n",
+                  window->desc);
+      meta_window_focus (window, meta_display_get_current_time (window->display));
+
+      set_net_wm_state (window);
+    }
+}
+
+static gboolean
+unminimize_func (MetaWindow *window,
+                 void       *data)
+{
+  meta_window_unminimize (window);
+  return TRUE;
+}
+
+static void
+unminimize_window_and_all_transient_parents (MetaWindow *window)
+{
+  meta_window_unminimize (window);
+  meta_window_foreach_ancestor (window, unminimize_func, NULL);
+}
+
+void
+meta_window_activate (MetaWindow *window,
+                      guint32     timestamp)
+{
+  /* disable show desktop mode unless we're a desktop component */
+  maybe_leave_show_desktop_mode (window);
+ 
+  /* Get window on current workspace */
+  if (!meta_window_visible_on_workspace (window,
+                                         window->screen->active_workspace))
+    meta_window_change_workspace (window,
+                                  window->screen->active_workspace);
+  
+  if (window->shaded)
+    meta_window_unshade (window);
+
+  unminimize_window_and_all_transient_parents (window);
+  
+  meta_window_raise (window);
+  meta_topic (META_DEBUG_FOCUS,
+              "Focusing window %s due to activation\n",
+              window->desc);
+  meta_window_focus (window, timestamp);
+}
+
+/* returns values suitable for meta_window_move
+ * i.e. static gravity
+ */
+static void
+adjust_for_gravity (MetaWindow        *window,
+                    MetaFrameGeometry *fgeom,
+                    gboolean           coords_assume_border,
+                    int                x,
+                    int                y,
+                    int                width,
+                    int                height,
+                    int               *xp,
+                    int               *yp)
+{
+  int ref_x, ref_y;
+  int bw;
+  int child_x, child_y;
+  int frame_width, frame_height;
+  
+  if (coords_assume_border)
+    bw = window->border_width;
+  else
+    bw = 0;
+
+  if (fgeom)
+    {
+      child_x = fgeom->left_width;
+      child_y = fgeom->top_height;
+      frame_width = child_x + width + fgeom->right_width;
+      frame_height = child_y + height + fgeom->bottom_height;
+    }
+  else
+    {
+      child_x = 0;
+      child_y = 0;
+      frame_width = width;
+      frame_height = height;
+    }
+  
+  /* We're computing position to pass to window_move, which is
+   * the position of the client window (StaticGravity basically)
+   *
+   * (see WM spec description of gravity computation, but note that
+   * their formulas assume we're honoring the border width, rather
+   * than compensating for having turned it off)
+   */
+  switch (window->size_hints.win_gravity)
+    {
+    case NorthWestGravity:
+      ref_x = x;
+      ref_y = y;
+      break;
+    case NorthGravity:
+      ref_x = x + width / 2 + bw;
+      ref_y = y;
+      break;
+    case NorthEastGravity:
+      ref_x = x + width + bw * 2;
+      ref_y = y;
+      break;
+    case WestGravity:
+      ref_x = x;
+      ref_y = y + height / 2 + bw;
+      break;
+    case CenterGravity:
+      ref_x = x + width / 2 + bw;
+      ref_y = y + height / 2 + bw;
+      break;
+    case EastGravity:
+      ref_x = x + width + bw * 2;
+      ref_y = y + height / 2 + bw;
+      break;
+    case SouthWestGravity:
+      ref_x = x;
+      ref_y = y + height + bw * 2;
+      break;
+    case SouthGravity:
+      ref_x = x + width / 2 + bw;
+      ref_y = y + height + bw * 2;
+      break;
+    case SouthEastGravity:
+      ref_x = x + width + bw * 2;
+      ref_y = y + height + bw * 2;
+      break;
+    case StaticGravity:
+    default:
+      ref_x = x;
+      ref_y = y;
+      break;
+    }
+
+  switch (window->size_hints.win_gravity)
+    {
+    case NorthWestGravity:
+      *xp = ref_x + child_x;
+      *yp = ref_y + child_y;
+      break;
+    case NorthGravity:
+      *xp = ref_x - frame_width / 2 + child_x;
+      *yp = ref_y + child_y;
+      break;
+    case NorthEastGravity:
+      *xp = ref_x - frame_width + child_x;
+      *yp = ref_y + child_y;
+      break;
+    case WestGravity:
+      *xp = ref_x + child_x;
+      *yp = ref_y - frame_height / 2 + child_y;
+      break;
+    case CenterGravity:
+      *xp = ref_x - frame_width / 2 + child_x;
+      *yp = ref_y - frame_height / 2 + child_y;
+      break;
+    case EastGravity:
+      *xp = ref_x - frame_width + child_x;
+      *yp = ref_y - frame_height / 2 + child_y;
+      break;
+    case SouthWestGravity:
+      *xp = ref_x + child_x;
+      *yp = ref_y - frame_height + child_y;
+      break;
+    case SouthGravity:
+      *xp = ref_x - frame_width / 2 + child_x;
+      *yp = ref_y - frame_height + child_y;
+      break;
+    case SouthEastGravity:
+      *xp = ref_x - frame_width + child_x;
+      *yp = ref_y - frame_height + child_y;
+      break;
+    case StaticGravity:
+    default:
+      *xp = ref_x;
+      *yp = ref_y;
+      break;
+    }
+}
+
+static gboolean
+static_gravity_works (MetaDisplay *display)
+{
+  return display->static_gravity_works;
+}
+
+static void
+get_mouse_deltas_for_resize (MetaWindow *window,
+                             int         resize_gravity,
+                             int         w,
+                             int         h,
+                             int        *x_delta,
+                             int        *y_delta)
+{
+  switch (meta_x_direction_from_gravity (resize_gravity))
+    {
+    case META_RESIZE_LEFT_OR_TOP:
+      *x_delta = window->rect.width - w;
+      break;
+    case META_RESIZE_RIGHT_OR_BOTTOM:
+      *x_delta = w - window->rect.width;
+      break;
+    case META_RESIZE_CENTER:
+      /* FIXME this implies that with center gravity you have to grow
+       * in increments of two
+       */
+      *x_delta = (w - window->rect.width) / 2;
+      break;
+    }
+  
+  switch (meta_y_direction_from_gravity (resize_gravity))
+    {
+    case META_RESIZE_LEFT_OR_TOP:
+      *y_delta = window->rect.height - h;
+      break;
+    case META_RESIZE_RIGHT_OR_BOTTOM:
+      *y_delta = h - window->rect.height;
+      break;
+    case META_RESIZE_CENTER:
+      /* FIXME this implies that with center gravity you have to grow
+       * in increments of two
+       */
+      *y_delta = (h - window->rect.height) / 2;
+      break;
+    }
+}
+
+static void
+meta_window_move_resize_internal (MetaWindow  *window,
+                                  MetaMoveResizeFlags flags,
+                                  int          resize_gravity,
+                                  int          root_x_nw,
+                                  int          root_y_nw,
+                                  int          w,
+                                  int          h)
+{
+  XWindowChanges values;
+  unsigned int mask;
+  gboolean need_configure_notify;
+  MetaFrameGeometry fgeom;
+  gboolean need_move_client = FALSE;
+  gboolean need_move_frame = FALSE;
+  gboolean need_resize_client = FALSE;
+  gboolean need_resize_frame = FALSE;
+  int frame_size_dx;
+  int frame_size_dy;
+  int size_dx;
+  int size_dy;
+  gboolean is_configure_request;
+  gboolean do_gravity_adjust;
+  gboolean is_user_action;
+  gboolean configure_frame_first;
+  gboolean use_static_gravity;
+  /* used for the configure request, but may not be final
+   * destination due to StaticGravity etc.
+   */
+  int client_move_x;
+  int client_move_y;
+  int x_delta;
+  int y_delta;
+  MetaRectangle new_rect;
+  MetaRectangle old_rect;
+  
+  is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0;
+  do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0;
+  is_user_action = (flags & META_USER_MOVE_RESIZE) != 0;
+  
+  /* We don't need it in the idle queue anymore. */
+  meta_window_unqueue_move_resize (window);
+
+  old_rect = window->rect;
+  meta_window_get_position (window, &old_rect.x, &old_rect.y);
+  
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Move/resize %s to %d,%d %dx%d%s%s from %d,%d %dx%d\n",
+              window->desc, root_x_nw, root_y_nw, w, h,
+              is_configure_request ? " (configure request)" : "",
+              is_user_action ? " (user move/resize)" : "",
+              old_rect.x, old_rect.y, old_rect.width, old_rect.height);
+  
+  if (window->frame)
+    meta_frame_calc_geometry (window->frame,
+                              &fgeom);
+  
+  if (is_configure_request || do_gravity_adjust)
+    {      
+      adjust_for_gravity (window,
+                          window->frame ? &fgeom : NULL,
+                          /* configure request coords assume
+                           * the border width existed
+                           */
+                          is_configure_request,
+                          root_x_nw,
+                          root_y_nw,
+                          w, h,
+                          &root_x_nw,
+                          &root_y_nw);
+      
+      meta_topic (META_DEBUG_GEOMETRY,
+                  "Compensated position for gravity, new pos %d,%d\n",
+                  root_x_nw, root_y_nw);
+    }
+
+  get_mouse_deltas_for_resize (window, resize_gravity, w, h,
+                               &x_delta, &y_delta);
+  
+  meta_window_constrain (window,
+                         window->frame ? &fgeom : NULL,
+                         &old_rect,
+                         root_x_nw - old_rect.x,
+                         root_y_nw - old_rect.y,
+                         meta_x_direction_from_gravity (resize_gravity),
+                         x_delta,
+                         meta_y_direction_from_gravity (resize_gravity),
+                         y_delta,
+                         &new_rect);
+
+  w = new_rect.width;
+  h = new_rect.height;
+  root_x_nw = new_rect.x;
+  root_y_nw = new_rect.y;
+  
+  if (w != window->rect.width ||
+      h != window->rect.height)
+    need_resize_client = TRUE;  
+  
+  window->rect.width = w;
+  window->rect.height = h;
+  
+  if (window->frame)
+    {
+      int new_w, new_h;
+
+      new_w = window->rect.width + fgeom.left_width + fgeom.right_width;
+
+      if (window->shaded)
+        new_h = fgeom.top_height;
+      else
+        new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height;
+
+      frame_size_dx = new_w - window->frame->rect.width;
+      frame_size_dy = new_h - window->frame->rect.height;
+
+      need_resize_frame = (frame_size_dx != 0 || frame_size_dy != 0);
+      
+      window->frame->rect.width = new_w;
+      window->frame->rect.height = new_h;
+
+      meta_topic (META_DEBUG_GEOMETRY,
+                  "Calculated frame size %dx%d\n",
+                  window->frame->rect.width,
+                  window->frame->rect.height);
+    }
+  else
+    {
+      frame_size_dx = 0;
+      frame_size_dy = 0;
+    }
+
+  /* For nice effect, when growing the window we want to move/resize
+   * the frame first, when shrinking the window we want to move/resize
+   * the client first. If we grow one way and shrink the other,
+   * see which way we're moving "more"
+   *
+   * Mail from Owen subject "Suggestion: Gravity and resizing from the left"
+   * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html
+   *
+   * An annoying fact you need to know in this code is that StaticGravity
+   * does nothing if you _only_ resize or _only_ move the frame;
+   * it must move _and_ resize, otherwise you get NorthWestGravity
+   * behavior. The move and resize must actually occur, it is not
+   * enough to set CWX | CWWidth but pass in the current size/pos.
+   */
+      
+  if (window->frame)
+    {
+      int new_x, new_y;
+      int frame_pos_dx, frame_pos_dy;
+      
+      /* Compute new frame coords */
+      new_x = root_x_nw - fgeom.left_width;
+      new_y = root_y_nw - fgeom.top_height;
+
+      frame_pos_dx = new_x - window->frame->rect.x;
+      frame_pos_dy = new_y - window->frame->rect.y;
+
+      need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0);
+      
+      window->frame->rect.x = new_x;
+      window->frame->rect.y = new_y;      
+
+      /* If frame will both move and resize, then StaticGravity
+       * on the child window will kick in and implicitly move
+       * the child with respect to the frame. The implicit
+       * move will keep the child in the same place with
+       * respect to the root window. If frame only moves
+       * or only resizes, then the child will just move along
+       * with the frame.
+       */
+
+      /* window->rect.x, window->rect.y are relative to frame,
+       * remember they are the server coords
+       */
+          
+      new_x = fgeom.left_width;
+      new_y = fgeom.top_height;
+
+      if (need_resize_frame && need_move_frame &&
+          static_gravity_works (window->display))
+        {
+          /* static gravity kicks in because frame
+           * is both moved and resized
+           */
+          /* when we move the frame by frame_pos_dx, frame_pos_dy the
+           * client will implicitly move relative to frame by the
+           * inverse delta.
+           * 
+           * When moving client then frame, we move the client by the
+           * frame delta, to be canceled out by the implicit move by
+           * the inverse frame delta, resulting in a client at new_x,
+           * new_y.
+           *
+           * When moving frame then client, we move the client
+           * by the same delta as the frame, because the client
+           * was "left behind" by the frame - resulting in a client
+           * at new_x, new_y.
+           *
+           * In both cases we need to move the client window
+           * in all cases where we had to move the frame window.
+           */
+          
+          client_move_x = new_x + frame_pos_dx;
+          client_move_y = new_y + frame_pos_dy;
+
+          if (need_move_frame)
+            need_move_client = TRUE;
+
+          use_static_gravity = TRUE;
+        }
+      else
+        {
+          client_move_x = new_x;
+          client_move_y = new_y;
+
+          if (client_move_x != window->rect.x ||
+              client_move_y != window->rect.y)
+            need_move_client = TRUE;
+
+          use_static_gravity = FALSE;
+        }
+
+      /* This is the final target position, but not necessarily what
+       * we pass to XConfigureWindow, due to StaticGravity implicit
+       * movement.
+       */      
+      window->rect.x = new_x;
+      window->rect.y = new_y;
+    }
+  else
+    {
+      if (root_x_nw != window->rect.x ||
+          root_y_nw != window->rect.y)
+        need_move_client = TRUE;
+      
+      window->rect.x = root_x_nw;
+      window->rect.y = root_y_nw;
+
+      client_move_x = window->rect.x;
+      client_move_y = window->rect.y;
+
+      use_static_gravity = FALSE;
+    }
+
+  /* If frame extents have changed, fill in other frame fields and
+     change frame's extents property. */
+  if (window->frame &&
+      (window->frame->child_x != fgeom.left_width ||
+          window->frame->child_y != fgeom.top_height ||
+          window->frame->right_width != fgeom.right_width ||
+          window->frame->bottom_height != fgeom.bottom_height))
+    {
+      window->frame->child_x = fgeom.left_width;
+      window->frame->child_y = fgeom.top_height;
+      window->frame->right_width = fgeom.right_width;
+      window->frame->bottom_height = fgeom.bottom_height;
+
+      update_net_frame_extents (window);
+    }
+
+  /* See ICCCM 4.1.5 for when to send ConfigureNotify */
+  
+  need_configure_notify = FALSE;
+  
+  /* If this is a configure request and we change nothing, then we
+   * must send configure notify.
+   */
+  if  (is_configure_request &&
+       !(need_move_client || need_move_frame ||
+         need_resize_client || need_resize_frame ||
+         window->border_width != 0))
+    need_configure_notify = TRUE;
+
+  /* We must send configure notify if we move but don't resize, since
+   * the client window may not get a real event
+   */
+  if ((need_move_client || need_move_frame) &&
+      !(need_resize_client || need_resize_frame))
+    need_configure_notify = TRUE;
+  
+  /* The rest of this function syncs our new size/pos with X as
+   * efficiently as possible
+   */
+
+  /* configure frame first if we grow more than we shrink
+   */
+  size_dx = w - window->rect.width;
+  size_dy = h - window->rect.height;
+
+  configure_frame_first = (size_dx + size_dy >= 0);
+
+  if (use_static_gravity)
+    meta_window_set_gravity (window, StaticGravity);  
+  
+  if (configure_frame_first && window->frame)
+    meta_frame_sync_to_window (window->frame,
+                               resize_gravity,
+                               need_move_frame, need_resize_frame);
+
+  values.border_width = 0;
+  values.x = client_move_x;
+  values.y = client_move_y;
+  values.width = window->rect.width;
+  values.height = window->rect.height;
+  
+  mask = 0;
+  if (is_configure_request && window->border_width != 0)
+    mask |= CWBorderWidth; /* must force to 0 */
+  if (need_move_client)
+    mask |= (CWX | CWY);
+  if (need_resize_client)
+    mask |= (CWWidth | CWHeight);
+
+  if (mask != 0)
+    {
+      {
+        int newx, newy;
+        meta_window_get_position (window, &newx, &newy);
+        meta_topic (META_DEBUG_GEOMETRY,
+                    "Syncing new client geometry %d,%d %dx%d, border: %s pos: %s size: %s\n",
+                    newx, newy,
+                    window->rect.width, window->rect.height,
+                    mask & CWBorderWidth ? "true" : "false",
+                    need_move_client ? "true" : "false",
+                    need_resize_client ? "true" : "false");
+      }
+      
+      meta_error_trap_push (window->display);
+      XConfigureWindow (window->display->xdisplay,
+                        window->xwindow,
+                        mask,
+                        &values);
+      meta_error_trap_pop (window->display, FALSE);
+    }
+
+  if (!configure_frame_first && window->frame)
+    meta_frame_sync_to_window (window->frame,
+                               resize_gravity,
+                               need_move_frame, need_resize_frame);  
+
+  /* Put gravity back to be nice to lesser window managers */
+  if (use_static_gravity)
+    meta_window_set_gravity (window, NorthWestGravity);  
+  
+  if (need_configure_notify)
+    send_configure_notify (window);
+
+  if (is_user_action)
+    {
+      window->user_has_move_resized = TRUE;
+
+      window->user_rect.width = window->rect.width;
+      window->user_rect.height = window->rect.height;
+
+      meta_window_get_position (window, 
+                                &window->user_rect.x,
+                                &window->user_rect.y);
+    }
+  
+  if (need_move_frame || need_resize_frame ||
+      need_move_client || need_resize_client)
+    {
+      int newx, newy;
+
+      meta_window_get_position (window, &newx, &newy);
+      meta_topic (META_DEBUG_GEOMETRY,
+                  "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
+                  newx, newy, window->rect.width, window->rect.height,
+                  window->user_rect.x, window->user_rect.y,
+                  window->user_rect.width, window->user_rect.height);
+
+      if (is_user_action)
+	meta_history_remember_window (window);
+      if (!is_user_action)
+	meta_history_commit_window ();
+    }
+  else
+    {
+      meta_topic (META_DEBUG_GEOMETRY, "Size/position not modified\n");
+    }
+  
+  meta_window_refresh_resize_popup (window);
+  
+  /* Invariants leaving this function are:
+   *   a) window->rect and frame->rect reflect the actual
+   *      server-side size/pos of window->xwindow and frame->xwindow
+   *   b) all constraints are obeyed by window->rect and frame->rect
+   */
+}
+
+void
+meta_window_resize (MetaWindow  *window,
+                    gboolean     user_op,
+                    int          w,
+                    int          h)
+{
+  int x, y;
+
+  meta_window_get_position (window, &x, &y);
+  
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    NorthWestGravity,
+                                    x, y, w, h);
+
+}
+
+void
+meta_window_move (MetaWindow  *window,
+                  gboolean     user_op,
+                  int          root_x_nw,
+                  int          root_y_nw)
+{
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    NorthWestGravity,
+                                    root_x_nw, root_y_nw,
+                                    window->rect.width,
+                                    window->rect.height);
+}
+
+void
+meta_window_move_resize (MetaWindow  *window,
+                         gboolean     user_op,
+                         int          root_x_nw,
+                         int          root_y_nw,
+                         int          w,
+                         int          h)
+{
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    NorthWestGravity,
+                                    root_x_nw, root_y_nw,
+                                    w, h);
+}
+
+void
+meta_window_resize_with_gravity (MetaWindow *window,
+                                 gboolean     user_op,
+                                 int          w,
+                                 int          h,
+                                 int          gravity)
+{
+  int x, y;
+
+  meta_window_get_position (window, &x, &y);
+  
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    gravity,
+                                    x, y, w, h);
+}
+
+void
+meta_window_move_resize_now (MetaWindow  *window)
+{
+  int x, y;
+
+  /* If constraints have changed then we'll snap back to wherever
+   * the user had the window
+   */
+  meta_window_get_user_position (window, &x, &y);
+
+  /* This used to use the user width/height if the user hadn't resized,
+   * but it turns out that breaks things pretty often, because configure
+   * requests from the app or size hints changes from the app frequently
+   * reflect user actions such as changing terminal font size
+   * or expanding a disclosure triangle.
+   */
+  meta_window_move_resize (window, FALSE, x, y,
+                           window->rect.width,
+                           window->rect.height);
+}
+
+static void
+check_maximize_to_work_area (MetaWindow          *window,
+                             const MetaRectangle *work_area)
+{
+  /* If we now fill the screen, maximize.
+   * the point here is that fill horz + fill vert = maximized
+   */
+  MetaRectangle rect;
+
+  if (!window->has_maximize_func)
+    return;
+  
+  meta_window_get_outer_rect (window, &rect);
+
+  if ( rect.x >= work_area->x &&
+       rect.y >= work_area->y &&
+       (((work_area->width - work_area->x) - rect.width) <
+        window->size_hints.width_inc) &&
+       (((work_area->height - work_area->y) - rect.height) <
+        window->size_hints.height_inc) )
+    meta_window_maximize (window);
+}
+
+void
+meta_window_fill_horizontal (MetaWindow  *window)
+{
+  MetaRectangle work_area;
+  int x, y, w, h;
+  
+  meta_window_get_user_position (window, &x, &y);
+
+  w = window->rect.width;
+  h = window->rect.height;
+  
+  meta_window_get_work_area_current_xinerama (window, &work_area);
+  
+  x = work_area.x;
+  w = work_area.width;
+  
+  if (window->frame != NULL)
+    {
+      x += window->frame->child_x;
+      w -= (window->frame->child_x + window->frame->right_width);
+    }
+  
+  meta_window_move_resize (window, TRUE,
+                           x, y, w, h);
+
+  check_maximize_to_work_area (window, &work_area);
+}
+
+void
+meta_window_fill_vertical (MetaWindow  *window)
+{
+  MetaRectangle work_area;
+  int x, y, w, h;
+  
+  meta_window_get_user_position (window, &x, &y);
+
+  w = window->rect.width;
+  h = window->rect.height;
+
+  meta_window_get_work_area_current_xinerama (window, &work_area);
+
+  y = work_area.y;
+  h = work_area.height;
+  
+  if (window->frame != NULL)
+    {
+      y += window->frame->child_y;
+      h -= (window->frame->child_y + window->frame->bottom_height);
+    }
+  
+  meta_window_move_resize (window, TRUE,
+                           x, y, w, h);
+
+  check_maximize_to_work_area (window, &work_area);
+}
+
+static guint move_resize_idle = 0;
+static GSList *move_resize_pending = NULL;
+
+static gboolean
+idle_move_resize (gpointer data)
+{
+  GSList *tmp;
+  GSList *copy;
+
+  meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue\n");
+
+  /* Work with a copy, for reentrancy. The allowed reentrancy isn't
+   * complete; destroying a window while we're in here would result in
+   * badness. But it's OK to queue/unqueue move_resizes.
+   */
+  copy = g_slist_copy (move_resize_pending);
+  g_slist_free (move_resize_pending);
+  move_resize_pending = NULL;
+  move_resize_idle = 0;
+
+  destroying_windows_disallowed += 1;
+  
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      /* As a side effect, sets window->move_resize_queued = FALSE */
+      meta_window_move_resize_now (window); 
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (copy);
+
+  destroying_windows_disallowed -= 1;
+  
+  return FALSE;
+}
+
+void
+meta_window_unqueue_move_resize (MetaWindow *window)
+{
+  if (!window->move_resize_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Removing %s from the move_resize queue\n",
+              window->desc);
+
+  /* Note that window may not actually be in move_resize_pending
+   * because it may have been in "copy" inside the idle handler
+   */
+  move_resize_pending = g_slist_remove (move_resize_pending, window);
+  window->move_resize_queued = FALSE;
+  
+  if (move_resize_pending == NULL &&
+      move_resize_idle != 0)
+    {
+      g_source_remove (move_resize_idle);
+      move_resize_idle = 0;
+    }
+}
+
+void
+meta_window_flush_move_resize (MetaWindow *window)
+{
+  if (window->move_resize_queued)
+    {
+      meta_window_unqueue_move_resize (window);
+      meta_window_move_resize_now (window);
+    }
+}
+
+/* The move/resize queue is only used when we need to
+ * recheck the constraints on the window, e.g. when
+ * maximizing or when changing struts. Configure requests
+ * and such always have to be handled synchronously,
+ * they can't be done via a queue.
+ */
+void
+meta_window_queue_move_resize (MetaWindow  *window)
+{
+  if (window->unmanaging)
+    return;
+
+  if (window->move_resize_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Putting %s in the move_resize queue\n",
+              window->desc);
+  
+  window->move_resize_queued = TRUE;
+  
+  if (move_resize_idle == 0)
+    move_resize_idle = g_idle_add_full (META_PRIORITY_RESIZE,
+                                        idle_move_resize, NULL, NULL);
+  
+  move_resize_pending = g_slist_prepend (move_resize_pending, window);
+}
+
+void
+meta_window_get_position (MetaWindow  *window,
+                          int         *x,
+                          int         *y)
+{
+  if (window->frame)
+    {
+      if (x)
+        *x = window->frame->rect.x + window->frame->child_x;
+      if (y)
+        *y = window->frame->rect.y + window->frame->child_y;
+    }
+  else
+    {
+      if (x)
+        *x = window->rect.x;
+      if (y)
+        *y = window->rect.y;
+    }
+}
+
+void
+meta_window_get_user_position  (MetaWindow  *window,
+                                int         *x,
+                                int         *y)
+{
+  if (window->user_has_move_resized)
+    {
+      if (x)
+        *x = window->user_rect.x;
+      if (y)
+        *y = window->user_rect.y;
+    }
+  else
+    {
+      meta_window_get_position (window, x, y);
+    }
+}
+
+void
+meta_window_get_gravity_position (MetaWindow  *window,
+                                  int         *root_x,
+                                  int         *root_y)
+{
+  MetaRectangle frame_extents;
+  int w, h;
+  int x, y;
+  
+  w = window->rect.width;
+  h = window->rect.height;
+
+  if (window->size_hints.win_gravity == StaticGravity)
+    {
+      frame_extents = window->rect;
+      if (window->frame)
+        {
+          frame_extents.x = window->frame->rect.x + window->frame->child_x;
+          frame_extents.y = window->frame->rect.y + window->frame->child_y;
+        }
+    }
+  else
+    {
+      if (window->frame == NULL)
+        frame_extents = window->rect;
+      else
+        frame_extents = window->frame->rect;
+    }
+
+  x = frame_extents.x;
+  y = frame_extents.y;
+  
+  switch (window->size_hints.win_gravity)
+    {
+    case NorthGravity:
+    case CenterGravity:
+    case SouthGravity:
+      /* Find center of frame. */
+      x += frame_extents.width / 2;
+      /* Center client window on that point. */
+      x -= w / 2;
+      break;
+      
+    case SouthEastGravity:
+    case EastGravity:
+    case NorthEastGravity:
+      /* Find right edge of frame */
+      x += frame_extents.width;
+      /* Align left edge of client at that point. */
+      x -= w;
+      break;
+    default:
+      break;
+    }
+  
+  switch (window->size_hints.win_gravity)
+    {
+    case WestGravity:
+    case CenterGravity:
+    case EastGravity:
+      /* Find center of frame. */
+      y += frame_extents.height / 2;
+      /* Center client window there. */
+      y -= h / 2;
+      break;
+    case SouthWestGravity:
+    case SouthGravity:
+    case SouthEastGravity:
+      /* Find south edge of frame */
+      y += frame_extents.height;
+      /* Place bottom edge of client there */
+      y -= h;
+      break;
+    default:
+      break;
+    }
+  
+  if (root_x)
+    *root_x = x;
+  if (root_y)
+    *root_y = y;
+}
+
+void
+meta_window_get_geometry (MetaWindow  *window,
+                          int         *x,
+                          int         *y,
+                          int         *width,
+                          int         *height)
+{
+  meta_window_get_gravity_position (window, x, y);
+
+  *width = (window->rect.width - window->size_hints.base_width) /
+    window->size_hints.width_inc;
+  *height = (window->rect.height - window->size_hints.base_height) /
+    window->size_hints.height_inc;
+}
+
+void
+meta_window_get_outer_rect (MetaWindow    *window,
+                            MetaRectangle *rect)
+{
+  if (window->frame)
+    *rect = window->frame->rect;
+  else
+    *rect = window->rect;
+}
+
+const char*
+meta_window_get_startup_id (MetaWindow *window)
+{
+  if (window->startup_id == NULL)
+    {
+      MetaGroup *group;
+
+      group = meta_window_get_group (window);
+
+      if (group != NULL)
+        return meta_group_get_startup_id (group);
+    }
+
+  return window->startup_id;
+}
+
+void
+meta_window_focus (MetaWindow  *window,
+                   Time         timestamp)
+{  
+  meta_topic (META_DEBUG_FOCUS,
+              "Setting input focus to window %s, input: %d take_focus: %d\n",
+              window->desc, window->input, window->take_focus);
+  
+  if (window->display->grab_window &&
+      window->display->grab_window->all_keys_grabbed)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Current focus window %s has global keygrab, not focusing window %s after all\n",
+                  window->display->grab_window->desc, window->desc);
+      return;
+    }
+
+  meta_window_flush_calc_showing (window);
+
+  if (!window->mapped && !window->shaded)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Window %s is not showing, not focusing after all\n",
+                  window->desc);
+      return;
+    }
+  
+  /* For output-only or shaded windows, focus the frame.
+   * This seems to result in the client window getting key events
+   * though, so I don't know if it's icccm-compliant.
+   *
+   * Still, we have to do this or keynav breaks for these windows.
+   */
+  if (window->frame &&
+      (window->shaded ||
+       !(window->input || window->take_focus)))
+    {
+      if (window->frame)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Focusing frame of %s\n", window->desc);
+          XSetInputFocus (window->display->xdisplay,
+                          window->frame->xwindow,
+                          RevertToPointerRoot,
+                          timestamp);
+          window->display->expected_focus_window = window;
+        }
+    }
+  else
+    {
+      meta_error_trap_push (window->display);
+      
+      if (window->input)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Calling XSetInputFocus() on client window %s since input = true\n",
+                      window->desc);
+          XSetInputFocus (window->display->xdisplay,
+                          window->xwindow,
+                          RevertToPointerRoot,
+                          timestamp);
+          window->display->expected_focus_window = window;
+        }
+      
+      if (window->take_focus)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
+                      window->desc);
+          meta_window_send_icccm_message (window,
+                                          window->display->atom_wm_take_focus,
+                                          timestamp);
+          window->display->expected_focus_window = window;
+        }
+      
+      meta_error_trap_pop (window->display, FALSE);
+    }
+}
+
+static void
+meta_window_change_workspace_without_transients (MetaWindow    *window,
+                                                 MetaWorkspace *workspace)
+{
+  GList *next;
+  
+  meta_verbose ("Changing window %s to workspace %d\n",
+                window->desc, meta_workspace_index (workspace));
+  
+  /* unstick if stuck. meta_window_unstick would call 
+   * meta_window_change_workspace recursively if the window
+   * is not in the active workspace.
+   */
+  if (window->on_all_workspaces)
+    meta_window_unstick (window);
+
+  /* See if we're already on this space. If not, make sure we are */
+  if (g_list_find (window->workspaces, workspace) == NULL)
+    meta_workspace_add_window (workspace, window);
+  
+  /* Remove from all other spaces */
+  next = window->workspaces;
+  while (next != NULL)
+    {
+      MetaWorkspace *remove;
+      remove = next->data;
+      next = next->next;
+
+      if (remove != workspace)
+        meta_workspace_remove_window (remove, window);
+    }
+
+  /* list size == 1 */
+  g_assert (window->workspaces != NULL);
+  g_assert (window->workspaces->next == NULL);
+}
+
+static gboolean
+change_workspace_foreach (MetaWindow *window,
+                          void       *data)
+{
+  meta_window_change_workspace_without_transients (window, data);
+  return TRUE;
+}
+
+void
+meta_window_change_workspace (MetaWindow    *window,
+                              MetaWorkspace *workspace)
+{
+  meta_window_change_workspace_without_transients (window, workspace);
+
+  meta_window_foreach_transient (window, change_workspace_foreach,
+                                 workspace);
+}
+
+void
+meta_window_stick (MetaWindow  *window)
+{
+  meta_window_stick_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_stick_internal (MetaWindow  *window)
+{
+  GList *tmp; 
+  MetaWorkspace *workspace;
+
+  meta_verbose ("Sticking window %s current on_all_workspaces = %d\n",
+                window->desc, window->on_all_workspaces);
+  
+  if (window->on_all_workspaces)
+    return;
+
+  /* We don't change window->workspaces, because we revert
+   * to that original workspace list if on_all_workspaces is
+   * toggled back off.
+   */
+  window->on_all_workspaces = TRUE;
+
+  /* We do, however, change the MRU lists of all the workspaces
+   */
+  tmp = window->screen->workspaces;
+  while (tmp)
+    {
+      workspace = (MetaWorkspace *) tmp->data;
+      if (!g_list_find (workspace->mru_list, window))
+        workspace->mru_list = g_list_append (workspace->mru_list, window);
+
+      tmp = tmp->next;
+    }
+
+  meta_window_set_current_workspace_hint (window);
+  
+  meta_window_queue_calc_showing (window);
+}
+
+void
+meta_window_unstick (MetaWindow  *window)
+{
+  meta_window_unstick_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unstick_internal (MetaWindow  *window)
+{
+  GList *tmp;
+  MetaWorkspace *workspace;
+
+  if (!window->on_all_workspaces)
+    return;
+
+  /* Revert to window->workspaces */
+
+  window->on_all_workspaces = FALSE;
+
+  /* Remove window from MRU lists that it doesn't belong in */
+  tmp = window->screen->workspaces;
+  while (tmp)
+    {
+      workspace = (MetaWorkspace *) tmp->data;
+      if (!meta_workspace_contains_window (workspace, window))
+        workspace->mru_list = g_list_remove (workspace->mru_list, window);
+      tmp = tmp->next;
+    }
+
+  /* We change ourselves to the active workspace, since otherwise you'd get
+   * a weird window-vaporization effect. Once we have UI for being
+   * on more than one workspace this should probably be add_workspace
+   * not change_workspace.
+   */
+  if (!meta_workspace_contains_window (window->screen->active_workspace,
+                                       window))
+    meta_window_change_workspace (window, window->screen->active_workspace);
+  
+  meta_window_set_current_workspace_hint (window);
+  
+  meta_window_queue_calc_showing (window);
+}
+
+unsigned long
+meta_window_get_net_wm_desktop (MetaWindow *window)
+{
+  if (window->on_all_workspaces ||
+      g_list_length (window->workspaces) > 1)
+    return 0xFFFFFFFF;
+  else
+    return meta_workspace_index (window->workspaces->data);
+}
+
+static void
+update_net_frame_extents (MetaWindow *window)
+{
+  unsigned long data[4] = { 0, 0, 0, 0 };
+
+  if (window->frame)
+    {
+      /* Left */
+      data[0] = window->frame->child_x;
+      /* Right */
+      data[1] = window->frame->right_width;
+      /* Top */
+      data[2] = window->frame->child_y;
+      /* Bottom */
+      data[3] = window->frame->bottom_height;
+    }
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Setting _NET_FRAME_EXTENTS on managed window 0x%lx "
+              "to top = %ld, left = %ld, bottom = %ld, right = %ld\n",
+              window->xwindow, data[0], data[1], data[2], data[3]);
+
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_frame_extents,
+                   XA_CARDINAL,
+                   32, PropModeReplace, (guchar*) data, 4);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+void
+meta_window_set_current_workspace_hint (MetaWindow *window)
+{
+  /* FIXME if on more than one workspace, we claim to be "sticky",
+   * the WM spec doesn't say what to do here.
+   */
+  unsigned long data[1];
+
+  if (window->workspaces == NULL)
+    {
+      /* this happens when unmanaging windows */      
+      return;
+    }
+  
+  data[0] = meta_window_get_net_wm_desktop (window);
+
+  meta_verbose ("Setting _NET_WM_DESKTOP of %s to %ld\n",
+                window->desc, data[0]);
+  
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_wm_desktop,
+                   XA_CARDINAL,
+                   32, PropModeReplace, (guchar*) data, 1);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+void
+meta_window_raise (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Raising window %s\n", window->desc);
+
+  meta_stack_raise (window->screen->stack, window);
+}
+
+void
+meta_window_lower (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Lowering window %s\n", window->desc);
+
+  meta_stack_lower (window->screen->stack, window);
+}
+
+void
+meta_window_send_icccm_message (MetaWindow *window,
+                                Atom        atom,
+                                Time        timestamp)
+{
+  /* This comment and code are from twm, copyright
+   * Open Group, Evans & Sutherland, etc.
+   */
+  
+  /*
+   * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
+   * client messages will have the following form:
+   *
+   *     event type	ClientMessage
+   *     message type	_XA_WM_PROTOCOLS
+   *     window		tmp->w
+   *     format		32
+   *     data[0]		message atom
+   *     data[1]		time stamp
+   */
+  
+    XClientMessageEvent ev;
+    
+    ev.type = ClientMessage;
+    ev.window = window->xwindow;
+    ev.message_type = window->display->atom_wm_protocols;
+    ev.format = 32;
+    ev.data.l[0] = atom;
+    ev.data.l[1] = timestamp;
+
+    meta_error_trap_push (window->display);
+    XSendEvent (window->display->xdisplay,
+                window->xwindow, False, 0, (XEvent*) &ev);
+    meta_error_trap_pop (window->display, FALSE);
+}
+
+gboolean
+meta_window_configure_request (MetaWindow *window,
+                               XEvent     *event)
+{
+  int x, y, width, height;
+  gboolean only_resize;
+  gboolean allow_position_change;
+  gboolean in_grab_op;
+
+  /* We ignore configure requests while the user is moving/resizing
+   * the window, since these represent the app sucking and fighting
+   * the user, most likely due to a bug in the app (e.g. pfaedit
+   * seemed to do this)
+   *
+   * Still have to do the ConfigureNotify and all, but pretend the
+   * app asked for the current size/position instead of the new one.
+   */  
+  in_grab_op = FALSE;
+  if (window->display->grab_op != META_GRAB_OP_NONE &&
+      window == window->display->grab_window)
+    {
+      switch (window->display->grab_op)
+        {
+        case META_GRAB_OP_MOVING:
+        case META_GRAB_OP_RESIZING_SE:
+        case META_GRAB_OP_RESIZING_S:
+        case META_GRAB_OP_RESIZING_SW:
+        case META_GRAB_OP_RESIZING_N:
+        case META_GRAB_OP_RESIZING_NE:
+        case META_GRAB_OP_RESIZING_NW:
+        case META_GRAB_OP_RESIZING_W:
+        case META_GRAB_OP_RESIZING_E:
+          in_grab_op = TRUE;
+          break;
+        default:
+          break;
+        }
+    }
+  
+  /* it's essential to use only the explicitly-set fields,
+   * and otherwise use our current up-to-date position.
+   *
+   * Otherwise you get spurious position changes when the app changes
+   * size, for example, if window->rect is not in sync with the
+   * server-side position in effect when the configure request was
+   * generated.
+   */
+
+  meta_window_get_gravity_position (window, &x, &y);
+
+  only_resize = TRUE;
+
+  allow_position_change = FALSE;
+  
+  if (meta_prefs_get_disable_workarounds ())
+    {
+      if (window->type == META_WINDOW_DIALOG ||
+          window->type == META_WINDOW_MODAL_DIALOG ||
+          window->type == META_WINDOW_SPLASHSCREEN)
+        ; /* No position change for these */
+      else if ((window->size_hints.flags & PPosition) ||
+               /* USPosition is just stale if window is placed;
+                * no --geometry involved here.
+                */
+               ((window->size_hints.flags & USPosition) &&
+                !window->placed))
+        allow_position_change = TRUE;
+    }
+  else
+    {
+      allow_position_change = TRUE;
+    }
+
+  if (in_grab_op)
+    allow_position_change = FALSE;
+  
+  if (allow_position_change)
+    {
+      if (event->xconfigurerequest.value_mask & CWX)
+        x = event->xconfigurerequest.x;
+      
+      if (event->xconfigurerequest.value_mask & CWY)
+        y = event->xconfigurerequest.y;
+
+      if (event->xconfigurerequest.value_mask & (CWX | CWY))
+        {
+          only_resize = FALSE;
+
+          /* Once manually positioned, windows shouldn't be placed
+           * by the window manager.
+           */
+          window->placed = TRUE;
+        }
+    }
+  else
+    {
+      meta_topic (META_DEBUG_GEOMETRY,
+		  "Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %d\n", 
+		  window->desc, window->size_hints.flags & PPosition, 
+		  window->size_hints.flags & USPosition,
+		  window->type);     
+    }
+
+  width = window->rect.width;
+  height = window->rect.height;
+
+  if (!in_grab_op)
+    {
+      if (event->xconfigurerequest.value_mask & CWWidth)
+        width = event->xconfigurerequest.width;
+      
+      if (event->xconfigurerequest.value_mask & CWHeight)
+        height = event->xconfigurerequest.height;
+    }
+
+  /* ICCCM 4.1.5 */
+  
+  /* Note that x, y is the corner of the window border,
+   * and width, height is the size of the window inside
+   * its border, but that we always deny border requests
+   * and give windows a border of 0. But we save the
+   * requested border here.
+   */
+  window->border_width = event->xconfigurerequest.border_width;
+
+  /* We're ignoring the value_mask here, since sizes
+   * not in the mask will be the current window geometry.
+   */
+  
+  window->size_hints.x = x;
+  window->size_hints.y = y;
+  window->size_hints.width = width;
+  window->size_hints.height = height;
+
+  /* FIXME passing the gravity on only_resize thing is kind of crack-rock.
+   * Basically I now have several ways of handling gravity, and things
+   * don't make too much sense. I think I am doing the math in a couple
+   * places and could do it in only one function, and remove some of the
+   * move_resize_internal arguments.
+   */
+  
+  meta_window_move_resize_internal (window, META_IS_CONFIGURE_REQUEST,
+                                    only_resize ?
+                                    window->size_hints.win_gravity : NorthWestGravity,
+                                    window->size_hints.x,
+                                    window->size_hints.y,
+                                    window->size_hints.width,
+                                    window->size_hints.height);
+
+  /* Handle stacking. We only handle raises/lowers, mostly because
+   * stack.c really can't deal with anything else.  I guess we'll fix
+   * that if a client turns up that really requires it. Only a very
+   * few clients even require the raise/lower (and in fact all client
+   * attempts to deal with stacking order are essentially broken,
+   * since they have no idea what other clients are involved or how
+   * the stack looks).
+   *
+   * I'm pretty sure no interesting client uses TopIf, BottomIf, or
+   * Opposite anyway, so the only possible missing thing is
+   * Above/Below with a sibling set. For now we just pretend there's
+   * never a sibling set and always do the full raise/lower instead of
+   * the raise-just-above/below-sibling.
+   */
+  if (event->xconfigurerequest.value_mask & CWStackMode)
+    {
+      switch (event->xconfigurerequest.detail)
+        {
+        case Above:
+          meta_window_raise (window);
+          break;
+        case Below:
+          meta_window_lower (window);
+          break;
+        case TopIf:
+        case BottomIf:
+        case Opposite:
+          break;
+        }
+    }      
+  
+  return TRUE;
+}
+
+gboolean
+meta_window_property_notify (MetaWindow *window,
+                             XEvent     *event)
+{
+  return process_property_notify (window, &event->xproperty);  
+}
+
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
+#define _NET_WM_MOVERESIZE_SIZE_TOP          1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
+#define _NET_WM_MOVERESIZE_MOVE              8
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10
+
+gboolean
+meta_window_client_message (MetaWindow *window,
+                            XEvent     *event)
+{
+  MetaDisplay *display;
+
+  display = window->display;
+  
+  if (event->xclient.message_type ==
+      display->atom_net_close_window)
+    {
+      /* I think the wm spec should maybe put a time
+       * in this message, CurrentTime here is sort of
+       * bogus. But it rarely matters most likely.
+       */
+      meta_window_delete (window, meta_display_get_current_time (window->display));
+
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_wm_desktop)
+    {
+      int space;
+      MetaWorkspace *workspace;
+              
+      space = event->xclient.data.l[0];
+              
+      meta_verbose ("Request to move %s to workspace %d\n",
+                    window->desc, space);
+
+      workspace =
+        meta_screen_get_workspace_by_index (window->screen,
+                                            space);
+
+      if (workspace)
+        {
+          if (window->on_all_workspaces)
+            meta_window_unstick (window);
+          meta_window_change_workspace (window, workspace);
+        }
+      else if (space == (int) 0xFFFFFFFF)
+        {
+          meta_window_stick (window);
+        }
+      else
+        {
+          meta_verbose ("No such workspace %d for screen\n", space);
+        }
+
+      meta_verbose ("Window %s now on_all_workspaces = %d\n",
+                    window->desc, window->on_all_workspaces);
+      
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_wm_state)
+    {
+      gulong action;
+      Atom first;
+      Atom second;
+
+      action = event->xclient.data.l[0];
+      first = event->xclient.data.l[1];
+      second = event->xclient.data.l[2];
+      
+      if (meta_is_verbose ())
+        {
+          char *str1;
+          char *str2;
+
+          meta_error_trap_push_with_return (display);
+          str1 = XGetAtomName (display->xdisplay, first);
+          if (meta_error_trap_pop_with_return (display, TRUE) != Success)
+            str1 = NULL;
+
+          meta_error_trap_push_with_return (display);
+          str2 = XGetAtomName (display->xdisplay, second); 
+          if (meta_error_trap_pop_with_return (display, TRUE) != Success)
+            str2 = NULL;
+          
+          meta_verbose ("Request to change _NET_WM_STATE action %ld atom1: %s atom2: %s\n",
+                        action,
+                        str1 ? str1 : "(unknown)",
+                        str2 ? str2 : "(unknown)");
+
+          meta_XFree (str1);
+          meta_XFree (str2);
+        }
+
+      if (first == display->atom_net_wm_state_shaded ||
+          second == display->atom_net_wm_state_shaded)
+        {
+          gboolean shade;
+
+          shade = (action == _NET_WM_STATE_ADD ||
+                   (action == _NET_WM_STATE_TOGGLE && !window->shaded));
+          if (shade && window->has_shade_func)
+            meta_window_shade (window);
+          else
+            meta_window_unshade (window);
+        }
+
+      if (first == display->atom_net_wm_state_fullscreen ||
+          second == display->atom_net_wm_state_fullscreen)
+        {
+          gboolean make_fullscreen;
+
+          make_fullscreen = (action == _NET_WM_STATE_ADD ||
+                             (action == _NET_WM_STATE_TOGGLE && !window->fullscreen));
+          if (make_fullscreen && window->has_fullscreen_func)
+            meta_window_make_fullscreen (window);
+          else
+            meta_window_unmake_fullscreen (window);
+        }
+      
+      if (first == display->atom_net_wm_state_maximized_horz ||
+          second == display->atom_net_wm_state_maximized_horz ||
+          first == display->atom_net_wm_state_maximized_vert ||
+          second == display->atom_net_wm_state_maximized_vert)
+        {
+          gboolean max;
+
+          max = (action == _NET_WM_STATE_ADD ||
+                 (action == _NET_WM_STATE_TOGGLE && !window->maximized));
+          if (max && window->has_maximize_func)
+            meta_window_maximize (window);
+          else
+            meta_window_unmaximize (window);
+        }
+
+      if (first == display->atom_net_wm_state_modal ||
+          second == display->atom_net_wm_state_modal)
+        {
+          window->wm_state_modal =
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->wm_state_modal);
+          
+          recalc_window_type (window);
+          meta_window_queue_move_resize (window);
+        }
+
+      if (first == display->atom_net_wm_state_skip_pager ||
+          second == display->atom_net_wm_state_skip_pager)
+        {
+          window->wm_state_skip_pager = 
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->skip_pager);
+
+          recalc_window_features (window);
+          set_net_wm_state (window);
+        }
+
+      if (first == display->atom_net_wm_state_skip_taskbar ||
+          second == display->atom_net_wm_state_skip_taskbar)
+        {
+          window->wm_state_skip_taskbar =
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar);
+
+          recalc_window_features (window);
+          set_net_wm_state (window);
+        }
+
+      if (first == display->atom_net_wm_state_above ||
+          second == display->atom_net_wm_state_above)
+        {
+          window->wm_state_above = 
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->wm_state_above);
+
+          meta_window_update_layer (window);
+          set_net_wm_state (window);
+        }
+
+      if (first == display->atom_net_wm_state_below ||
+          second == display->atom_net_wm_state_below)
+        {
+          window->wm_state_below = 
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->wm_state_below);
+
+          meta_window_update_layer (window);
+          set_net_wm_state (window);
+        }
+      
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_wm_change_state)
+    {
+      meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n",
+                    event->xclient.data.l[0]);
+      if (event->xclient.data.l[0] == IconicState &&
+          window->has_minimize_func)
+        meta_window_minimize (window);
+
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_wm_moveresize)
+    {
+      int x_root;
+      int y_root;
+      int action;
+      MetaGrabOp op;
+      int button;
+      
+      x_root = event->xclient.data.l[0];
+      y_root = event->xclient.data.l[1];
+      action = event->xclient.data.l[2];
+      button = event->xclient.data.l[3];
+
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
+                  window->desc,
+                  x_root, y_root, action, button);
+      
+      op = META_GRAB_OP_NONE;
+      switch (action)
+        {
+        case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
+          op = META_GRAB_OP_RESIZING_NW;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_TOP:
+          op = META_GRAB_OP_RESIZING_N;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
+          op = META_GRAB_OP_RESIZING_NE;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_RIGHT:
+          op = META_GRAB_OP_RESIZING_E;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
+          op = META_GRAB_OP_RESIZING_SE;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_BOTTOM:          
+          op = META_GRAB_OP_RESIZING_S;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
+          op = META_GRAB_OP_RESIZING_SW;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_LEFT:
+          op = META_GRAB_OP_RESIZING_W;
+          break;
+        case _NET_WM_MOVERESIZE_MOVE:
+          op = META_GRAB_OP_MOVING;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
+          op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
+          break;
+        case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
+          op = META_GRAB_OP_KEYBOARD_MOVING;
+          break;
+        default:
+          break;
+        }
+
+      if (op != META_GRAB_OP_NONE &&
+          ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
+           (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
+        {
+
+          meta_window_begin_grab_op (window,
+                                     op,
+                                     meta_display_get_current_time (window->display));
+        }
+      else if (op != META_GRAB_OP_NONE &&
+               ((window->has_move_func && op == META_GRAB_OP_MOVING) ||
+               (window->has_resize_func && 
+                (op != META_GRAB_OP_MOVING && 
+                 op != META_GRAB_OP_KEYBOARD_MOVING))))
+        {
+          /*
+           * the button SHOULD already be included in the message
+           */
+          if (button == 0)
+            {
+              int x, y, query_root_x, query_root_y;
+              Window root, child;
+              guint mask;
+
+              /* The race conditions in this _NET_WM_MOVERESIZE thing
+               * are mind-boggling
+               */
+              mask = 0;
+              meta_error_trap_push (window->display);
+              XQueryPointer (window->display->xdisplay,
+                             window->xwindow,
+                             &root, &child,
+                             &query_root_x, &query_root_y,
+                             &x, &y,
+                             &mask);
+              meta_error_trap_pop (window->display, TRUE);
+
+              if (mask & Button1Mask)
+                button = 1;
+              else if (mask & Button2Mask)
+                button = 2;
+              else if (mask & Button3Mask)
+                button = 3;
+              else
+                button = 0;
+            }
+
+          if (button != 0)
+            {
+              meta_topic (META_DEBUG_WINDOW_OPS,
+                          "Beginning move/resize with button = %d\n", button);
+              meta_display_begin_grab_op (window->display,
+                                          window->screen,
+                                          window,
+                                          op,
+                                          FALSE, 0 /* event_serial */,
+                                          button, 0,
+                                          meta_display_get_current_time (window->display),
+                                          x_root,
+                                          y_root);
+            }
+        }
+
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_active_window)
+    {
+      meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating",
+                    window->desc);
+
+      meta_window_activate (window, meta_display_get_current_time (window->display));
+
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+is_in_dock_group (MetaWindow *window)
+{
+  MetaGroup *group;
+  GSList *list;
+  GSList *tmp;
+  
+  if (META_WINDOW_IN_DOCK_TAB_CHAIN (window))
+    return TRUE;
+
+  if (window->type == META_WINDOW_NORMAL)
+    return FALSE;
+
+  /* If a transient-type window is in dock group,
+   * return TRUE
+   */
+  group = meta_window_get_group (window);
+  list = meta_group_list_windows (group);
+  tmp = list;
+  while (tmp != NULL)
+    {
+      MetaWindow *gw = tmp->data;
+
+      if (META_WINDOW_IN_DOCK_TAB_CHAIN (gw))
+        {
+          g_slist_free (list);
+          return TRUE;
+        }
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (list);
+  return FALSE;
+}
+
+static int
+docks_at_end_cmp (const void *a,
+                  const void *b)
+{
+  MetaWindow *window_a = (MetaWindow*) a;
+  MetaWindow *window_b = (MetaWindow*) b;
+
+  if (META_WINDOW_IN_DOCK_TAB_CHAIN (window_a))
+    {
+      if (META_WINDOW_IN_DOCK_TAB_CHAIN (window_b))
+        return 0;
+      else
+        return 1; /* a > b since a is a dock */
+    }
+  else
+    {
+      if (META_WINDOW_IN_DOCK_TAB_CHAIN (window_b))
+        return -1; /* b > a since b is a dock */
+      else
+        return 0;
+    }
+}
+
+static void
+shuffle_docks_to_end (GList **mru_list_p)
+{
+  *mru_list_p = g_list_sort (*mru_list_p,
+                             docks_at_end_cmp);
+}
+
+gboolean
+meta_window_notify_focus (MetaWindow *window,
+                          XEvent     *event)
+{
+  /* note the event can be on either the window or the frame,
+   * we focus the frame for shaded windows
+   */
+  
+  /* The event can be FocusIn, FocusOut, or UnmapNotify.
+   * On UnmapNotify we have to pretend it's focus out,
+   * because we won't get a focus out if it occurs, apparently.
+   */
+
+  /* We ignore grabs, though this is questionable.
+   * It may be better to increase the intelligence of
+   * the focus window tracking.
+   *
+   * The problem is that keybindings for windows are done with
+   * XGrabKey, which means focus_window disappears and the front of
+   * the MRU list gets confused from what the user expects once a
+   * keybinding is used.
+   */  
+  meta_topic (META_DEBUG_FOCUS,
+              "Focus %s event received on %s 0x%lx (%s) "
+              "mode %s detail %s\n",
+              event->type == FocusIn ? "in" :
+              event->type == FocusOut ? "out" :
+              event->type == UnmapNotify ? "unmap" :
+              "???",
+              window->desc, event->xany.window,
+              event->xany.window == window->xwindow ?
+              "client window" :
+              (window->frame && event->xany.window == window->frame->xwindow) ?
+              "frame window" :
+              "unknown window",
+              event->type != UnmapNotify ?
+              meta_event_mode_to_string (event->xfocus.mode) : "n/a",
+              event->type != UnmapNotify ?
+              meta_event_detail_to_string (event->xfocus.detail) : "n/a");
+
+  /* FIXME our pointer tracking is broken; see how
+   * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
+   * handle it for the correct way.  In brief you need to track
+   * pointer focus and regular focus, and handle EnterNotify in
+   * PointerRoot mode with no window manager.  However as noted above,
+   * accurate focus tracking will break things because we want to keep
+   * windows "focused" when using keybindings on them, and also we
+   * sometimes "focus" a window by focusing its frame or
+   * no_focus_window; so this all needs rethinking massively.
+   *
+   * My suggestion is to change it so that we clearly separate
+   * actual keyboard focus tracking using the xterm algorithm,
+   * and metacity's "pretend" focus window, and go through all
+   * the code and decide which one should be used in each place;
+   * a hard bit is deciding on a policy for that.
+   *
+   * http://bugzilla.gnome.org/show_bug.cgi?id=90382
+   */
+  
+  if ((event->type == FocusIn ||
+       event->type == FocusOut) &&
+      (event->xfocus.mode == NotifyGrab ||
+       event->xfocus.mode == NotifyUngrab ||
+       /* From WindowMaker, ignore all funky pointer root events */
+       event->xfocus.detail > NotifyNonlinearVirtual))
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Ignoring focus event generated by a grab or other weirdness\n");
+      return TRUE;
+    }
+    
+  if (event->type == FocusIn)
+    {
+      if (window->display->expected_focus_window == window)
+        window->display->expected_focus_window = NULL;
+
+      if (window != window->display->focus_window)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "* Focus --> %s\n", window->desc);
+          window->display->focus_window = window;
+          window->has_focus = TRUE;
+
+          /* Move to the front of the focusing workspace's MRU list.
+           * We should only be "removing" it from the MRU list if it's
+           * not already there.  Note that it's possible that we might
+           * be processing this FocusIn after we've changed to a
+           * different workspace; we should therefore update the MRU
+           * list only if the window is actually on the active
+           * workspace.
+           */
+          if (window->screen->active_workspace &&
+              meta_window_visible_on_workspace (window, 
+                                                window->screen->active_workspace))
+            {
+              GList* link;
+              link = g_list_find (window->screen->active_workspace->mru_list, 
+                                  window);
+              g_assert (link);
+
+              window->screen->active_workspace->mru_list = 
+                g_list_remove_link (window->screen->active_workspace->mru_list,
+                                    link);
+              g_list_free (link);
+
+              window->screen->active_workspace->mru_list = 
+                g_list_prepend (window->screen->active_workspace->mru_list, 
+                                window);
+              if (!is_in_dock_group (window))
+                shuffle_docks_to_end (&window->screen->active_workspace->mru_list);
+            }
+
+          if (window->frame)
+            meta_frame_queue_draw (window->frame);
+          
+          meta_error_trap_push (window->display);
+          XInstallColormap (window->display->xdisplay,
+                            window->colormap);
+          meta_error_trap_pop (window->display, FALSE);
+
+          /* move into FOCUSED_WINDOW layer */
+          meta_window_update_layer (window);
+
+          /* Ungrab click to focus button since the sync grab can interfere
+           * with some things you might do inside the focused window, by
+           * causing the client to get funky enter/leave events.
+           */
+          if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
+            meta_display_ungrab_focus_window_button (window->display, window);
+        }
+    }
+  else if (event->type == FocusOut ||
+           event->type == UnmapNotify)
+    {
+      if (event->type == FocusOut &&
+          event->xfocus.detail == NotifyInferior)
+        {
+          /* This event means the client moved focus to a subwindow */
+          meta_topic (META_DEBUG_FOCUS,
+                      "Ignoring focus out on %s with NotifyInferior\n",
+                      window->desc);
+          return TRUE;
+        }
+      
+      if (window == window->display->focus_window)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "%s is now the previous focus window due to being focused out or unmapped\n",
+                      window->desc);
+
+          meta_topic (META_DEBUG_FOCUS,
+                      "* Focus --> NULL (was %s)\n", window->desc);
+          
+          window->display->focus_window = NULL;
+          window->has_focus = FALSE;
+          if (window->frame)
+            meta_frame_queue_draw (window->frame);
+
+          meta_error_trap_push (window->display);
+          XUninstallColormap (window->display->xdisplay,
+                              window->colormap);
+          meta_error_trap_pop (window->display, FALSE);
+
+          /* move out of FOCUSED_WINDOW layer */
+          meta_window_update_layer (window);
+
+          /* Re-grab for click to focus, if necessary */
+          if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
+            meta_display_grab_focus_window_button (window->display, window);
+       }
+    }
+
+  /* Now set _NET_ACTIVE_WINDOW hint */
+  meta_display_update_active_window_hint (window->display);
+  
+  return FALSE;
+}
+
+static gboolean
+process_property_notify (MetaWindow     *window,
+                         XPropertyEvent *event)
+{
+  /* FIXME once we move entirely to the window-props.h framework, we
+   * can just call reload on the property in the event and get rid of
+   * this if-else chain.
+   */
+  
+  if (event->atom == XA_WM_NAME)
+    {
+      meta_verbose ("Property notify on %s for WM_NAME\n", window->desc);
+
+      /* don't bother reloading WM_NAME if using _NET_WM_NAME already */
+      if (!window->using_net_wm_name)
+        meta_window_reload_property (window, XA_WM_NAME);
+    }
+  else if (event->atom == window->display->atom_net_wm_name)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_NAME\n", window->desc);
+      meta_window_reload_property (window, window->display->atom_net_wm_name);
+      
+      /* if _NET_WM_NAME was unset, reload WM_NAME */
+      if (!window->using_net_wm_name)
+        meta_window_reload_property (window, XA_WM_NAME);      
+    }
+  else if (event->atom == XA_WM_ICON_NAME)
+    {
+      meta_verbose ("Property notify on %s for WM_ICON_NAME\n", window->desc);
+
+      /* don't bother reloading WM_ICON_NAME if using _NET_WM_ICON_NAME already */
+      if (!window->using_net_wm_icon_name)
+        meta_window_reload_property (window, XA_WM_ICON_NAME);
+    }
+  else if (event->atom == window->display->atom_net_wm_icon_name)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_ICON_NAME\n", window->desc);
+      meta_window_reload_property (window, window->display->atom_net_wm_icon_name);
+      
+      /* if _NET_WM_ICON_NAME was unset, reload WM_ICON_NAME */
+      if (!window->using_net_wm_icon_name)
+        meta_window_reload_property (window, XA_WM_ICON_NAME);
+    }  
+  else if (event->atom == XA_WM_NORMAL_HINTS)
+    {
+      meta_verbose ("Property notify on %s for WM_NORMAL_HINTS\n", window->desc);
+      
+      meta_window_reload_property (window, XA_WM_NORMAL_HINTS);
+      
+      /* See if we need to constrain current size */
+      meta_window_queue_move_resize (window);
+    }
+  else if (event->atom == window->display->atom_wm_protocols)
+    {
+      meta_verbose ("Property notify on %s for WM_PROTOCOLS\n", window->desc);
+      
+      meta_window_reload_property (window, window->display->atom_wm_protocols);
+    }
+  else if (event->atom == XA_WM_HINTS)
+    {
+      meta_verbose ("Property notify on %s for WM_HINTS\n", window->desc);
+
+      meta_window_reload_property (window, XA_WM_HINTS);
+    }
+  else if (event->atom == window->display->atom_motif_wm_hints)
+    {
+      meta_verbose ("Property notify on %s for MOTIF_WM_HINTS\n", window->desc);
+      
+      update_mwm_hints (window);
+      
+      if (window->decorated)
+        meta_window_ensure_frame (window);
+      else
+        meta_window_destroy_frame (window);
+
+      meta_window_queue_move_resize (window);
+      /* because ensure/destroy frame may unmap */
+      meta_window_queue_calc_showing (window);
+    }
+  else if (event->atom == XA_WM_CLASS)
+    {
+      meta_verbose ("Property notify on %s for WM_CLASS\n", window->desc);
+      
+      update_wm_class (window);
+    }
+  else if (event->atom == XA_WM_TRANSIENT_FOR)
+    {
+      meta_verbose ("Property notify on %s for WM_TRANSIENT_FOR\n", window->desc);
+      
+      update_transient_for (window);
+
+      meta_window_queue_move_resize (window);
+    }
+  else if (event->atom ==
+           window->display->atom_wm_window_role)
+    {
+      meta_verbose ("Property notify on %s for WM_WINDOW_ROLE\n", window->desc);
+      
+      update_role (window);
+    }
+  else if (event->atom ==
+           window->display->atom_wm_client_leader ||
+           event->atom ==
+           window->display->atom_sm_client_id)
+    {
+      meta_warning ("Broken client! Window %s changed client leader window or SM client ID\n", window->desc);
+    }
+  else if (event->atom ==
+           window->display->atom_net_wm_window_type)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_WINDOW_TYPE\n", window->desc);
+      update_net_wm_type (window);
+    }
+  else if (event->atom == window->display->atom_net_wm_icon)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_ICON\n", window->desc);
+      meta_icon_cache_property_changed (&window->icon_cache,
+                                        window->display,
+                                        event->atom);
+      meta_window_queue_update_icon (window);
+    }
+  else if (event->atom == window->display->atom_kwm_win_icon)
+    {
+      meta_verbose ("Property notify on %s for KWM_WIN_ICON\n", window->desc);
+
+      meta_icon_cache_property_changed (&window->icon_cache,
+                                        window->display,
+                                        event->atom);
+      meta_window_queue_update_icon (window);
+    }
+  else if ((event->atom == window->display->atom_net_wm_strut) ||
+	   (event->atom == window->display->atom_net_wm_strut_partial))
+    {
+      meta_verbose ("Property notify on %s for _NET_WM_STRUT\n", window->desc);
+      meta_window_update_struts (window);
+    }
+  else if (event->atom == window->display->atom_net_startup_id)
+    {
+      meta_verbose ("Property notify on %s for _NET_STARTUP_ID\n", window->desc);
+      
+      meta_window_reload_property (window,
+                                   window->display->atom_net_startup_id);
+    }
+  else if (event->atom == window->display->atom_metacity_update_counter)
+    {
+      meta_verbose ("Property notify on %s for _METACITY_UPDATE_COUNTER\n", window->desc);
+      
+      meta_window_reload_property (window,
+                                   window->display->atom_metacity_update_counter);
+    }
+
+  return TRUE;
+}
+
+static void
+send_configure_notify (MetaWindow *window)
+{
+  XEvent event;
+
+  /* from twm */
+  
+  event.type = ConfigureNotify;
+  event.xconfigure.display = window->display->xdisplay;
+  event.xconfigure.event = window->xwindow;
+  event.xconfigure.window = window->xwindow;
+  event.xconfigure.x = window->rect.x - window->border_width;
+  event.xconfigure.y = window->rect.y - window->border_width;
+  if (window->frame)
+    {
+      /* Need to be in root window coordinates */
+      event.xconfigure.x += window->frame->rect.x;
+      event.xconfigure.y += window->frame->rect.y;
+    }
+  event.xconfigure.width = window->rect.width;
+  event.xconfigure.height = window->rect.height;
+  event.xconfigure.border_width = window->border_width; /* requested not actual */
+  event.xconfigure.above = None; /* FIXME */
+  event.xconfigure.override_redirect = False;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
+              window->desc,
+              event.xconfigure.x, event.xconfigure.y,
+              event.xconfigure.width, event.xconfigure.height);
+  
+  meta_error_trap_push (window->display);
+  XSendEvent (window->display->xdisplay,
+              window->xwindow,
+              False, StructureNotifyMask, &event);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+static void
+update_net_wm_state (MetaWindow *window)
+{
+  /* We know this is only on initial window creation,
+   * clients don't change the property.
+   */
+
+  int n_atoms;
+  Atom *atoms;
+
+  window->shaded = FALSE;
+  window->maximized = FALSE;
+  window->wm_state_modal = FALSE;
+  window->wm_state_skip_taskbar = FALSE;
+  window->wm_state_skip_pager = FALSE;
+  window->wm_state_above = FALSE;
+  window->wm_state_below = FALSE;
+  
+  if (meta_prop_get_atom_list (window->display, window->xwindow,
+                               window->display->atom_net_wm_state,
+                               &atoms, &n_atoms))
+    {
+      int i;
+      
+      i = 0;
+      while (i < n_atoms)
+        {
+          if (atoms[i] == window->display->atom_net_wm_state_shaded)
+            window->shaded = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_maximized_horz)
+            window->maximize_after_placement = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_maximized_vert)
+            window->maximize_after_placement = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_modal)
+            window->wm_state_modal = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_skip_taskbar)
+            window->wm_state_skip_taskbar = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_skip_pager)
+            window->wm_state_skip_pager = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_fullscreen)
+            window->fullscreen = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_above)
+            window->wm_state_above = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_below)
+            window->wm_state_below = TRUE;
+          
+          ++i;
+        }
+  
+      meta_XFree (atoms);
+    }
+  else if (event->atom == window->display->atom_net_wm_geometry_save_id)
+    {
+      meta_verbose ("Property notify on %s for _NET_WM_GEOMETRY_SAVE_ID\n", window->desc);
+      
+      update_geometry_save_id (window);
+    }
+  
+  recalc_window_type (window);
+}
+
+
+static void
+update_mwm_hints (MetaWindow *window)
+{
+  MotifWmHints *hints;
+
+  window->mwm_decorated = TRUE;
+  window->mwm_border_only = FALSE;
+  window->mwm_has_close_func = TRUE;
+  window->mwm_has_minimize_func = TRUE;
+  window->mwm_has_maximize_func = TRUE;
+  window->mwm_has_move_func = TRUE;
+  window->mwm_has_resize_func = TRUE;
+
+  if (!meta_prop_get_motif_hints (window->display, window->xwindow,
+                                  window->display->atom_motif_wm_hints,
+                                  &hints))
+    {
+      meta_verbose ("Window %s has no MWM hints\n", window->desc);
+      return;
+    }
+  
+  /* We support those MWM hints deemed non-stupid */
+
+  meta_verbose ("Window %s has MWM hints\n",
+                window->desc);
+  
+  if (hints->flags & MWM_HINTS_DECORATIONS)
+    {
+      meta_verbose ("Window %s sets MWM_HINTS_DECORATIONS 0x%lx\n",
+                    window->desc, hints->decorations);
+
+      if (hints->decorations == 0)
+        window->mwm_decorated = FALSE;
+      /* some input methods use this */
+      else if (hints->decorations == MWM_DECOR_BORDER)
+        window->mwm_border_only = TRUE;
+    }
+  else
+    meta_verbose ("Decorations flag unset\n");
+  
+  if (hints->flags & MWM_HINTS_FUNCTIONS)
+    {
+      gboolean toggle_value;
+      
+      meta_verbose ("Window %s sets MWM_HINTS_FUNCTIONS 0x%lx\n",
+                    window->desc, hints->functions);
+
+      /* If _ALL is specified, then other flags indicate what to turn off;
+       * if ALL is not specified, flags are what to turn on.
+       * at least, I think so
+       */
+      
+      if ((hints->functions & MWM_FUNC_ALL) == 0)
+        {
+          toggle_value = TRUE;
+
+          meta_verbose ("Window %s disables all funcs then reenables some\n",
+                        window->desc);
+          window->mwm_has_close_func = FALSE;
+          window->mwm_has_minimize_func = FALSE;
+          window->mwm_has_maximize_func = FALSE;
+          window->mwm_has_move_func = FALSE;
+          window->mwm_has_resize_func = FALSE;
+        }
+      else
+        {
+          meta_verbose ("Window %s enables all funcs then disables some\n",
+                        window->desc);
+          toggle_value = FALSE;
+        }
+      
+      if ((hints->functions & MWM_FUNC_CLOSE) != 0)
+        {
+          meta_verbose ("Window %s toggles close via MWM hints\n",
+                        window->desc);
+          window->mwm_has_close_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_MINIMIZE) != 0)
+        {
+          meta_verbose ("Window %s toggles minimize via MWM hints\n",
+                        window->desc);
+          window->mwm_has_minimize_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_MAXIMIZE) != 0)
+        {
+          meta_verbose ("Window %s toggles maximize via MWM hints\n",
+                        window->desc);
+          window->mwm_has_maximize_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_MOVE) != 0)
+        {
+          meta_verbose ("Window %s toggles move via MWM hints\n",
+                        window->desc);
+          window->mwm_has_move_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_RESIZE) != 0)
+        {
+          meta_verbose ("Window %s toggles resize via MWM hints\n",
+                        window->desc);
+          window->mwm_has_resize_func = toggle_value;
+        }
+    }
+  else
+    meta_verbose ("Functions flag unset\n");
+
+  meta_XFree (hints);
+
+  recalc_window_features (window);
+}
+
+static void
+update_geometry_save_id (MetaWindow *window)
+{
+  gchar *save_id = NULL;
+
+  if (!meta_prop_get_save_id (window->display, window->xwindow,
+				       window->display->atom_net_wm_geometry_save_id,
+				       &save_id))
+    {
+      meta_verbose ("Window Geometry Save ID wasn't set.  We manage this window.\n");
+      /*      meta_history_clear_window (window);*/
+      return;
+
+    }
+
+ 
+  if (!save_id || strlen (save_id) == 0) 
+    {
+      meta_verbose ("Window Geometry Save ID is empty.  Clearing Window History.\n");
+      meta_history_clear_window (window);
+      return;      
+    }
+
+  meta_history_update_key (window, save_id);
+  
+  meta_verbose ("Window Geometry Save ID updated.\n");
+}
+
+gboolean
+meta_window_get_icon_geometry (MetaWindow    *window,
+                               MetaRectangle *rect)
+{
+  gulong *geometry = NULL;
+  int nitems;
+
+  if (meta_prop_get_cardinal_list (window->display,
+                                   window->xwindow,
+                                   window->display->atom_net_wm_icon_geometry,
+                                   &geometry, &nitems))
+    {
+      if (nitems != 4)
+        {
+          meta_verbose ("_NET_WM_ICON_GEOMETRY on %s has %d values instead of 4\n",
+                        window->desc, nitems);
+          meta_XFree (geometry);
+          return FALSE;
+        }
+  
+      if (rect)
+        {
+          rect->x = geometry[0];
+          rect->y = geometry[1];
+          rect->width = geometry[2];
+          rect->height = geometry[3];
+        }
+
+      meta_XFree (geometry);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+update_wm_class (MetaWindow *window)
+{
+  XClassHint ch;
+  
+  if (window->res_class)
+    g_free (window->res_class);
+  if (window->res_name)
+    g_free (window->res_name);
+
+  window->res_class = NULL;
+  window->res_name = NULL;
+
+  ch.res_name = NULL;
+  ch.res_class = NULL;
+
+  meta_prop_get_class_hint (window->display,
+                            window->xwindow,
+                            XA_WM_CLASS,
+                            &ch);
+
+  if (ch.res_name)
+    {
+      window->res_name = g_strdup (ch.res_name);
+
+      /* This is used by the history system. */
+      if (window->orig_name == NULL)
+	{
+	  window->orig_name = g_strdup (ch.res_name);
+	}
+      XFree (ch.res_name);
+    }
+
+  if (ch.res_class)
+    {
+      window->res_class = g_strdup (ch.res_class);
+      XFree (ch.res_class);
+    }
+
+  meta_verbose ("Window %s class: '%s' name: '%s'\n",
+                window->desc,
+                window->res_class ? window->res_class : "none",
+                window->res_name ? window->res_name : "none");
+}
+
+static Window
+read_client_leader (MetaDisplay *display,
+                    Window       xwindow)
+{
+  Window retval = None;
+  
+  meta_prop_get_window (display, xwindow,
+                        display->atom_wm_client_leader,
+                        &retval);
+
+  return retval;
+}
+
+typedef struct
+{
+  Window leader;  
+} ClientLeaderData;
+
+static gboolean
+find_client_leader_func (MetaWindow *ancestor,
+                         void       *data)
+{
+  ClientLeaderData *d;
+
+  d = data;
+
+  d->leader = read_client_leader (ancestor->display,
+                                  ancestor->xwindow);
+
+  /* keep going if no client leader found */
+  return d->leader == None;
+}
+
+static void
+update_sm_hints (MetaWindow *window)
+{
+  Window leader;
+  
+  window->xclient_leader = None;
+  window->sm_client_id = NULL;
+
+  /* If not on the current window, we can get the client
+   * leader from transient parents. If we find a client
+   * leader, we read the SM_CLIENT_ID from it.
+   */
+  leader = read_client_leader (window->display, window->xwindow);
+  if (leader == None)
+    {
+      ClientLeaderData d;
+      d.leader = None;
+      meta_window_foreach_ancestor (window, find_client_leader_func,
+                                    &d);
+      leader = d.leader;
+    }
+      
+  if (leader != None)
+    {
+      char *str;
+      
+      window->xclient_leader = leader;
+
+      if (meta_prop_get_latin1_string (window->display, leader,
+                                       window->display->atom_sm_client_id,
+                                       &str))
+        {
+          window->sm_client_id = g_strdup (str);
+          meta_XFree (str);
+        }
+    }
+  else
+    {
+      meta_verbose ("Didn't find a client leader for %s\n", window->desc);
+
+      if (!meta_prefs_get_disable_workarounds ())
+        {
+          /* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app
+           * instead of the client leader
+           */
+          char *str;
+
+          str = NULL;
+          if (meta_prop_get_latin1_string (window->display, window->xwindow,
+                                           window->display->atom_sm_client_id,
+                                           &str))
+            {
+              if (window->sm_client_id == NULL) /* first time through */
+                meta_warning (_("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n"),
+                              window->desc);
+              
+              window->sm_client_id = g_strdup (str);
+              meta_XFree (str);
+            }
+        }
+    }
+
+  meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n",
+                window->desc, window->xclient_leader,
+                window->sm_client_id ? window->sm_client_id : "none");
+}
+
+static void
+update_role (MetaWindow *window)
+{
+  char *str;
+  
+  if (window->role)
+    g_free (window->role);
+  window->role = NULL;
+
+  if (meta_prop_get_latin1_string (window->display, window->xwindow,
+                                   window->display->atom_wm_window_role,
+                                   &str))
+    {
+      window->role = g_strdup (str);
+      meta_XFree (str);
+    }
+
+  meta_verbose ("Updated role of %s to '%s'\n",
+                window->desc, window->role ? window->role : "null");
+}
+
+static void
+update_transient_for (MetaWindow *window)
+{
+  Window w;
+
+  meta_error_trap_push (window->display);
+  w = None;
+  XGetTransientForHint (window->display->xdisplay,
+                        window->xwindow,
+                        &w);
+  meta_error_trap_pop (window->display, TRUE);
+  window->xtransient_for = w;
+
+  window->transient_parent_is_root_window =
+    window->xtransient_for == window->screen->xroot;
+  
+  if (window->xtransient_for != None)
+    meta_verbose ("Window %s transient for 0x%lx (root = %d)\n", window->desc,
+                  window->xtransient_for, window->transient_parent_is_root_window);
+  else
+    meta_verbose ("Window %s is not transient\n", window->desc);
+  
+  /* may now be a dialog */
+  recalc_window_type (window);
+
+  /* update stacking constraints */
+  meta_stack_update_transient (window->screen->stack, window);
+}
+
+/* some legacy cruft */
+typedef enum
+{
+  WIN_LAYER_DESKTOP     = 0,
+  WIN_LAYER_BELOW       = 2,
+  WIN_LAYER_NORMAL      = 4,
+  WIN_LAYER_ONTOP       = 6,
+  WIN_LAYER_DOCK        = 8,
+  WIN_LAYER_ABOVE_DOCK  = 10
+} GnomeWinLayer;
+
+static void
+update_net_wm_type (MetaWindow *window)
+{
+  int n_atoms;
+  Atom *atoms;
+  int i;
+
+  window->type_atom = None;
+  n_atoms = 0;
+  atoms = NULL;
+  
+  meta_prop_get_atom_list (window->display, window->xwindow, 
+                           window->display->atom_net_wm_window_type,
+                           &atoms, &n_atoms);
+
+  i = 0;
+  while (i < n_atoms)
+    {
+      /* We break as soon as we find one we recognize,
+       * supposed to prefer those near the front of the list
+       */
+      if (atoms[i] == window->display->atom_net_wm_window_type_desktop ||
+          atoms[i] == window->display->atom_net_wm_window_type_dock ||
+          atoms[i] == window->display->atom_net_wm_window_type_toolbar ||
+          atoms[i] == window->display->atom_net_wm_window_type_menu ||
+          atoms[i] == window->display->atom_net_wm_window_type_dialog ||
+          atoms[i] == window->display->atom_net_wm_window_type_normal ||
+          atoms[i] == window->display->atom_net_wm_window_type_utility ||
+          atoms[i] == window->display->atom_net_wm_window_type_splash)
+        {
+          window->type_atom = atoms[i];
+          break;
+        }
+      
+      ++i;
+    }
+  
+  meta_XFree (atoms);
+
+  if (meta_is_verbose ())
+    {
+      char *str;
+
+      str = NULL;
+      if (window->type_atom != None)
+        {
+          meta_error_trap_push (window->display);
+          str = XGetAtomName (window->display->xdisplay, window->type_atom);
+          meta_error_trap_pop (window->display, TRUE);
+        }
+
+      meta_verbose ("Window %s type atom %s\n", window->desc,
+                    str ? str : "(none)");
+
+      if (str)
+        meta_XFree (str);
+    }
+  
+  recalc_window_type (window);
+}
+
+static void
+redraw_icon (MetaWindow *window)
+{
+  /* We could probably be smart and just redraw the icon here,
+   * instead of the whole frame.
+   */
+  if (window->frame && (window->mapped || window->frame->mapped))
+    meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow);
+}
+
+static void
+meta_window_update_icon_now (MetaWindow *window)
+{
+  GdkPixbuf *icon;
+  GdkPixbuf *mini_icon;
+
+  icon = NULL;
+  mini_icon = NULL;
+  
+  if (meta_read_icons (window->screen,
+                       window->xwindow,
+                       &window->icon_cache,
+                       window->wm_hints_pixmap,
+                       window->wm_hints_mask,
+                       &icon,
+                       META_ICON_WIDTH, META_ICON_HEIGHT,
+                       &mini_icon,
+                       META_MINI_ICON_WIDTH,
+                       META_MINI_ICON_HEIGHT))
+    {
+      if (window->icon)
+        g_object_unref (G_OBJECT (window->icon));
+      
+      if (window->mini_icon)
+        g_object_unref (G_OBJECT (window->mini_icon));
+      
+      window->icon = icon;
+      window->mini_icon = mini_icon;
+
+      redraw_icon (window);
+    }
+  
+  g_assert (window->icon);
+  g_assert (window->mini_icon);
+}
+
+static guint update_icon_idle = 0;
+static GSList *update_icon_pending = NULL;
+
+static gboolean
+idle_update_icon (gpointer data)
+{
+  GSList *tmp;
+  GSList *copy;
+
+  meta_topic (META_DEBUG_GEOMETRY, "Clearing the update_icon queue\n");
+
+  /* Work with a copy, for reentrancy. The allowed reentrancy isn't
+   * complete; destroying a window while we're in here would result in
+   * badness. But it's OK to queue/unqueue update_icons.
+   */
+  copy = g_slist_copy (update_icon_pending);
+  g_slist_free (update_icon_pending);
+  update_icon_pending = NULL;
+  update_icon_idle = 0;
+
+  destroying_windows_disallowed += 1;
+  
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      meta_window_update_icon_now (window); 
+      window->update_icon_queued = FALSE;
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (copy);
+
+  destroying_windows_disallowed -= 1;
+  
+  return FALSE;
+}
+
+void
+meta_window_unqueue_update_icon (MetaWindow *window)
+{
+  if (!window->update_icon_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Removing %s from the update_icon queue\n",
+              window->desc);
+
+  /* Note that window may not actually be in update_icon_pending
+   * because it may have been in "copy" inside the idle handler
+   */
+  update_icon_pending = g_slist_remove (update_icon_pending, window);
+  window->update_icon_queued = FALSE;
+  
+  if (update_icon_pending == NULL &&
+      update_icon_idle != 0)
+    {
+      g_source_remove (update_icon_idle);
+      update_icon_idle = 0;
+    }
+}
+
+void
+meta_window_flush_update_icon (MetaWindow *window)
+{
+  if (window->update_icon_queued)
+    {
+      meta_window_unqueue_update_icon (window);
+      meta_window_update_icon_now (window);
+    }
+}
+
+void
+meta_window_queue_update_icon (MetaWindow *window)
+{
+  if (window->unmanaging)
+    return;
+
+  if (window->update_icon_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Putting %s in the update_icon queue\n",
+              window->desc);
+  
+  window->update_icon_queued = TRUE;
+  
+  if (update_icon_idle == 0)
+    update_icon_idle = g_idle_add (idle_update_icon, NULL);
+  
+  update_icon_pending = g_slist_prepend (update_icon_pending, window);
+}
+
+GList*
+meta_window_get_workspaces (MetaWindow *window)
+{
+  if (window->on_all_workspaces)
+    return window->screen->workspaces;
+  else
+    return window->workspaces;
+}
+
+static void
+invalidate_work_areas (MetaWindow *window)
+{
+  GList *tmp;
+
+  tmp = meta_window_get_workspaces (window);
+  
+  while (tmp != NULL)
+    {
+      meta_workspace_invalidate_work_area (tmp->data);
+      tmp = tmp->next;
+    }
+}
+
+void
+meta_window_update_struts (MetaWindow *window)
+{
+  gulong *struts = NULL;
+  int nitems;
+  gboolean old_has_struts;
+  gboolean new_has_struts;
+
+  MetaRectangle old_left;
+  MetaRectangle old_right;
+  MetaRectangle old_top;
+  MetaRectangle old_bottom;
+
+  MetaRectangle new_left;
+  MetaRectangle new_right;
+  MetaRectangle new_top;
+  MetaRectangle new_bottom;
+
+#define MIN_EMPTY (75)
+  
+  meta_verbose ("Updating struts for %s\n", window->desc);
+  
+  if (window->struts)
+    {
+      old_has_struts = TRUE;
+      old_left = window->struts->left;
+      old_right = window->struts->right;
+      old_top = window->struts->top;
+      old_bottom = window->struts->bottom;
+    }
+  else
+    {
+      old_has_struts = FALSE;
+    }
+
+  new_has_struts = FALSE;
+  new_left.width = 0;
+  new_left.x = 0;
+  new_left.y = 0;
+  new_left.height = window->screen->height;
+
+  new_right.width = 0;
+  new_right.x = window->screen->width;
+  new_right.y = 0;
+  new_right.height = window->screen->height;
+
+  new_top.height = 0;
+  new_top.y = 0;
+  new_top.x = 0;
+  new_top.width = window->screen->width;
+
+  new_bottom.height = 0;
+  new_bottom.y = window->screen->height;
+  new_bottom.x = 0;
+  new_bottom.width = window->screen->width;
+  
+  if (meta_prop_get_cardinal_list (window->display,
+                                   window->xwindow,
+                                   window->display->atom_net_wm_strut_partial,
+                                   &struts, &nitems))
+    {
+      if (nitems != 12)
+        {
+          meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead of 12\n",
+                        window->desc, nitems);
+        }
+      else
+        {
+          new_has_struts = TRUE;
+          new_left.width = MIN ((int)struts[0], 
+                                window->screen->width/2 - MIN_EMPTY);
+          new_right.width = MIN ((int)struts[1], 
+                                 window->screen->width/2 - MIN_EMPTY);
+          new_top.height = MIN ((int)struts[2], 
+                                window->screen->height/2 - MIN_EMPTY);
+          new_bottom.height = MIN ((int)struts[3], 
+                                   window->screen->height/2 - MIN_EMPTY);
+          new_right.x = window->screen->width - 
+            new_right.width;
+          new_bottom.y = window->screen->height - 
+            new_bottom.height;
+          new_left.y = struts[4];
+          new_left.height = struts[5] - new_left.y + 1;
+          new_right.y = struts[6];
+          new_right.height = struts[7] - new_right.y + 1;
+          new_top.x = struts[8];
+          new_top.width = struts[9] - new_top.x + 1;
+          new_bottom.x = struts[10];
+          new_bottom.width = struts[11] - new_bottom.x + 1;
+          
+          meta_verbose ("_NET_WM_STRUT_PARTIAL struts %d %d %d %d for window %s\n",
+                        new_left.width,
+                        new_right.width,
+                        new_top.height,
+                        new_bottom.height,
+                        window->desc);
+          
+        }
+      meta_XFree (struts);
+    }
+  else
+    {
+      meta_verbose ("No _NET_WM_STRUT property for %s\n",
+                    window->desc);
+    }
+
+  if (!new_has_struts)
+    {
+      if (meta_prop_get_cardinal_list (window->display,
+                                       window->xwindow,
+                                       window->display->atom_net_wm_strut,
+                                       &struts, &nitems))
+        {
+          if (nitems != 4)
+            {
+              meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n",
+                            window->desc, nitems);
+            }
+          else
+            {
+              new_has_struts = TRUE;
+              new_left.width = MIN ((int)struts[0], 
+                                    window->screen->width/2 - MIN_EMPTY);
+              new_right.width = MIN ((int)struts[1], 
+                                     window->screen->width/2 - MIN_EMPTY);
+              new_top.height = MIN ((int)struts[2], 
+                                    window->screen->height/2 - MIN_EMPTY);
+              new_bottom.height = MIN ((int)struts[3], 
+                                       window->screen->height/2 - MIN_EMPTY);
+              new_left.x = 0;
+              new_right.x = window->screen->width - 
+                new_right.width;
+              new_top.y = 0;
+              new_bottom.y = window->screen->height -
+                new_bottom.height;
+          
+              meta_verbose ("_NET_WM_STRUT struts %d %d %d %d for window %s\n",
+                            new_left.width,
+                            new_right.width,
+                            new_top.height,
+                            new_bottom.height,
+                            window->desc);
+              
+            }
+          meta_XFree (struts);
+        }
+      else
+        {
+          meta_verbose ("No _NET_WM_STRUT property for %s\n",
+                        window->desc);
+        }
+    }
+ 
+  if (old_has_struts != new_has_struts ||
+      (new_has_struts && old_has_struts &&
+       (!meta_rectangle_equal(&old_left, &new_left) ||
+        !meta_rectangle_equal(&old_right, &new_right) ||
+        !meta_rectangle_equal(&old_top, &new_top) ||
+        !meta_rectangle_equal(&old_bottom, &new_bottom))))
+    {
+      if (new_has_struts)
+        {
+          if (!window->struts)
+            window->struts = g_new (MetaStruts, 1);
+              
+          window->struts->left = new_left;
+          window->struts->right = new_right;
+          window->struts->top = new_top;
+          window->struts->bottom = new_bottom;
+        }
+      else
+        {
+          g_free (window->struts);
+          window->struts = NULL;
+        }
+      meta_topic (META_DEBUG_WORKAREA,
+                  "Invalidating work areas of window %s due to struts update\n",
+                  window->desc);
+      invalidate_work_areas (window);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_WORKAREA,
+                  "Struts on %s were unchanged\n", window->desc);
+    }
+}
+
+static void
+recalc_window_type (MetaWindow *window)
+{
+  MetaWindowType old_type;
+
+  old_type = window->type;
+  
+  if (window->type_atom != None)
+    {
+      if (window->type_atom  == window->display->atom_net_wm_window_type_desktop)
+        window->type = META_WINDOW_DESKTOP;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_dock)
+        window->type = META_WINDOW_DOCK;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_toolbar)
+        window->type = META_WINDOW_TOOLBAR;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_menu)
+        window->type = META_WINDOW_MENU;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_dialog)
+        window->type = META_WINDOW_DIALOG;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_normal)
+        window->type = META_WINDOW_NORMAL;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_utility)
+        window->type = META_WINDOW_UTILITY;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_splash)
+        window->type = META_WINDOW_SPLASHSCREEN;
+      else
+        meta_bug ("Set a type atom for %s that wasn't handled in recalc_window_type\n",
+                  window->desc);
+    }
+  else if (window->xtransient_for != None)
+    {
+      window->type = META_WINDOW_DIALOG;
+    }
+  else
+    {
+      window->type = META_WINDOW_NORMAL;
+    }
+
+  if (window->type == META_WINDOW_DIALOG &&
+      window->wm_state_modal)
+    window->type = META_WINDOW_MODAL_DIALOG;
+  
+  meta_verbose ("Calculated type %d for %s, old type %d\n",
+                window->type, window->desc, old_type);
+
+  if (old_type != window->type)
+    {
+      recalc_window_features (window);
+      
+      set_net_wm_state (window);
+      
+      /* Update frame */
+      if (window->decorated)
+        meta_window_ensure_frame (window);
+      else
+        meta_window_destroy_frame (window);
+      
+      /* update stacking constraints */
+      meta_window_update_layer (window);
+
+      meta_window_grab_keys (window);
+    }
+}
+
+static void
+set_allowed_actions_hint (MetaWindow *window)
+{
+#define MAX_N_ACTIONS 10
+  unsigned long data[MAX_N_ACTIONS];
+  int i;
+
+  i = 0;
+  if (window->has_move_func)
+    {
+      data[i] = window->display->atom_net_wm_action_move;
+      ++i;
+    }
+  if (window->has_resize_func)
+    {
+      data[i] = window->display->atom_net_wm_action_resize;
+      ++i;
+      data[i] = window->display->atom_net_wm_action_fullscreen;
+      ++i;
+    }
+  if (window->has_minimize_func)
+    {
+      data[i] = window->display->atom_net_wm_action_minimize;
+      ++i;
+    }
+  if (window->has_shade_func)
+    {
+      data[i] = window->display->atom_net_wm_action_shade;
+      ++i;
+    }
+  /* sticky according to EWMH is different from metacity's sticky;
+   * metacity doesn't support EWMH sticky
+   */
+  if (window->has_maximize_func)
+    {
+      data[i] = window->display->atom_net_wm_action_maximize_horz;
+      ++i;
+      data[i] = window->display->atom_net_wm_action_maximize_vert;
+      ++i;
+    }
+  /* We always allow this */
+  data[i] = window->display->atom_net_wm_action_change_desktop;
+  ++i;
+  if (window->has_close_func)
+    {
+      data[i] = window->display->atom_net_wm_action_close;
+      ++i;
+    }
+
+  g_assert (i <= MAX_N_ACTIONS);
+
+  meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i);
+  
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_wm_allowed_actions,
+                   XA_ATOM,
+                   32, PropModeReplace, (guchar*) data, i);
+  meta_error_trap_pop (window->display, FALSE);
+#undef MAX_N_ACTIONS
+}
+
+void
+meta_window_recalc_features (MetaWindow *window)
+{
+  recalc_window_features (window);
+}
+
+static void
+recalc_window_features (MetaWindow *window)
+{
+  gboolean old_has_close_func;
+  gboolean old_has_minimize_func;
+  gboolean old_has_move_func;
+  gboolean old_has_resize_func;
+  gboolean old_has_shade_func;
+  gboolean old_always_sticky;
+
+  old_has_close_func = window->has_close_func;
+  old_has_minimize_func = window->has_minimize_func;
+  old_has_move_func = window->has_move_func;
+  old_has_resize_func = window->has_resize_func;
+  old_has_shade_func = window->has_shade_func;
+  old_always_sticky = window->always_sticky;
+
+  /* Use MWM hints initially */
+  window->decorated = window->mwm_decorated;
+  window->border_only = window->mwm_border_only;
+  window->has_close_func = window->mwm_has_close_func;
+  window->has_minimize_func = window->mwm_has_minimize_func;
+  window->has_maximize_func = window->mwm_has_maximize_func;
+  window->has_move_func = window->mwm_has_move_func;
+    
+  window->has_resize_func = TRUE;  
+
+  /* If min_size == max_size, then don't allow resize */
+  if (window->size_hints.min_width == window->size_hints.max_width &&
+      window->size_hints.min_height == window->size_hints.max_height)
+    window->has_resize_func = FALSE;
+  else if (!window->mwm_has_resize_func)
+    {
+      /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the
+       * authoritative source for that info. Some apps such as mplayer or
+       * xine disable resize via MWM but not WM_NORMAL_HINTS, but that
+       * leads to e.g. us not fullscreening their windows.  Apps that set
+       * MWM but not WM_NORMAL_HINTS are basically broken. We complain
+       * about these apps but make them work.
+       */
+      
+      meta_warning (_("Window %s sets an MWM hint indicating it isn't resizable, but sets min size %d x %d and max size %d x %d; this doesn't make much sense.\n"),
+                    window->desc,
+                    window->size_hints.min_width,
+                    window->size_hints.min_height,
+                    window->size_hints.max_width,
+                    window->size_hints.max_height);
+    }
+
+  window->has_shade_func = TRUE;
+  window->has_fullscreen_func = TRUE;
+
+  window->always_sticky = FALSE;
+  
+  /* Semantic category overrides the MWM hints */
+  if (window->type == META_WINDOW_TOOLBAR)
+    window->decorated = FALSE;
+
+  if (window->type == META_WINDOW_DESKTOP ||
+      window->type == META_WINDOW_DOCK)
+    window->always_sticky = TRUE;
+  
+  if (window->type == META_WINDOW_DESKTOP ||
+      window->type == META_WINDOW_DOCK ||
+      window->type == META_WINDOW_SPLASHSCREEN)
+    {
+      window->decorated = FALSE;
+      window->has_close_func = FALSE;
+      window->has_shade_func = FALSE;
+
+      /* FIXME this keeps panels and things from using
+       * NET_WM_MOVERESIZE; the problem is that some
+       * panels (edge panels) have fixed possible locations,
+       * and others ("floating panels") do not.
+       *
+       * Perhaps we should require edge panels to explicitly
+       * disable movement?
+       */
+      window->has_move_func = FALSE;
+      window->has_resize_func = FALSE;
+    }
+  
+  if (window->type != META_WINDOW_NORMAL)
+    {
+      window->has_minimize_func = FALSE;
+      window->has_maximize_func = FALSE;
+      window->has_fullscreen_func = FALSE;
+    }
+
+  if (!window->has_resize_func)
+    {      
+      window->has_maximize_func = FALSE;
+      
+      /* don't allow fullscreen if we can't resize, unless the size
+       * is entire screen size (kind of broken, because we
+       * actually fullscreen to xinerama head size not screen size)
+       */
+      if (window->size_hints.min_width == window->screen->width &&
+          window->size_hints.min_height == window->screen->height &&
+          !window->decorated)
+        ; /* leave fullscreen available */
+      else
+        window->has_fullscreen_func = FALSE;
+    }
+
+  /* We leave fullscreen windows decorated, just push the frame outside
+   * the screen. This avoids flickering to unparent them.
+   *
+   * Note that setting has_resize_func = FALSE here must come after the
+   * above code that may disable fullscreen, because if the window
+   * is not resizable purely due to fullscreen, we don't want to
+   * disable fullscreen mode.
+   */
+  if (window->fullscreen)
+    {
+      window->has_shade_func = FALSE;
+      window->has_move_func = FALSE;
+      window->has_resize_func = FALSE;
+      window->has_maximize_func = FALSE;
+    }
+
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d\n",
+              window->desc,
+              window->fullscreen,
+              window->has_maximize_func, window->has_fullscreen_func,
+              window->size_hints.min_width,
+              window->size_hints.min_height,
+              window->size_hints.max_width,
+              window->size_hints.max_height);
+  
+  /* no shading if not decorated */
+  if (!window->decorated || window->border_only)
+    window->has_shade_func = FALSE;
+
+  window->skip_taskbar = FALSE;
+  window->skip_pager = FALSE;
+
+  if (window->wm_state_skip_taskbar)
+    window->skip_taskbar = TRUE;
+  
+  if (window->wm_state_skip_pager)
+    window->skip_pager = TRUE;
+  
+  switch (window->type)
+    {
+      /* Force skip taskbar/pager on these window types */
+    case META_WINDOW_DESKTOP:
+    case META_WINDOW_DOCK:
+    case META_WINDOW_TOOLBAR:
+    case META_WINDOW_MENU:
+    case META_WINDOW_UTILITY:
+    case META_WINDOW_SPLASHSCREEN:
+      window->skip_taskbar = TRUE;
+      window->skip_pager = TRUE;
+      break;
+
+    case META_WINDOW_DIALOG:
+    case META_WINDOW_MODAL_DIALOG:
+      /* only skip taskbar if we have a real transient parent */
+      if (window->xtransient_for != None &&
+          window->xtransient_for != window->screen->xroot)
+        window->skip_taskbar = TRUE;
+      break;
+      
+    case META_WINDOW_NORMAL:
+      break;
+    }
+
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Window %s decorated = %d border_only = %d has_close = %d has_minimize = %d has_maximize = %d has_move = %d has_shade = %d skip_taskbar = %d skip_pager = %d\n",
+              window->desc,
+              window->decorated,
+              window->border_only,
+              window->has_close_func,
+              window->has_minimize_func,
+              window->has_maximize_func,
+              window->has_move_func,
+              window->has_shade_func,
+              window->skip_taskbar,
+              window->skip_pager);
+  
+  /* FIXME:
+   * Lame workaround for recalc_window_features
+   * being used overzealously. The fix is to
+   * only recalc_window_features when something
+   * has actually changed.
+   */
+  if (old_has_close_func != window->has_close_func       ||
+      old_has_minimize_func != window->has_minimize_func ||
+      old_has_move_func != window->has_move_func         ||
+      old_has_resize_func != window->has_resize_func     ||
+      old_has_shade_func != window->has_shade_func       ||
+      old_always_sticky != window->always_sticky)
+    set_allowed_actions_hint (window);
+    
+  /* FIXME perhaps should ensure if we don't have a shade func,
+   * we aren't shaded, etc.
+   */
+}
+
+static void
+menu_callback (MetaWindowMenu *menu,
+               Display        *xdisplay,
+               Window          client_xwindow,
+               MetaMenuOp      op,
+               int             workspace_index,
+               gpointer        data)
+{
+  MetaDisplay *display;
+  MetaWindow *window;
+  MetaWorkspace *workspace;
+  
+  display = meta_display_for_x_display (xdisplay);
+  window = meta_display_lookup_x_window (display, client_xwindow);
+  workspace = NULL;
+  
+  if (window != NULL) /* window can be NULL */
+    {
+      meta_verbose ("Menu op %d on %s\n", op, window->desc);
+      
+      /* op can be 0 for none */
+      switch (op)
+        {
+        case META_MENU_OP_DELETE:
+          meta_window_delete (window, meta_display_get_current_time (window->display));
+          break;
+
+        case META_MENU_OP_MINIMIZE:
+          meta_window_minimize (window);
+          break;
+
+        case META_MENU_OP_UNMAXIMIZE:
+          meta_window_unmaximize (window);
+          break;
+      
+        case META_MENU_OP_MAXIMIZE:
+          meta_window_maximize (window);
+          break;
+
+        case META_MENU_OP_UNSHADE:
+          meta_window_unshade (window);
+          break;
+      
+        case META_MENU_OP_SHADE:
+          meta_window_shade (window);
+          break;
+      
+        case META_MENU_OP_MOVE_LEFT:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_LEFT);
+          break;
+
+        case META_MENU_OP_MOVE_RIGHT:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_RIGHT);
+          break;
+
+        case META_MENU_OP_MOVE_UP:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_UP);
+          break;
+
+        case META_MENU_OP_MOVE_DOWN:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_DOWN);
+          break;
+
+        case META_MENU_OP_WORKSPACES:
+          workspace = meta_screen_get_workspace_by_index (window->screen,
+                                                          workspace_index);
+          break;
+
+        case META_MENU_OP_STICK:
+          meta_window_stick (window);
+          break;
+
+        case META_MENU_OP_UNSTICK:
+          meta_window_unstick (window);
+          break;
+
+        case META_MENU_OP_ABOVE:
+          meta_window_make_above (window);
+          break;
+
+        case META_MENU_OP_UNABOVE:
+          meta_window_unmake_above (window);
+          break;
+
+        case META_MENU_OP_MOVE:
+          meta_window_begin_grab_op (window,
+                                     META_GRAB_OP_KEYBOARD_MOVING,
+                                     meta_display_get_current_time (window->display));
+          break;
+
+        case META_MENU_OP_RESIZE:
+          meta_window_begin_grab_op (window,
+                                     META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
+                                     meta_display_get_current_time (window->display));
+          break;
+          
+        case 0:
+          /* nothing */
+          break;
+          
+        default:
+          meta_warning (G_STRLOC": Unknown window op\n");
+          break;
+        }
+
+      if (workspace)
+	{
+	  meta_window_change_workspace (window,
+					workspace);
+#if 0
+	  meta_workspace_activate (workspace);
+	  meta_window_raise (window);
+#endif
+	}
+    }
+  else
+    {
+      meta_verbose ("Menu callback on nonexistent window\n");
+    }
+
+  if (display->window_menu == menu)
+    {
+      display->window_menu = NULL;
+      display->window_with_menu = NULL;
+    }
+  
+  meta_ui_window_menu_free (menu);
+}
+
+void
+meta_window_show_menu (MetaWindow *window,
+                       int         root_x,
+                       int         root_y,
+                       int         button,
+                       Time        timestamp)
+{
+  MetaMenuOp ops;
+  MetaMenuOp insensitive;
+  MetaWindowMenu *menu;
+  MetaWorkspaceLayout layout;
+  
+  if (window->display->window_menu)
+    {
+      meta_ui_window_menu_free (window->display->window_menu);
+      window->display->window_menu = NULL;
+      window->display->window_with_menu = NULL;
+    }
+
+  ops = 0;
+  insensitive = 0;
+
+  ops |= (META_MENU_OP_DELETE | META_MENU_OP_WORKSPACES | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
+
+  meta_screen_calc_workspace_layout (window->screen,
+                                     meta_screen_get_n_workspaces (window->screen),
+                                     meta_workspace_index ( window->screen->active_workspace),
+                                     &layout);
+
+  if (!window->on_all_workspaces)
+    {
+      if (layout.current_col > 0)
+        ops |= META_MENU_OP_MOVE_LEFT;
+      if (layout.current_col < layout.cols - 1)
+        ops |= META_MENU_OP_MOVE_RIGHT;
+      if (layout.current_row > 0)
+        ops |= META_MENU_OP_MOVE_UP;
+      if (layout.current_row < layout.rows - 1)
+        ops |= META_MENU_OP_MOVE_DOWN;
+    }
+
+  if (window->maximized)
+    ops |= META_MENU_OP_UNMAXIMIZE;
+  else
+    ops |= META_MENU_OP_MAXIMIZE;
+  
+#if 0
+  if (window->shaded)
+    ops |= META_MENU_OP_UNSHADE;
+  else
+    ops |= META_MENU_OP_SHADE;
+#endif
+
+  if (window->on_all_workspaces)
+    ops |= META_MENU_OP_UNSTICK;
+  else
+    ops |= META_MENU_OP_STICK;
+
+  if (window->wm_state_above)
+    ops |= META_MENU_OP_UNABOVE;
+  else
+    ops |= META_MENU_OP_ABOVE;
+  
+  if (!window->has_maximize_func)
+    insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE;
+  
+  if (!window->has_minimize_func)
+    insensitive |= META_MENU_OP_MINIMIZE;
+  
+  if (!window->has_close_func)
+    insensitive |= META_MENU_OP_DELETE;
+
+  if (!window->has_shade_func)
+    insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE;
+
+  if (!META_WINDOW_ALLOWS_MOVE (window))
+    insensitive |= META_MENU_OP_MOVE;
+
+  if (!META_WINDOW_ALLOWS_RESIZE (window))
+    insensitive |= META_MENU_OP_RESIZE;
+
+   if (window->always_sticky)
+     insensitive |= META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES;
+
+  if ((window->type == META_WINDOW_DESKTOP) ||
+      (window->type == META_WINDOW_DOCK) ||
+      (window->type == META_WINDOW_SPLASHSCREEN))
+    insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE;
+  
+  menu =
+    meta_ui_window_menu_new (window->screen->ui,
+                             window->xwindow,
+                             ops,
+                             insensitive,
+                             meta_window_get_net_wm_desktop (window),
+                             meta_screen_get_n_workspaces (window->screen),
+                             menu_callback,
+                             NULL); 
+
+  window->display->window_menu = menu;
+  window->display->window_with_menu = window;
+  
+  meta_verbose ("Popping up window menu for %s\n", window->desc);
+  
+  meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
+}
+
+static void
+clear_moveresize_time (MetaWindow *window)
+{
+  /* Forces the next update to actually do something */
+  window->display->grab_last_moveresize_time.tv_sec = 0;
+  window->display->grab_last_moveresize_time.tv_usec = 0;
+}
+
+static gboolean
+check_moveresize_frequency (MetaWindow *window)
+{
+  GTimeVal current_time;
+  double elapsed;
+  double max_resizes_per_second;
+  
+  g_get_current_time (&current_time);
+
+  /* use milliseconds, 1000 milliseconds/second */
+  elapsed =
+    ((((double)current_time.tv_sec - window->display->grab_last_moveresize_time.tv_sec) * G_USEC_PER_SEC +
+      (current_time.tv_usec - window->display->grab_last_moveresize_time.tv_usec))) / 1000.0;
+
+#ifdef HAVE_XSYNC
+  if (window->display->grab_update_alarm != None)
+    max_resizes_per_second = 1.0;   /* this is max resizes without
+                                     * getting any alarms; we resize
+                                     * immediately if we get one.
+                                     * i.e. this is a timeout for the
+                                     * client getting stuck.
+                                     */
+  else
+#endif /* HAVE_XSYNC */
+    max_resizes_per_second = 20.0;
+  
+#define EPSILON (1e-6)  
+  if (elapsed >= 0.0 && elapsed < (1000.0 / max_resizes_per_second))
+    {
+      meta_topic (META_DEBUG_RESIZING,
+                  "Delaying move/resize as only %g of %g seconds elapsed\n",
+                  elapsed / 1000.0, 1.0 / max_resizes_per_second);
+      return FALSE;
+    }
+  else if (elapsed < (0.0 - EPSILON)) /* handle clock getting set backward */
+    clear_moveresize_time (window);
+  
+  /* store latest time */
+  window->display->grab_last_moveresize_time = current_time;
+  
+  meta_topic (META_DEBUG_RESIZING,
+              " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n",
+              elapsed / 1000.0, 1.0 / max_resizes_per_second);
+  
+  return TRUE;
+}
+
+static void
+update_move (MetaWindow  *window,
+             unsigned int mask,
+             int          x,
+             int          y)
+{
+  int dx, dy;
+  int new_x, new_y;
+  int shake_threshold;
+  
+  window->display->grab_latest_motion_x = x;
+  window->display->grab_latest_motion_y = y;
+  
+  dx = x - window->display->grab_anchor_root_x;
+  dy = y - window->display->grab_anchor_root_y;
+
+  new_x = window->display->grab_anchor_window_pos.x + dx;
+  new_y = window->display->grab_anchor_window_pos.y + dy;
+
+  meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
+                x, y,
+                window->display->grab_anchor_root_x,
+                window->display->grab_anchor_root_y,
+                window->display->grab_anchor_window_pos.x,
+                window->display->grab_anchor_window_pos.y,
+                dx, dy);
+  
+  /* shake loose (unmaximize) maximized window if dragged beyond the threshold
+   * in the Y direction. You can't pull a window loose via X motion.
+   */
+
+#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
+  shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
+    DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
+    
+  if (window->maximized && ABS (dy) >= shake_threshold)
+    {
+      double prop;
+
+      /* Shake loose */
+      window->shaken_loose = TRUE;
+                  
+      /* move the unmaximized window to the cursor */
+      prop = 
+        ((double)(x - window->display->grab_initial_window_pos.x)) / 
+        ((double)window->display->grab_initial_window_pos.width);
+
+      window->display->grab_initial_window_pos.x = 
+        x - window->saved_rect.width * prop;
+      window->display->grab_initial_window_pos.y = y;
+
+      if (window->frame)
+        {
+          window->display->grab_initial_window_pos.y += window->frame->child_y / 2;
+        }
+
+      window->saved_rect.x = window->display->grab_initial_window_pos.x;
+      window->saved_rect.y = window->display->grab_initial_window_pos.y;
+      window->display->grab_anchor_root_x = x;
+      window->display->grab_anchor_root_y = y;
+
+      meta_window_unmaximize (window);
+
+      return;
+    }
+  /* remaximize window on an other xinerama monitor if window has
+   * been shaken loose or it is still maximized (then move straight)
+   */
+  else if (window->shaken_loose || window->maximized)
+    {
+      const MetaXineramaScreenInfo *wxinerama;
+      MetaRectangle work_area;
+      int monitor;
+
+      wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
+
+      for (monitor = 0; monitor < window->screen->n_xinerama_infos; monitor++)
+        {
+          meta_window_get_work_area_for_xinerama (window, monitor, &work_area);
+
+          /* check if cursor is near the top of a xinerama work area */ 
+          if (x >= work_area.x &&
+              x < (work_area.x + work_area.width) &&
+              y >= work_area.y &&
+              y < (work_area.y + shake_threshold))
+            {
+              /* move the saved rect if window will become maximized on an
+               * other monitor so user isn't surprised on a later unmaximize
+               */
+              if (wxinerama->number != monitor)
+                {
+                  window->saved_rect.x = work_area.x;
+                  window->saved_rect.y = work_area.y;
+                  
+                  if (window->frame) 
+                    {
+                      window->saved_rect.x += window->frame->child_x;
+                      window->saved_rect.y += window->frame->child_y;
+                    }
+
+                  meta_window_unmaximize (window);
+                }
+
+              window->display->grab_initial_window_pos = work_area;
+              window->display->grab_anchor_root_x = x;
+              window->display->grab_anchor_root_y = y;
+              window->shaken_loose = FALSE;
+              
+              meta_window_maximize (window);
+
+              return;
+            }
+        }
+    }
+
+  /* don't allow a maximized window to move */
+  if (window->maximized)
+    return;
+
+  if (window->display->grab_wireframe_active)
+    {
+      /* FIXME Horribly broken, does not honor position
+       * constraints
+       */
+      MetaRectangle new_xor;
+      new_xor = window->display->grab_wireframe_rect;
+      new_xor.x = new_x;
+      new_xor.y = new_y;
+
+      meta_effects_update_wireframe (window->screen,
+                                     &window->display->grab_wireframe_rect,
+                                     &new_xor);
+      window->display->grab_wireframe_rect = new_xor;
+    }
+  else
+    {
+      /* FIXME, edge snapping broken in wireframe mode */
+      if (mask & ShiftMask)
+        {
+          /* snap to edges */
+          new_x = meta_window_find_nearest_vertical_edge (window, new_x);
+          new_y = meta_window_find_nearest_horizontal_edge (window, new_y);
+        }
+      
+      meta_window_move (window, TRUE, new_x, new_y);
+    }
+}
+
+static void
+update_resize (MetaWindow *window,
+               int x, int y)
+{
+  int dx, dy;
+  int new_w, new_h;
+  int gravity;
+  MetaRectangle old;
+  int new_x, new_y;
+  
+  window->display->grab_latest_motion_x = x;
+  window->display->grab_latest_motion_y = y;
+  
+  dx = x - window->display->grab_anchor_root_x;
+  dy = y - window->display->grab_anchor_root_y;
+
+  new_w = window->display->grab_anchor_window_pos.width;
+  new_h = window->display->grab_anchor_window_pos.height;
+
+  /* FIXME this is only used in wireframe mode */
+  new_x = window->display->grab_anchor_window_pos.x;
+  new_y = window->display->grab_anchor_window_pos.y;
+  
+  switch (window->display->grab_op)
+    {
+    case META_GRAB_OP_RESIZING_SE:
+    case META_GRAB_OP_RESIZING_NE:
+    case META_GRAB_OP_RESIZING_E:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_E:
+      new_w += dx;
+      break;
+
+    case META_GRAB_OP_RESIZING_NW:
+    case META_GRAB_OP_RESIZING_SW:
+    case META_GRAB_OP_RESIZING_W:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_W:
+      new_w -= dx;
+      new_x += dx;
+      break;
+      
+    default:
+      break;
+    }
+  
+  switch (window->display->grab_op)
+    {
+    case META_GRAB_OP_RESIZING_SE:
+    case META_GRAB_OP_RESIZING_S:
+    case META_GRAB_OP_RESIZING_SW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_S:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+      new_h += dy;
+      break;
+      
+    case META_GRAB_OP_RESIZING_N:
+    case META_GRAB_OP_RESIZING_NE:
+    case META_GRAB_OP_RESIZING_NW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_N:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+      new_h -= dy;
+      new_y += dy;
+      break;
+    default:
+      break;
+    }
+
+  if (!check_moveresize_frequency (window))
+    return;
+  
+  old = window->rect;
+
+  /* compute gravity of client during operation */
+  gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
+  g_assert (gravity >= 0);
+  
+  if (window->display->grab_wireframe_active)
+    {
+      /* FIXME This is crap. For example, the wireframe isn't
+       * constrained in the way that a real resize would be. An
+       * obvious elegant solution is to unmap the window during
+       * wireframe, but still resize it; however, that probably
+       * confuses broken clients that have problems with opaque
+       * resize, they probably don't track their visibility.
+       */
+      MetaRectangle new_xor;
+      
+      if ((new_x + new_w <= new_x) || (new_y + new_h <= new_y))
+        return;
+      
+      new_xor.x = new_x;
+      new_xor.y = new_y;
+      new_xor.width = new_w;
+      new_xor.height = new_h;
+
+      meta_effects_update_wireframe (window->screen,
+                                     &window->display->grab_wireframe_rect,
+                                     &new_xor);
+      window->display->grab_wireframe_rect = new_xor;
+
+      /* do this after drawing the wires, so we don't draw over it */
+      meta_window_refresh_resize_popup (window);
+    }
+  else
+    {
+      meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
+    }
+
+  /* If we don't actually resize the window, we clear the timestamp,
+   * so we'll quickly try again.  Otherwise you get "stuck" because
+   * the window doesn't increment its _METACITY_UPDATE_COUNTER when
+   * nothing happens.
+   */
+  if (window->rect.width == old.width &&
+      window->rect.height == old.height)
+    clear_moveresize_time (window);
+}
+
+typedef struct
+{
+  const XEvent *current_event;
+  int count;
+  Time last_time;
+} EventScannerData;
+
+static Bool
+find_last_time_predicate (Display  *display,
+                          XEvent   *xevent,
+                          XPointer  arg)
+{
+  EventScannerData *esd = (void*) arg;
+
+  if (esd->current_event->type == xevent->type &&
+      esd->current_event->xany.window == xevent->xany.window)
+    {
+      esd->count += 1;
+      esd->last_time = xevent->xmotion.time;
+    }
+
+  return False;
+}
+
+static gboolean
+check_use_this_motion_notify (MetaWindow *window,
+                              XEvent     *event)
+{
+  EventScannerData esd;
+  XEvent useless;
+
+  /* This code is copied from Owen's GDK code. */
+  
+  if (window->display->grab_motion_notify_time != 0)
+    {
+      /* == is really the right test, but I'm all for paranoia */
+      if (window->display->grab_motion_notify_time <=
+          event->xmotion.time)
+        {
+          meta_topic (META_DEBUG_RESIZING,
+                      "Arrived at event with time %lu (waiting for %lu), using it\n",
+                      (unsigned long) event->xmotion.time,
+                      (unsigned long) window->display->grab_motion_notify_time);
+          window->display->grab_motion_notify_time = 0;
+          return TRUE;
+        }
+      else
+        return FALSE; /* haven't reached the saved timestamp yet */
+    }
+  
+  esd.current_event = event;
+  esd.count = 0;
+  esd.last_time = 0;
+
+  /* "useless" isn't filled in because the predicate never returns True */
+  XCheckIfEvent (window->display->xdisplay,
+                 &useless,
+                 find_last_time_predicate,
+                 (XPointer) &esd);
+
+  if (esd.count > 0)
+    meta_topic (META_DEBUG_RESIZING,
+                "Will skip %d motion events and use the event with time %lu\n",
+                esd.count, (unsigned long) esd.last_time);
+  
+  if (esd.last_time == 0)
+    return TRUE;
+  else
+    {
+      /* Save this timestamp, and ignore all motion notify
+       * until we get to the one with this stamp.
+       */
+      window->display->grab_motion_notify_time = esd.last_time;
+      return FALSE;
+    }
+}
+
+void
+meta_window_handle_mouse_grab_op_event (MetaWindow *window,
+                                        XEvent     *event)
+{
+#ifdef HAVE_XSYNC
+  if (event->type == (window->display->xsync_event_base + XSyncAlarmNotify))
+    {
+      meta_topic (META_DEBUG_RESIZING,
+                  "Alarm event received last motion x = %d y = %d\n",
+                  window->display->grab_latest_motion_x,
+                  window->display->grab_latest_motion_y);
+      
+      /* This means we are ready for another configure. */
+      switch (window->display->grab_op)
+        {
+        case META_GRAB_OP_RESIZING_E:
+        case META_GRAB_OP_RESIZING_W:
+        case META_GRAB_OP_RESIZING_S:
+        case META_GRAB_OP_RESIZING_N:
+        case META_GRAB_OP_RESIZING_SE:
+        case META_GRAB_OP_RESIZING_SW:
+        case META_GRAB_OP_RESIZING_NE:
+        case META_GRAB_OP_RESIZING_NW:
+        case META_GRAB_OP_KEYBOARD_RESIZING_S:
+        case META_GRAB_OP_KEYBOARD_RESIZING_N:
+        case META_GRAB_OP_KEYBOARD_RESIZING_W:
+        case META_GRAB_OP_KEYBOARD_RESIZING_E:
+        case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+        case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+        case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+        case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+          clear_moveresize_time (window); /* force update to do something */
+
+          /* no pointer round trip here, to keep in sync */
+          update_resize (window,
+                         window->display->grab_latest_motion_x,
+                         window->display->grab_latest_motion_y);
+          break;
+          
+        default:
+          break;
+        }
+    }
+#endif /* HAVE_XSYNC */
+  
+  switch (event->type)
+    {
+    case ButtonRelease:      
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          clear_moveresize_time (window);
+          if (event->xbutton.root == window->screen->xroot)
+            update_move (window, event->xbutton.state,
+                         event->xbutton.x_root, event->xbutton.y_root);
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          clear_moveresize_time (window);
+          if (event->xbutton.root == window->screen->xroot)
+            update_resize (window, event->xbutton.x_root, event->xbutton.y_root);
+        }
+
+      meta_display_end_grab_op (window->display, event->xbutton.time);
+      break;    
+
+    case MotionNotify:
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          if (event->xmotion.root == window->screen->xroot)
+            {
+              if (check_use_this_motion_notify (window,
+                                                event))
+                update_move (window,
+                             event->xmotion.state,
+                             event->xmotion.x_root,
+                             event->xmotion.y_root);
+            }
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          if (event->xmotion.root == window->screen->xroot)
+            {
+              if (check_use_this_motion_notify (window,
+                                                event))
+                update_resize (window,
+                               event->xmotion.x_root,
+                               event->xmotion.y_root);
+            }
+        }
+      break;
+
+    case EnterNotify:
+    case LeaveNotify:
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          if (event->xcrossing.root == window->screen->xroot)
+            update_move (window,
+                         event->xcrossing.state,
+                         event->xcrossing.x_root,
+                         event->xcrossing.y_root);
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          if (event->xcrossing.root == window->screen->xroot)
+            update_resize (window,
+                           event->xcrossing.x_root,
+                           event->xcrossing.y_root);
+        }
+      break;
+    default:
+      break;
+    }
+}
+
+gboolean
+meta_window_shares_some_workspace (MetaWindow *window,
+                                   MetaWindow *with)
+{
+  GList *tmp;
+  
+  if (window->on_all_workspaces ||
+      with->on_all_workspaces)
+    return TRUE;
+  
+  tmp = window->workspaces;
+  while (tmp != NULL)
+    {
+      if (g_list_find (with->workspaces, tmp->data) != NULL)
+        return TRUE;
+
+      tmp = tmp->next;
+    }
+
+  return FALSE;
+}
+
+void
+meta_window_set_gravity (MetaWindow *window,
+                         int         gravity)
+{
+  XSetWindowAttributes attrs;
+
+  meta_verbose ("Setting gravity of %s to %d\n", window->desc, gravity);
+
+  attrs.win_gravity = gravity;
+  
+  meta_error_trap_push (window->display);
+
+  XChangeWindowAttributes (window->display->xdisplay,
+                           window->xwindow,
+                           CWWinGravity,
+                           &attrs);
+  
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+static void
+get_work_area_xinerama (MetaWindow    *window,
+                        MetaRectangle *area,
+                        int            which_xinerama)
+{
+  MetaRectangle space_area;
+  GList *tmp;  
+  int left_strut;
+  int right_strut;
+  int top_strut;
+  int bottom_strut;  
+  int xinerama_origin_x;
+  int xinerama_origin_y;
+  int xinerama_width;
+  int xinerama_height;
+  
+  g_assert (which_xinerama >= 0);
+
+  xinerama_origin_x = window->screen->xinerama_infos[which_xinerama].x_origin;
+  xinerama_origin_y = window->screen->xinerama_infos[which_xinerama].y_origin;
+  xinerama_width = window->screen->xinerama_infos[which_xinerama].width;
+  xinerama_height = window->screen->xinerama_infos[which_xinerama].height;
+  
+  left_strut = 0;
+  right_strut = 0;
+  top_strut = 0;
+  bottom_strut = 0;
+  
+  tmp = meta_window_get_workspaces (window);  
+  while (tmp != NULL)
+    {
+      meta_workspace_get_work_area_for_xinerama (tmp->data,
+                                                 which_xinerama,
+                                                 &space_area);
+
+      left_strut = MAX (left_strut, space_area.x - xinerama_origin_x);
+      right_strut = MAX (right_strut,
+			 (xinerama_width - 
+			  (space_area.x - xinerama_origin_x) - 
+			  space_area.width));
+      top_strut = MAX (top_strut, space_area.y - xinerama_origin_y);
+      bottom_strut = MAX (bottom_strut,
+			  (xinerama_height - 
+			   (space_area.y - xinerama_origin_y) - 
+			   space_area.height));
+      tmp = tmp->next;
+    }
+  
+  area->x = xinerama_origin_x + left_strut;
+  area->y = xinerama_origin_y + top_strut;
+  area->width = xinerama_width - left_strut - right_strut;
+  area->height = xinerama_height - top_strut - bottom_strut;
+
+  meta_topic (META_DEBUG_WORKAREA,
+              "Window %s xinerama %d has work area %d,%d %d x %d\n",
+              window->desc, which_xinerama,
+              area->x, area->y, area->width, area->height);
+}
+
+void
+meta_window_get_work_area_current_xinerama (MetaWindow    *window,
+					    MetaRectangle *area)
+{
+  const MetaXineramaScreenInfo *xinerama = NULL;
+  xinerama = meta_screen_get_xinerama_for_window (window->screen,
+						  window);
+
+  meta_window_get_work_area_for_xinerama (window,
+                                          xinerama->number,
+                                          area);
+}
+
+void
+meta_window_get_work_area_for_xinerama (MetaWindow    *window,
+					int            which_xinerama,
+					MetaRectangle *area)
+{
+  g_return_if_fail (which_xinerama >= 0);
+  
+  get_work_area_xinerama (window,
+                          area,
+                          which_xinerama);
+}
+
+void
+meta_window_get_work_area_all_xineramas (MetaWindow    *window,
+                                         MetaRectangle *area)
+{
+  MetaRectangle space_area;
+  GList *tmp;  
+  int left_strut;
+  int right_strut;
+  int top_strut;
+  int bottom_strut;  
+  int screen_origin_x;
+  int screen_origin_y;
+  int screen_width;
+  int screen_height;
+
+  screen_origin_x = 0;
+  screen_origin_y = 0;
+  screen_width = window->screen->width;
+  screen_height = window->screen->height;
+  
+  left_strut = 0;
+  right_strut = 0;
+  top_strut = 0;
+  bottom_strut = 0;
+  
+  tmp = meta_window_get_workspaces (window);  
+  while (tmp != NULL)
+    {
+      meta_workspace_get_work_area_all_xineramas (tmp->data,
+                                                  &space_area);
+
+      left_strut = MAX (left_strut, space_area.x - screen_origin_x);
+      right_strut = MAX (right_strut,
+			 (screen_width - 
+			  (space_area.x - screen_origin_x) - 
+			  space_area.width));
+      top_strut = MAX (top_strut, space_area.y - screen_origin_y);
+      bottom_strut = MAX (bottom_strut,
+			  (screen_height - 
+			   (space_area.y - screen_origin_y) - 
+			   space_area.height));      
+      tmp = tmp->next;
+    }
+  
+  area->x = screen_origin_x + left_strut;
+  area->y = screen_origin_y + top_strut;
+  area->width = screen_width - left_strut - right_strut;
+  area->height = screen_height - top_strut - bottom_strut;
+
+  meta_topic (META_DEBUG_WORKAREA,
+              "Window %s has whole-screen work area %d,%d %d x %d\n",
+              window->desc, area->x, area->y, area->width, area->height);
+}
+
+
+gboolean
+meta_window_same_application (MetaWindow *window,
+                              MetaWindow *other_window)
+{
+  return
+    meta_window_get_group (window) ==
+    meta_window_get_group (other_window);
+}
+
+void
+meta_window_refresh_resize_popup (MetaWindow *window)
+{
+  if (window->display->grab_op == META_GRAB_OP_NONE)
+    return;
+
+  if (window->display->grab_window != window)
+    return;
+
+  /* FIXME for now we bail out when doing wireframe, because our
+   * server grab keeps us from being able to redraw the stuff
+   * underneath the resize popup.
+   */
+  if (window->display->grab_wireframe_active)
+    return;
+  
+  switch (window->display->grab_op)
+    {
+    case META_GRAB_OP_RESIZING_SE:
+    case META_GRAB_OP_RESIZING_S:
+    case META_GRAB_OP_RESIZING_SW:
+    case META_GRAB_OP_RESIZING_N:
+    case META_GRAB_OP_RESIZING_NE:
+    case META_GRAB_OP_RESIZING_NW:
+    case META_GRAB_OP_RESIZING_W:
+    case META_GRAB_OP_RESIZING_E:
+    case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+    case META_GRAB_OP_KEYBOARD_RESIZING_S:
+    case META_GRAB_OP_KEYBOARD_RESIZING_N:
+    case META_GRAB_OP_KEYBOARD_RESIZING_W:
+    case META_GRAB_OP_KEYBOARD_RESIZING_E:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+      break;
+
+    default:
+      /* Not resizing */
+      return;
+    }
+      
+  if (window->display->grab_resize_popup == NULL)
+    {
+      if (window->size_hints.width_inc > 1 ||
+          window->size_hints.height_inc > 1)
+        window->display->grab_resize_popup =
+          meta_ui_resize_popup_new (window->display->xdisplay,
+                                    window->screen->number);
+    }
+  
+  if (window->display->grab_resize_popup != NULL)
+    {
+      int gravity;
+      int x, y, width, height;
+      MetaFrameGeometry fgeom;
+
+      if (window->frame)
+        meta_frame_calc_geometry (window->frame, &fgeom);
+      else
+        {
+          fgeom.left_width = 0;
+          fgeom.right_width = 0;
+          fgeom.top_height = 0;
+          fgeom.bottom_height = 0;
+        }
+      
+      gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
+      g_assert (gravity >= 0);
+
+      if (window->display->grab_wireframe_active)
+        {
+          x = window->display->grab_wireframe_rect.x;
+          y = window->display->grab_wireframe_rect.y;
+          width = window->display->grab_wireframe_rect.width;
+          height = window->display->grab_wireframe_rect.height;
+        }
+      else
+        {
+          meta_window_get_position (window, &x, &y);
+          width = window->rect.width;
+          height = window->rect.height;
+        }
+      
+      meta_ui_resize_popup_set (window->display->grab_resize_popup,
+                                gravity,
+                                x, y,
+                                width, height,
+                                window->size_hints.base_width,
+                                window->size_hints.base_height,
+                                window->size_hints.min_width,
+                                window->size_hints.min_height,
+                                window->size_hints.width_inc,
+                                window->size_hints.height_inc,
+                                fgeom.left_width,
+                                fgeom.right_width,
+                                fgeom.top_height,
+                                fgeom.bottom_height);
+
+      meta_ui_resize_popup_set_showing (window->display->grab_resize_popup,
+                                        TRUE);
+    }
+}
+
+void
+meta_window_foreach_transient (MetaWindow            *window,
+                               MetaWindowForeachFunc  func,
+                               void                  *data)
+{
+  GSList *windows;
+  GSList *tmp;
+
+  windows = meta_display_list_windows (window->display);
+
+  tmp = windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *transient = tmp->data;
+      
+      if (meta_window_is_ancestor_of_transient (window, transient))
+        {
+          if (!(* func) (transient, data))
+            break;
+        }
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (windows);
+}
+
+void
+meta_window_foreach_ancestor (MetaWindow            *window,
+                              MetaWindowForeachFunc  func,
+                              void                  *data)
+{
+  MetaWindow *w;
+  MetaWindow *tortoise;
+  
+  w = window;
+  tortoise = window;
+  while (TRUE)
+    {
+      if (w->xtransient_for == None ||
+          w->transient_parent_is_root_window)
+        break;
+      
+      w = meta_display_lookup_x_window (w->display, w->xtransient_for);
+      
+      if (w == NULL || w == tortoise)
+        break;
+
+      if (!(* func) (w, data))
+        break;      
+      
+      if (w->xtransient_for == None ||
+          w->transient_parent_is_root_window)
+        break;
+
+      w = meta_display_lookup_x_window (w->display, w->xtransient_for);
+      
+      if (w == NULL || w == tortoise)
+        break;
+
+      if (!(* func) (w, data))
+        break;
+      
+      tortoise = meta_display_lookup_x_window (tortoise->display,
+                                               tortoise->xtransient_for);
+
+      /* "w" should have already covered all ground covered by the
+       * tortoise, so the following must hold.
+       */
+      g_assert (tortoise != NULL);
+      g_assert (tortoise->xtransient_for != None);
+      g_assert (!tortoise->transient_parent_is_root_window);
+    }
+}
+
+typedef struct
+{
+  MetaWindow *ancestor;
+  gboolean found;
+} FindAncestorData;
+
+static gboolean
+find_ancestor_func (MetaWindow *window,
+                    void       *data)
+{
+  FindAncestorData *d = data;
+
+  if (window == d->ancestor)
+    {
+      d->found = TRUE;
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+meta_window_is_ancestor_of_transient (MetaWindow *window,
+                                      MetaWindow *transient)
+{
+  FindAncestorData d;
+
+  d.ancestor = window;
+  d.found = FALSE;
+
+  meta_window_foreach_ancestor (transient, find_ancestor_func, &d);
+
+  return d.found;
+}
+
+/* Warp pointer to location appropriate for grab,
+ * return root coordinates where pointer ended up.
+ */
+static gboolean
+warp_grab_pointer (MetaWindow          *window,
+                   MetaGrabOp           grab_op,
+                   int                 *x,
+                   int                 *y)
+{
+  MetaRectangle rect;
+
+  /* We may not have done begin_grab_op yet, i.e. may not be in a grab
+   */
+  
+  if (window == window->display->grab_window &&
+      window->display->grab_wireframe_active)
+    rect = window->display->grab_wireframe_rect;
+  else
+    {
+      rect = window->rect;
+      meta_window_get_position (window, &rect.x, &rect.y);
+    }
+  
+  switch (grab_op)
+    {
+      case META_GRAB_OP_KEYBOARD_MOVING:
+      case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+        *x = rect.width / 2;
+        *y = rect.height / 2;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_S:
+        *x = rect.width / 2;
+        *y = rect.height;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_N:
+        *x = rect.width / 2;
+        *y = 0;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_W:
+        *x = 0;
+        *y = rect.height / 2;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_E:
+        *x = rect.width;
+        *y = rect.height / 2;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+        *x = rect.width;
+        *y = rect.height;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+        *x = rect.width;
+        *y = 0;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+        *x = 0;
+        *y = rect.height;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+        *x = 0;
+        *y = 0;
+        break;
+
+      default:
+        return FALSE;
+    }
+
+  *x += rect.x;
+  *y += rect.y;
+  
+  meta_error_trap_push_with_return (window->display);
+
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Warping pointer to %d,%d with window at %d,%d\n",
+              *x, *y, rect.x, rect.y);
+  
+  XWarpPointer (window->display->xdisplay,
+                None,
+                window->screen->xroot,
+                0, 0, 0, 0, 
+                *x, *y);
+
+  if (meta_error_trap_pop_with_return (window->display, FALSE) != Success)
+    {
+      meta_verbose ("Failed to warp pointer for window %s\n",
+                    window->desc);
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+void
+meta_window_begin_grab_op (MetaWindow *window,
+                           MetaGrabOp  op,
+                           Time        timestamp)
+{
+  int x, y;
+  gulong grab_start_serial;
+
+  grab_start_serial = XNextRequest (window->display->xdisplay);
+  
+  meta_window_raise (window);
+
+  warp_grab_pointer (window,
+                     op, &x, &y);
+
+  meta_display_begin_grab_op (window->display,
+                              window->screen,
+                              window,
+                              op,
+                              FALSE,
+                              grab_start_serial /* event_serial */,
+                              0 /* button */,
+                              0,
+                              timestamp,
+                              x, y);
+
+  /* We override the one set in display_begin_grab_op since we
+   * did additional stuff as part of the grabbing process
+   */
+  window->display->grab_start_serial = grab_start_serial;
+}
+
+void
+meta_window_update_keyboard_resize (MetaWindow *window,
+                                    gboolean    update_cursor)
+{
+  int x, y;
+  
+  warp_grab_pointer (window,
+                     window->display->grab_op,
+                     &x, &y);
+
+  {
+    /* As we warped the pointer, we have to reset the anchor state,
+     * since if the mouse moves we want to use those events to do the
+     * right thing. Also, this means that the motion notify
+     * from the pointer warp comes back as a no-op.
+     */
+    int dx, dy;
+
+    dx = x - window->display->grab_anchor_root_x;
+    dy = y - window->display->grab_anchor_root_y;
+    
+    window->display->grab_anchor_root_x += dx;
+    window->display->grab_anchor_root_y += dy;
+    if (window->display->grab_wireframe_active)
+      {
+        window->display->grab_anchor_window_pos =
+          window->display->grab_wireframe_rect;
+      }
+    else
+      {
+        window->display->grab_anchor_window_pos = window->rect;
+        meta_window_get_position (window,
+                                  &window->display->grab_anchor_window_pos.x,
+                                  &window->display->grab_anchor_window_pos.y);
+      }
+  }
+  
+  if (update_cursor)
+    {
+      meta_display_set_grab_op_cursor (window->display,
+                                       NULL,
+                                       window->display->grab_op,
+                                       TRUE,
+                                       window->display->grab_xwindow,
+                                       meta_display_get_current_time (window->display));
+    }
+}
+
+void
+meta_window_update_keyboard_move (MetaWindow *window)
+{
+  int x, y;
+  
+  warp_grab_pointer (window,
+                     window->display->grab_op,
+                     &x, &y);
+}
+
+const char*
+window_type_to_string (MetaWindowType type)
+{
+  switch (type)
+    {
+    case META_WINDOW_NORMAL:
+      return "normal";
+      break;
+    case META_WINDOW_DESKTOP:
+      return "desktop";
+      break;
+    case META_WINDOW_DOCK:
+      return "dock";
+      break;
+    case META_WINDOW_DIALOG:
+      return "dialog";
+      break;
+    case META_WINDOW_MODAL_DIALOG:
+      return "modal_dialog";
+      break;
+    case META_WINDOW_TOOLBAR:
+      return "toolbar";
+      break;
+    case META_WINDOW_MENU:
+      return "menu";
+      break;
+    case META_WINDOW_SPLASHSCREEN:
+      return "splashscreen";
+      break;
+    case META_WINDOW_UTILITY:
+      return "utility";
+      break;
+    }
+
+  return "";
+} 
+
+MetaWindowType
+window_type_from_string (const char *str)
+{
+  if (strcmp (str, "normal") == 0)
+    return META_WINDOW_NORMAL;
+  else if (strcmp (str, "desktop") == 0)
+    return META_WINDOW_DESKTOP;
+  else if (strcmp (str, "dock") == 0)
+    return META_WINDOW_DOCK;
+  else if (strcmp (str, "dialog") == 0)
+    return META_WINDOW_DIALOG;
+  else if (strcmp (str, "modal_dialog") == 0)
+    return META_WINDOW_MODAL_DIALOG;
+  else if (strcmp (str, "toolbar") == 0)
+    return META_WINDOW_TOOLBAR;
+  else if (strcmp (str, "menu") == 0)
+    return META_WINDOW_MENU;
+  else if (strcmp (str, "utility") == 0)
+    return META_WINDOW_UTILITY;
+  else if (strcmp (str, "splashscreen") == 0)
+    return META_WINDOW_SPLASHSCREEN;
+  else
+    return META_WINDOW_NORMAL;
+}
+
+const char*
+window_gravity_to_string (int gravity)
+{
+  switch (gravity)
+    {
+    case NorthWestGravity:
+      return "NorthWestGravity";
+      break;
+    case NorthGravity:
+      return "NorthGravity";
+      break;
+    case NorthEastGravity:
+      return "NorthEastGravity";
+      break;
+    case WestGravity:
+      return "WestGravity";
+      break;
+    case CenterGravity:
+      return "CenterGravity";
+      break;
+    case EastGravity:
+      return "EastGravity";
+      break;
+    case SouthWestGravity:
+      return "SouthWestGravity";
+      break;
+    case SouthGravity:
+      return "SouthGravity";
+      break;
+    case SouthEastGravity:
+      return "SouthEastGravity";
+      break;
+    case StaticGravity:
+      return "StaticGravity";
+      break;
+    default:
+      return "NorthWestGravity";
+      break;
+    }
+}
+  
+int
+window_gravity_from_string (const char *str)
+{
+  if (strcmp (str, "NorthWestGravity") == 0)
+    return NorthWestGravity;
+  else if (strcmp (str, "NorthGravity") == 0)
+    return NorthGravity;
+  else if (strcmp (str, "NorthEastGravity") == 0)
+    return NorthEastGravity;
+  else if (strcmp (str, "WestGravity") == 0)
+    return WestGravity;
+  else if (strcmp (str, "CenterGravity") == 0)
+    return CenterGravity;
+  else if (strcmp (str, "EastGravity") == 0)
+    return EastGravity;
+  else if (strcmp (str, "SouthWestGravity") == 0)
+    return SouthWestGravity;
+  else if (strcmp (str, "SouthGravity") == 0)
+    return SouthGravity;
+  else if (strcmp (str, "SouthEastGravity") == 0)
+    return SouthEastGravity;
+  else if (strcmp (str, "StaticGravity") == 0)
+    return StaticGravity;
+  else
+    return NorthWestGravity;
+}
+
+void
+meta_window_update_layer (MetaWindow *window)
+{
+  MetaGroup *group;
+  
+  meta_stack_freeze (window->screen->stack);
+  group = meta_window_get_group (window);
+  if (group)
+    meta_group_update_layers (group);
+  else
+    meta_stack_update_layer (window->screen->stack, window);
+  meta_stack_thaw (window->screen->stack);
+}
diff -r -N -u metacity-2.8.1/src/window.c.orig metacity-2.8.1-new/src/window.c.orig
--- metacity-2.8.1/src/window.c.orig	1969-12-31 19:00:00.000000000 -0500
+++ metacity-2.8.1-new/src/window.c.orig	2004-04-14 14:58:08.000000000 -0400
@@ -0,0 +1,6928 @@
+/* Metacity X managed windows */
+
+/* 
+ * Copyright (C) 2001 Havoc Pennington, Anders Carlsson
+ * Copyright (C) 2002, 2003 Red Hat, Inc.
+ * Copyright (C) 2003 Rob Adams
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "window.h"
+#include "util.h"
+#include "frame.h"
+#include "errors.h"
+#include "workspace.h"
+#include "stack.h"
+#include "keybindings.h"
+#include "ui.h"
+#include "place.h"
+#include "session.h"
+#include "effects.h"
+#include "prefs.h"
+#include "resizepopup.h"
+#include "xprops.h"
+#include "group.h"
+#include "window-props.h"
+#include "constraints.h"
+
+#include <X11/Xatom.h>
+#include <string.h>
+
+#ifdef HAVE_SHAPE
+#include <X11/extensions/shape.h>
+#endif
+
+typedef enum
+{
+  META_IS_CONFIGURE_REQUEST = 1 << 0,
+  META_DO_GRAVITY_ADJUST    = 1 << 1,
+  META_USER_MOVE_RESIZE     = 1 << 2
+} MetaMoveResizeFlags;
+
+static int destroying_windows_disallowed = 0;
+
+
+static void     update_net_wm_state       (MetaWindow     *window);
+static void     update_mwm_hints          (MetaWindow     *window);
+static void     update_wm_class           (MetaWindow     *window);
+static void     update_transient_for      (MetaWindow     *window);
+static void     update_sm_hints           (MetaWindow     *window);
+static void     update_role               (MetaWindow     *window);
+static void     update_net_wm_type        (MetaWindow     *window);
+static void     update_net_frame_extents  (MetaWindow     *window);
+static void     recalc_window_type        (MetaWindow     *window);
+static void     recalc_window_features    (MetaWindow     *window);
+static void     invalidate_work_areas     (MetaWindow     *window);
+static void     set_wm_state              (MetaWindow     *window,
+                                           int             state);
+static void     set_net_wm_state          (MetaWindow     *window);
+
+static void     send_configure_notify     (MetaWindow     *window);
+static gboolean process_property_notify   (MetaWindow     *window,
+                                           XPropertyEvent *event);
+static void     meta_window_show          (MetaWindow     *window);
+static void     meta_window_hide          (MetaWindow     *window);
+
+static void     meta_window_save_rect         (MetaWindow    *window);
+
+static void meta_window_move_resize_internal (MetaWindow         *window,
+                                              MetaMoveResizeFlags flags,
+                                              int                 resize_gravity,
+                                              int                 root_x_nw,
+                                              int                 root_y_nw,
+                                              int                 w,
+                                              int                 h);
+
+
+void meta_window_move_resize_now (MetaWindow  *window);
+
+/* FIXME we need an abstraction that covers all these queues. */   
+
+void meta_window_unqueue_calc_showing (MetaWindow *window);
+void meta_window_flush_calc_showing   (MetaWindow *window);
+
+void meta_window_unqueue_move_resize  (MetaWindow *window);
+void meta_window_flush_move_resize    (MetaWindow *window);
+
+static void meta_window_update_icon_now (MetaWindow *window);
+void meta_window_unqueue_update_icon    (MetaWindow *window);
+void meta_window_flush_update_icon      (MetaWindow *window);
+
+static void meta_window_apply_session_info (MetaWindow                  *window,
+                                            const MetaWindowSessionInfo *info);
+
+#ifdef WITH_VERBOSE_MODE
+static const char*
+wm_state_to_string (int state)
+{
+  switch (state)
+    {
+    case NormalState:
+      return "NormalState";      
+    case IconicState:
+      return "IconicState";
+    case WithdrawnState:
+      return "WithdrawnState";
+    }
+
+  return "Unknown";
+}
+#endif
+
+static gboolean
+is_desktop_or_dock_foreach (MetaWindow *window,
+                            void       *data)
+{
+  gboolean *result = data;
+
+  *result =
+    window->type == META_WINDOW_DESKTOP ||
+    window->type == META_WINDOW_DOCK;
+  if (*result)
+    return FALSE; /* stop as soon as we find one */
+  else
+    return TRUE;
+}
+
+/* window is the window that's newly mapped provoking
+ * the possible change
+ */
+static void
+maybe_leave_show_desktop_mode (MetaWindow *window)
+{
+  gboolean is_desktop_or_dock;
+
+  if (!window->screen->showing_desktop)
+    return;
+
+  /* If the window is a transient for the dock or desktop, don't
+   * leave show desktop mode when the window opens. That's
+   * so you can e.g. hide all windows, manipulate a file on
+   * the desktop via a dialog, then unshow windows again.
+   */
+  is_desktop_or_dock = FALSE;
+  is_desktop_or_dock_foreach (window,
+                              &is_desktop_or_dock);
+
+  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
+                                &is_desktop_or_dock);
+
+  if (!is_desktop_or_dock)
+    {
+      meta_screen_minimize_all_except (window->screen, window);
+      meta_screen_unshow_desktop (window->screen);      
+    }
+}
+
+MetaWindow*
+meta_window_new (MetaDisplay *display,
+                 Window       xwindow,
+                 gboolean     must_be_viewable)
+{
+  XWindowAttributes attrs;
+  MetaWindow *window;
+  
+  meta_display_grab (display);
+  meta_error_trap_push (display); /* Push a trap over all of window
+                                   * creation, to reduce XSync() calls
+                                   */
+  
+  meta_error_trap_push_with_return (display);
+  
+  XGetWindowAttributes (display->xdisplay,
+                        xwindow, &attrs);
+  
+  if (meta_error_trap_pop_with_return (display, TRUE) != Success)
+    {
+      meta_verbose ("Failed to get attributes for window 0x%lx\n",
+                    xwindow);
+      meta_error_trap_pop (display, TRUE);
+      meta_display_ungrab (display);
+      return NULL;
+    }
+  window = meta_window_new_with_attrs (display, xwindow,
+                                       must_be_viewable, &attrs);
+
+
+  meta_error_trap_pop (display, FALSE);
+  meta_display_ungrab (display);
+
+  return window;
+}
+
+MetaWindow*
+meta_window_new_with_attrs (MetaDisplay       *display,
+                            Window             xwindow,
+                            gboolean           must_be_viewable,
+                            XWindowAttributes *attrs)
+{
+  MetaWindow *window;
+  GSList *tmp;
+  MetaWorkspace *space;
+  gulong existing_wm_state;
+  gulong event_mask;
+#define N_INITIAL_PROPS 12
+  Atom initial_props[N_INITIAL_PROPS];
+  int i;
+  gboolean has_shape;
+
+  g_assert (attrs != NULL);
+  g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props));
+  
+  meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
+
+  if (xwindow == display->no_focus_window)
+    {
+      meta_verbose ("Not managing no_focus_window 0x%lx\n",
+                    xwindow);
+      return NULL;
+    }
+
+  if (attrs->override_redirect)
+    {
+      meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
+      return NULL;
+    }
+  
+  /* Grab server */
+  meta_display_grab (display);
+  meta_error_trap_push (display); /* Push a trap over all of window
+                                   * creation, to reduce XSync() calls
+                                   */
+
+  meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
+                must_be_viewable,
+                attrs->map_state,
+                (attrs->map_state == IsUnmapped) ?
+                "IsUnmapped" :
+                (attrs->map_state == IsViewable) ?
+                "IsViewable" :
+                (attrs->map_state == IsUnviewable) ?
+                "IsUnviewable" :
+                "(unknown)");
+  
+  existing_wm_state = WithdrawnState;
+  if (must_be_viewable && attrs->map_state != IsViewable)
+    {
+      /* Only manage if WM_STATE is IconicState or NormalState */
+      gulong state;
+
+      /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
+      if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
+                                                   display->atom_wm_state,
+                                                   display->atom_wm_state,
+                                                   &state) &&
+            (state == IconicState || state == NormalState)))
+        {
+          meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
+          meta_error_trap_pop (display, TRUE);
+          meta_display_ungrab (display);
+          return NULL;
+        }
+
+      existing_wm_state = state;
+      meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
+                    wm_state_to_string (existing_wm_state));
+    }
+  
+  meta_error_trap_push_with_return (display);
+  
+  XAddToSaveSet (display->xdisplay, xwindow);
+
+  event_mask =
+    PropertyChangeMask | EnterWindowMask | LeaveWindowMask |
+    FocusChangeMask | ColormapChangeMask;
+
+  XSelectInput (display->xdisplay, xwindow, event_mask);
+
+  has_shape = FALSE;
+#ifdef HAVE_SHAPE
+  if (META_DISPLAY_HAS_SHAPE (display))
+    {
+      int x_bounding, y_bounding, x_clip, y_clip;
+      unsigned w_bounding, h_bounding, w_clip, h_clip;
+      int bounding_shaped, clip_shaped;
+
+      XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
+
+      XShapeQueryExtents (display->xdisplay, xwindow,
+                          &bounding_shaped, &x_bounding, &y_bounding,
+                          &w_bounding, &h_bounding,
+                          &clip_shaped, &x_clip, &y_clip,
+                          &w_clip, &h_clip);
+
+      has_shape = bounding_shaped != FALSE;
+
+      meta_topic (META_DEBUG_SHAPES,
+                  "Window has_shape = %d extents %d,%d %d x %d\n",
+                  has_shape, x_bounding, y_bounding,
+                  w_bounding, h_bounding);
+    }
+#endif
+
+  /* Get rid of any borders */
+  if (attrs->border_width != 0)
+    XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
+
+  /* Get rid of weird gravities */
+  if (attrs->win_gravity != NorthWestGravity)
+    {
+      XSetWindowAttributes set_attrs;
+      
+      set_attrs.win_gravity = NorthWestGravity;
+      
+      XChangeWindowAttributes (display->xdisplay,
+                               xwindow,
+                               CWWinGravity,
+                               &set_attrs);
+    }
+  
+  if (meta_error_trap_pop_with_return (display, FALSE) != Success)
+    {
+      meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
+                    xwindow);
+      meta_error_trap_pop (display, FALSE);
+      meta_display_ungrab (display);
+      return NULL;
+    }
+
+  g_assert (!attrs->override_redirect);
+  
+  window = g_new (MetaWindow, 1);
+
+  window->dialog_pid = -1;
+  window->dialog_pipe = -1;
+  
+  window->xwindow = xwindow;
+  
+  /* this is in window->screen->display, but that's too annoying to
+   * type
+   */
+  window->display = display;
+  window->workspaces = NULL;
+
+#ifdef HAVE_XSYNC
+  window->update_counter = None;
+#endif
+  
+  window->screen = NULL;
+  tmp = display->screens;
+  while (tmp != NULL)
+    {
+      MetaScreen *scr = tmp->data;
+
+      if (scr->xroot == attrs->root)
+        {
+          window->screen = tmp->data;
+          break;
+        }
+      
+      tmp = tmp->next;
+    }
+  
+  g_assert (window->screen);
+
+  window->desc = g_strdup_printf ("0x%lx", window->xwindow);
+
+  /* avoid tons of stack updates */
+  meta_stack_freeze (window->screen->stack);
+
+  window->has_shape = has_shape;
+  
+  /* Remember this rect is the actual window size */
+  window->rect.x = attrs->x;
+  window->rect.y = attrs->y;
+  window->rect.width = attrs->width;
+  window->rect.height = attrs->height;
+
+  /* And border width, size_hints are the "request" */
+  window->border_width = attrs->border_width;
+  window->size_hints.x = attrs->x;
+  window->size_hints.y = attrs->y;
+  window->size_hints.width = attrs->width;
+  window->size_hints.height = attrs->height;
+  /* initialize the remaining size_hints as if size_hints.flags were zero */
+  meta_set_normal_hints (window, NULL);
+
+  /* And this is our unmaximized size */
+  window->saved_rect = window->rect;
+  window->user_rect = window->rect;
+  
+  window->depth = attrs->depth;
+  window->xvisual = attrs->visual;
+  window->colormap = attrs->colormap;
+  
+  window->title = NULL;
+  window->icon_name = NULL;
+  window->icon = NULL;
+  window->mini_icon = NULL;
+  meta_icon_cache_init (&window->icon_cache);
+  window->wm_hints_pixmap = None;
+  window->wm_hints_mask = None;
+
+  window->frame = NULL;
+  window->has_focus = FALSE;
+
+  window->user_has_move_resized = FALSE;
+  
+  window->maximized = FALSE;
+  window->maximize_after_placement = FALSE;
+  window->fullscreen = FALSE;
+  window->on_all_workspaces = FALSE;
+  window->shaded = FALSE;
+  window->initially_iconic = FALSE;
+  window->minimized = FALSE;
+  window->iconic = FALSE;
+  window->mapped = attrs->map_state != IsUnmapped;
+  /* if already mapped we don't want to do the placement thing */
+  window->placed = window->mapped;
+  if (window->placed)
+    meta_topic (META_DEBUG_PLACEMENT,
+                "Not placing window 0x%lx since it's already mapped\n",
+                xwindow);
+  window->unmanaging = FALSE;
+  window->calc_showing_queued = FALSE;
+  window->move_resize_queued = FALSE;
+  window->keys_grabbed = FALSE;
+  window->grab_on_frame = FALSE;
+  window->all_keys_grabbed = FALSE;
+  window->withdrawn = FALSE;
+  window->initial_workspace_set = FALSE;
+  window->calc_placement = FALSE;
+  window->shaken_loose = FALSE;
+  window->have_focus_click_grab = FALSE;
+  
+  window->unmaps_pending = 0;
+  
+  window->mwm_decorated = TRUE;
+  window->mwm_border_only = FALSE;
+  window->mwm_has_close_func = TRUE;
+  window->mwm_has_minimize_func = TRUE;
+  window->mwm_has_maximize_func = TRUE;
+  window->mwm_has_move_func = TRUE;
+  window->mwm_has_resize_func = TRUE;
+  
+  window->decorated = TRUE;
+  window->has_close_func = TRUE;
+  window->has_minimize_func = TRUE;
+  window->has_maximize_func = TRUE;
+  window->has_move_func = TRUE;
+  window->has_resize_func = TRUE;
+
+  window->has_shade_func = TRUE;
+
+  window->has_fullscreen_func = TRUE;
+
+  window->always_sticky = FALSE;
+  
+  window->wm_state_modal = FALSE;
+  window->skip_taskbar = FALSE;
+  window->skip_pager = FALSE;
+  window->wm_state_skip_taskbar = FALSE;
+  window->wm_state_skip_pager = FALSE;
+  window->wm_state_above = FALSE;
+  window->wm_state_below = FALSE;
+  
+  window->res_class = NULL;
+  window->res_name = NULL;
+  window->role = NULL;
+  window->sm_client_id = NULL;
+  window->wm_client_machine = NULL;
+  window->startup_id = NULL;
+  
+  window->net_wm_pid = -1;
+  
+  window->xtransient_for = None;
+  window->xclient_leader = None;
+  window->transient_parent_is_root_window = FALSE;
+  
+  window->type = META_WINDOW_NORMAL;
+  window->type_atom = None;
+
+  window->struts = NULL;
+
+  window->using_net_wm_name = FALSE;
+  window->using_net_wm_icon_name = FALSE;
+
+  window->need_reread_icon = TRUE;
+  window->update_icon_queued = FALSE;
+  
+  window->layer = META_LAYER_LAST; /* invalid value */
+  window->stack_position = -1;
+  window->initial_workspace = 0; /* not used */
+  
+  meta_display_register_x_window (display, &window->xwindow, window);
+
+
+  /* assign the window to its group, or create a new group if needed
+   */
+  window->group = NULL;
+  window->xgroup_leader = None;
+  meta_window_compute_group (window);
+  
+  /* Fill these in the order we want them to be gotten.
+   * we want to get window name and class first
+   * so we can use them in error messages and such.
+   */
+  i = 0;
+  initial_props[i++] = display->atom_net_wm_name;
+  initial_props[i++] = display->atom_wm_client_machine;
+  initial_props[i++] = display->atom_net_wm_pid;
+  initial_props[i++] = XA_WM_NAME;
+  initial_props[i++] = display->atom_net_wm_icon_name;
+  initial_props[i++] = XA_WM_ICON_NAME;
+  initial_props[i++] = display->atom_net_wm_desktop;
+  initial_props[i++] = display->atom_net_startup_id;
+  initial_props[i++] = display->atom_metacity_update_counter;
+  initial_props[i++] = XA_WM_NORMAL_HINTS;
+  initial_props[i++] = display->atom_wm_protocols;
+  initial_props[i++] = XA_WM_HINTS;
+  g_assert (N_INITIAL_PROPS == i);
+  
+  meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
+
+  update_net_wm_state (window);
+  
+  update_mwm_hints (window);
+  update_wm_class (window);
+  update_transient_for (window);
+  update_sm_hints (window); /* must come after transient_for */
+  update_role (window);
+  update_net_wm_type (window);
+  meta_window_update_icon_now (window);
+
+  if (window->initially_iconic)
+    {
+      /* WM_HINTS said minimized */
+      window->minimized = TRUE;
+      meta_verbose ("Window %s asked to start out minimized\n", window->desc);
+    }
+
+  if (existing_wm_state == IconicState)
+    {
+      /* WM_STATE said minimized */
+      window->minimized = TRUE;
+      meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n",
+                    window->desc);
+
+      /* Assume window was previously placed, though perhaps it's
+       * been iconic its whole life, we have no way of knowing.
+       */
+      window->placed = TRUE;
+    }
+
+  /* Apply any window attributes such as initial workspace
+   * based on startup notification
+   */
+  meta_screen_apply_startup_properties (window->screen, window);
+  
+  /* FIXME we have a tendency to set this then immediately
+   * change it again.
+   */
+  set_wm_state (window, window->iconic ? IconicState : NormalState);
+  set_net_wm_state (window);
+
+  if (window->decorated)
+    meta_window_ensure_frame (window);
+
+  meta_window_grab_keys (window);
+  meta_display_grab_window_buttons (window->display, window->xwindow);
+  meta_display_grab_focus_window_button (window->display, window);
+
+  if (window->type == META_WINDOW_DESKTOP ||
+      window->type == META_WINDOW_DOCK)
+    {
+      /* Change the default, but don't enforce this if the user
+       * focuses the dock/desktop and unsticks it using key shortcuts.
+       * Need to set this before adding to the workspaces so the MRU
+       * lists will be updated.
+       */
+      window->on_all_workspaces = TRUE;
+    }
+  
+  /* For the workspace, first honor hints,
+   * if that fails put transients with parents,
+   * otherwise put window on active space
+   */
+  
+  if (window->initial_workspace_set)
+    {
+      if (window->initial_workspace == (int) 0xFFFFFFFF)
+        {
+          meta_topic (META_DEBUG_PLACEMENT,
+                      "Window %s is initially on all spaces\n",
+                      window->desc);
+          
+	  /* need to set on_all_workspaces first so that it will be
+	   * added to all the MRU lists
+	   */
+          window->on_all_workspaces = TRUE;
+          meta_workspace_add_window (window->screen->active_workspace, window);
+        }
+      else
+        {
+          meta_topic (META_DEBUG_PLACEMENT,
+                      "Window %s is initially on space %d\n",
+                      window->desc, window->initial_workspace);
+
+          space =
+            meta_screen_get_workspace_by_index (window->screen,
+                                                window->initial_workspace);
+          
+          if (space)
+            meta_workspace_add_window (space, window);
+        }
+    }
+  
+  if (window->workspaces == NULL && 
+      window->xtransient_for != None)
+    {
+      /* Try putting dialog on parent's workspace */
+      MetaWindow *parent;
+
+      parent = meta_display_lookup_x_window (window->display,
+                                             window->xtransient_for);
+
+      if (parent)
+        {
+          GList *tmp_list;
+
+          meta_topic (META_DEBUG_PLACEMENT,
+                      "Putting window %s on some workspaces as parent %s\n",
+                      window->desc, parent->desc);
+          
+          if (parent->on_all_workspaces)
+            window->on_all_workspaces = TRUE;
+          
+          tmp_list = parent->workspaces;
+          while (tmp_list != NULL)
+            {
+	      /* this will implicitly add to the appropriate MRU lists
+	       */
+              meta_workspace_add_window (tmp_list->data, window);
+              
+              tmp_list = tmp_list->next;
+            }
+        }
+    }
+  
+  if (window->workspaces == NULL)
+    {
+      meta_topic (META_DEBUG_PLACEMENT,
+                  "Putting window %s on active workspace\n",
+                  window->desc);
+      
+      space = window->screen->active_workspace;
+
+      meta_workspace_add_window (space, window);
+    }
+  
+  /* for the various on_all_workspaces = TRUE possible above */
+  meta_window_set_current_workspace_hint (window);
+  
+  meta_window_update_struts (window);
+
+  /* Put our state back where it should be,
+   * passing TRUE for is_configure_request, ICCCM says
+   * initial map is handled same as configure request
+   */
+  meta_window_move_resize_internal (window,
+                                    META_IS_CONFIGURE_REQUEST,
+                                    NorthWestGravity,
+                                    window->size_hints.x,
+                                    window->size_hints.y,
+                                    window->size_hints.width,
+                                    window->size_hints.height);
+
+  meta_stack_add (window->screen->stack, 
+                  window);
+
+  /* Now try applying saved stuff from the session */
+  {
+    const MetaWindowSessionInfo *info;
+
+    info = meta_window_lookup_saved_state (window);
+
+    if (info)
+      {
+        meta_window_apply_session_info (window, info);
+        meta_window_release_saved_state (info);
+      }
+  }
+  
+  /* Sync stack changes */
+  meta_stack_thaw (window->screen->stack);
+
+  /* disable show desktop mode unless we're a desktop component */
+  maybe_leave_show_desktop_mode (window);
+  
+  meta_window_queue_calc_showing (window);
+
+  meta_error_trap_pop (display, FALSE); /* pop the XSync()-reducing trap */
+  meta_display_ungrab (display);
+  
+  return window;
+}
+
+/* This function should only be called from the end of meta_window_new_with_attrs () */
+static void
+meta_window_apply_session_info (MetaWindow *window,
+                                const MetaWindowSessionInfo *info)
+{
+  if (info->stack_position_set)
+    {
+      meta_topic (META_DEBUG_SM,
+                  "Restoring stack position %d for window %s\n",
+                  info->stack_position, window->desc);
+
+      /* FIXME well, I'm not sure how to do this. */
+    }
+
+  if (info->minimized_set)
+    {
+      meta_topic (META_DEBUG_SM,
+                  "Restoring minimized state %d for window %s\n",
+                  info->minimized, window->desc);
+
+      if (window->has_minimize_func && info->minimized)
+        meta_window_minimize (window);
+    }
+
+  if (info->maximized_set)
+    {
+      meta_topic (META_DEBUG_SM,
+                  "Restoring maximized state %d for window %s\n",
+                  info->maximized, window->desc);
+      
+      if (window->has_maximize_func && info->maximized)
+	{
+	  meta_window_maximize (window);
+
+          if (info->saved_rect_set)
+            {
+              meta_topic (META_DEBUG_SM,
+                          "Restoring saved rect %d,%d %dx%d for window %s\n",
+                          info->saved_rect.x,
+                          info->saved_rect.y,
+                          info->saved_rect.width,
+                          info->saved_rect.height,
+                          window->desc);
+              
+              window->saved_rect.x = info->saved_rect.x;
+              window->saved_rect.y = info->saved_rect.y;
+              window->saved_rect.width = info->saved_rect.width;
+              window->saved_rect.height = info->saved_rect.height;
+            }
+	}
+    }
+  
+  if (info->on_all_workspaces_set)
+    {
+      window->on_all_workspaces = info->on_all_workspaces;
+      meta_topic (META_DEBUG_SM,
+                  "Restoring sticky state %d for window %s\n",
+                  window->on_all_workspaces, window->desc);
+    }
+  
+  if (info->workspace_indices)
+    {
+      GSList *tmp;
+      GSList *spaces;      
+
+      spaces = NULL;
+      
+      tmp = info->workspace_indices;
+      while (tmp != NULL)
+        {
+          MetaWorkspace *space;          
+
+          space =
+            meta_screen_get_workspace_by_index (window->screen,
+                                                GPOINTER_TO_INT (tmp->data));
+          
+          if (space)
+            spaces = g_slist_prepend (spaces, space);
+          
+          tmp = tmp->next;
+        }
+
+      if (spaces)
+        {
+          /* This briefly breaks the invariant that we are supposed
+           * to always be on some workspace. But we paranoically
+           * ensured that one of the workspaces from the session was
+           * indeed valid, so we know we'll go right back to one.
+           */
+          while (window->workspaces)
+            meta_workspace_remove_window (window->workspaces->data, window);
+
+          tmp = spaces;
+          while (tmp != NULL)
+            {
+              MetaWorkspace *space;
+
+              space = tmp->data;
+              
+              meta_workspace_add_window (space, window);              
+
+              meta_topic (META_DEBUG_SM,
+                          "Restoring saved window %s to workspace %d\n",
+                          window->desc,
+                          meta_workspace_index (space));
+              
+              tmp = tmp->next;
+            }
+
+          g_slist_free (spaces);
+        }
+    }
+
+  if (info->geometry_set)
+    {
+      int x, y, w, h;
+      
+      window->placed = TRUE; /* don't do placement algorithms later */
+
+      x = info->rect.x;
+      y = info->rect.y;
+
+      w = window->size_hints.base_width +
+        info->rect.width * window->size_hints.width_inc;
+      h = window->size_hints.base_height +
+        info->rect.height * window->size_hints.height_inc;
+
+      /* Force old gravity, ignoring anything now set */
+      window->size_hints.win_gravity = info->gravity;
+      
+      meta_topic (META_DEBUG_SM,
+                  "Restoring pos %d,%d size %d x %d for %s\n",
+                  x, y, w, h, window->desc);
+      
+      meta_window_move_resize_internal (window,
+                                        META_DO_GRAVITY_ADJUST,
+                                        NorthWestGravity,
+                                        x, y, w, h);
+    }
+}
+
+void
+meta_window_free (MetaWindow  *window)
+{
+  GList *tmp;
+  
+  meta_verbose ("Unmanaging 0x%lx\n", window->xwindow);
+
+  if (window->display->window_with_menu == window)
+    {
+      meta_ui_window_menu_free (window->display->window_menu);
+      window->display->window_menu = NULL;
+      window->display->window_with_menu = NULL;
+    }
+  
+  if (destroying_windows_disallowed > 0)
+    meta_bug ("Tried to destroy window %s while destruction was not allowed\n",
+              window->desc);
+  
+  window->unmanaging = TRUE;
+
+  if (window->fullscreen)
+    {
+      MetaGroup *group;
+
+      /* If the window is fullscreen, it may be forcing
+       * other windows in its group to a higher layer
+       */
+
+      meta_stack_freeze (window->screen->stack);
+      group = meta_window_get_group (window);
+      if (group)
+        meta_group_update_layers (group);
+      meta_stack_thaw (window->screen->stack);
+    }
+
+  meta_window_shutdown_group (window); /* safe to do this early as
+                                        * group.c won't re-add to the
+                                        * group if window->unmanaging
+                                        */
+  
+  /* If we have the focus, focus some other window.
+   * This is done first, so that if the unmap causes
+   * an EnterNotify the EnterNotify will have final say
+   * on what gets focused, maintaining sloppy focus
+   * invariants.
+   */
+  if (window->has_focus)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing top window since we're unmanaging %s\n",
+                  window->desc);
+      meta_workspace_focus_mru_window (window->screen->active_workspace, window);
+    }
+  else if (window->display->expected_focus_window == window)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing top window since expected focus window freed %s\n",
+                  window->desc);
+      window->display->expected_focus_window = NULL;
+      meta_workspace_focus_mru_window (window->screen->active_workspace, window);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Unmanaging window %s which doesn't currently have focus\n",
+                  window->desc);
+    }
+
+  if (window->struts)
+    {
+      g_free (window->struts);
+      window->struts = NULL;
+
+      meta_topic (META_DEBUG_WORKAREA,
+                  "Unmanaging window %s which has struts, so invalidating work areas\n",
+                  window->desc);
+      invalidate_work_areas (window);
+    }
+  
+  if (window->display->grab_window == window)
+    meta_display_end_grab_op (window->display,
+                              meta_display_get_current_time (window->display));
+
+  g_assert (window->display->grab_window != window);
+  
+  if (window->display->focus_window == window)
+    window->display->focus_window = NULL;
+
+  meta_window_unqueue_calc_showing (window);
+  meta_window_unqueue_move_resize (window);
+  meta_window_unqueue_update_icon (window);
+  meta_window_free_delete_dialog (window);
+  
+  tmp = window->workspaces;
+  while (tmp != NULL)
+    {
+      GList *next;
+
+      next = tmp->next;
+
+      /* pops front of list */
+      meta_workspace_remove_window (tmp->data, window);
+
+      tmp = next;
+    }
+
+  g_assert (window->workspaces == NULL);
+
+#ifndef G_DISABLE_CHECKS
+  tmp = window->screen->workspaces;
+  while (tmp != NULL)
+    {
+      MetaWorkspace *workspace = tmp->data;
+
+      g_assert (g_list_find (workspace->windows, window) == NULL);
+      g_assert (g_list_find (workspace->mru_list, window) == NULL);
+
+      tmp = tmp->next;
+    }
+#endif
+  
+  meta_stack_remove (window->screen->stack, window);
+  
+  /* FIXME restore original size if window has maximized */
+  
+  if (window->frame)
+    meta_window_destroy_frame (window);
+  
+  if (window->withdrawn)
+    {
+      /* We need to clean off the window's state so it
+       * won't be restored if the app maps it again.
+       */
+      meta_error_trap_push (window->display);
+      meta_verbose ("Cleaning state from window %s\n", window->desc);
+      XDeleteProperty (window->display->xdisplay,
+                       window->xwindow,
+                       window->display->atom_net_wm_desktop);
+      XDeleteProperty (window->display->xdisplay,
+                       window->xwindow,
+                       window->display->atom_net_wm_state);
+      set_wm_state (window, WithdrawnState);
+      meta_error_trap_pop (window->display, FALSE);
+    }
+  else
+    {
+      /* We need to put WM_STATE so that others will understand it on
+       * restart.
+       */
+      if (!window->minimized)
+        {
+          meta_error_trap_push (window->display);
+          set_wm_state (window, NormalState);
+          meta_error_trap_pop (window->display, FALSE);
+        }
+
+      /* And we need to be sure the window is mapped so other WMs
+       * know that it isn't Withdrawn
+       */
+      meta_error_trap_push (window->display);
+      XMapWindow (window->display->xdisplay,
+                  window->xwindow);
+      meta_error_trap_pop (window->display, FALSE);
+    }
+
+  meta_window_ungrab_keys (window);
+  meta_display_ungrab_window_buttons (window->display, window->xwindow);
+  meta_display_ungrab_focus_window_button (window->display, window);
+  
+  meta_display_unregister_x_window (window->display, window->xwindow);
+  
+
+  meta_error_trap_push (window->display);
+
+  /* Put back anything we messed up */
+  if (window->border_width != 0)
+    XSetWindowBorderWidth (window->display->xdisplay,
+                           window->xwindow,
+                           window->border_width);
+
+  /* No save set */
+  XRemoveFromSaveSet (window->display->xdisplay,
+                      window->xwindow);
+
+  /* Don't get events on not-managed windows */
+  XSelectInput (window->display->xdisplay,
+                window->xwindow,
+                NoEventMask);
+
+#ifdef HAVE_SHAPE
+  if (META_DISPLAY_HAS_SHAPE (window->display))
+    XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
+#endif
+  
+  meta_error_trap_pop (window->display, FALSE);
+
+  if (window->icon)
+    g_object_unref (G_OBJECT (window->icon));
+
+  if (window->mini_icon)
+    g_object_unref (G_OBJECT (window->mini_icon));
+
+  meta_icon_cache_free (&window->icon_cache);
+  
+  g_free (window->sm_client_id);
+  g_free (window->wm_client_machine);
+  g_free (window->startup_id);
+  g_free (window->role);
+  g_free (window->res_class);
+  g_free (window->res_name);
+  g_free (window->title);
+  g_free (window->icon_name);
+  g_free (window->desc);
+  g_free (window);
+}
+
+static void
+set_wm_state (MetaWindow *window,
+              int         state)
+{
+  unsigned long data[2];
+  
+  meta_verbose ("Setting wm state %s on %s\n",
+                wm_state_to_string (state), window->desc);
+  
+  /* Metacity doesn't use icon windows, so data[1] should be None
+   * according to the ICCCM 2.0 Section 4.1.3.1.
+   */
+  data[0] = state;
+  data[1] = None;
+
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_wm_state,
+                   window->display->atom_wm_state,
+                   32, PropModeReplace, (guchar*) data, 2);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+static void
+set_net_wm_state (MetaWindow *window)
+{
+  int i;
+  unsigned long data[10];
+  
+  i = 0;
+  if (window->shaded)
+    {
+      data[i] = window->display->atom_net_wm_state_shaded;
+      ++i;
+    }
+  if (window->wm_state_modal)
+    {
+      data[i] = window->display->atom_net_wm_state_modal;
+      ++i;
+    }
+  if (window->skip_pager)
+    {
+      data[i] = window->display->atom_net_wm_state_skip_pager;
+      ++i;
+    }
+  if (window->skip_taskbar)
+    {
+      data[i] = window->display->atom_net_wm_state_skip_taskbar;
+      ++i;
+    }
+  if (window->maximized)
+    {
+      data[i] = window->display->atom_net_wm_state_maximized_horz;
+      ++i;
+      data[i] = window->display->atom_net_wm_state_maximized_vert;
+      ++i;
+    }
+  if (window->fullscreen)
+    {
+      data[i] = window->display->atom_net_wm_state_fullscreen;
+      ++i;
+    }
+  if (window->shaded || window->minimized)
+    {
+      data[i] = window->display->atom_net_wm_state_hidden;
+      ++i;
+    }
+  if (window->wm_state_above)
+    {
+      data[i] = window->display->atom_net_wm_state_above;
+      ++i;
+    }
+  if (window->wm_state_below)
+    {
+      data[i] = window->display->atom_net_wm_state_below;
+      ++i;
+    }
+  
+  meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i);
+  
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_wm_state,
+                   XA_ATOM,
+                   32, PropModeReplace, (guchar*) data, i);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+/* FIXME rename this, it makes it sound like map state is relevant */
+gboolean
+meta_window_visible_on_workspace (MetaWindow    *window,
+                                  MetaWorkspace *workspace)
+{
+  return (window->on_all_workspaces && window->screen == workspace->screen) ||
+    meta_workspace_contains_window (workspace, window);
+}
+
+static gboolean
+is_minimized_foreach (MetaWindow *window,
+                      void       *data)
+{
+  gboolean *result = data;
+
+  *result = window->minimized;
+  if (*result)
+    return FALSE; /* stop as soon as we find one */
+  else
+    return TRUE;
+}
+
+static gboolean
+ancestor_is_minimized (MetaWindow *window)
+{
+  gboolean is_minimized;
+
+  is_minimized = FALSE;
+
+  meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized);
+
+  return is_minimized;
+}
+
+static gboolean
+window_should_be_showing (MetaWindow  *window)
+{
+  gboolean showing, on_workspace;
+  gboolean is_desktop_or_dock;
+
+  meta_verbose ("Should be showing for window %s\n", window->desc);
+
+  /* 1. See if we're on the workspace */
+  
+  on_workspace = meta_window_visible_on_workspace (window, 
+                                                   window->screen->active_workspace);
+
+  showing = on_workspace;
+  
+  if (!on_workspace)
+    meta_verbose ("Window %s is not on workspace %d\n",
+                  window->desc,
+                  meta_workspace_index (window->screen->active_workspace));
+  else
+    meta_verbose ("Window %s is on the active workspace %d\n",
+                  window->desc,
+                  meta_workspace_index (window->screen->active_workspace));
+
+  if (window->on_all_workspaces)
+    meta_verbose ("Window %s is on all workspaces\n", window->desc);
+
+  /* 2. See if we're minimized */
+  if (window->minimized)
+    showing = FALSE;
+  
+  /* 3. See if we're in "show desktop" mode */
+  is_desktop_or_dock = FALSE;
+  is_desktop_or_dock_foreach (window,
+                              &is_desktop_or_dock);
+
+  meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach,
+                                &is_desktop_or_dock);
+
+  if (showing &&      
+      window->screen->showing_desktop &&
+      !is_desktop_or_dock)
+    {
+      meta_verbose ("Window %s is on current workspace, but we're showing the desktop\n",
+                    window->desc);
+      showing = FALSE;
+    }
+
+  /* 4. See if an ancestor is minimized (note that
+   *    ancestor's "mapped" field may not be up to date
+   *    since it's being computed in this same idle queue)
+   */
+  
+  if (showing)
+    {
+      if (ancestor_is_minimized (window))
+        showing = FALSE;
+    }
+
+#if 0
+  /* 5. See if we're drawing wireframe
+   */
+  if (window->display->grab_window == window &&
+      window->display->grab_wireframe_active)    
+    showing = FALSE;
+#endif
+  
+  return showing;
+}
+
+static void
+implement_showing (MetaWindow *window,
+                   gboolean    showing)
+{
+  /* Actually show/hide the window */
+  meta_verbose ("Implement showing = %d for window %s\n",
+                showing, window->desc);
+  
+  if (!showing)
+    {
+      gboolean on_workspace;
+
+      on_workspace = meta_window_visible_on_workspace (window, 
+                                                       window->screen->active_workspace);
+  
+      /* Really this effects code should probably
+       * be in meta_window_hide so the window->mapped
+       * test isn't duplicated here. Anyhow, we animate
+       * if we are mapped now, we are supposed to
+       * be minimized, and we are on the current workspace.
+       */
+      if (on_workspace && window->minimized && window->mapped &&
+          !meta_prefs_get_reduced_resources ())
+        {
+	  MetaRectangle icon_rect, window_rect;
+	  gboolean result;
+	  
+	  /* Check if the window has an icon geometry */
+	  result = meta_window_get_icon_geometry (window, &icon_rect);
+          
+          if (!result)
+            {
+              /* just animate into the corner somehow - maybe
+               * not a good idea...
+               */              
+              icon_rect.x = window->screen->width;
+              icon_rect.y = window->screen->height;
+              icon_rect.width = 1;
+              icon_rect.height = 1;
+            }
+
+          meta_window_get_outer_rect (window, &window_rect);
+          
+          /* Draw a nice cool animation */
+          meta_effects_draw_box_animation (window->screen,
+                                           &window_rect,
+                                           &icon_rect,
+                                           META_MINIMIZE_ANIMATION_LENGTH,
+                                           META_BOX_ANIM_SCALE);
+	}
+
+      meta_window_hide (window);
+    }
+  else
+    {
+      meta_window_show (window);
+    }
+}
+
+void
+meta_window_calc_showing (MetaWindow  *window)
+{
+  implement_showing (window, window_should_be_showing (window));
+}
+
+static guint calc_showing_idle = 0;
+static GSList *calc_showing_pending = NULL;
+
+static int
+stackcmp (gconstpointer a, gconstpointer b)
+{
+  MetaWindow *aw = (gpointer) a;
+  MetaWindow *bw = (gpointer) b;
+
+  if (aw->screen != bw->screen)
+    return 0; /* don't care how they sort with respect to each other */
+  else
+    return meta_stack_windows_cmp (aw->screen->stack,
+                                   aw, bw);
+}
+
+static gboolean
+idle_calc_showing (gpointer data)
+{
+  GSList *tmp;
+  GSList *copy;
+  GSList *should_show;
+  GSList *should_hide;
+  GSList *unplaced;
+  GSList *displays;
+  
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Clearing the calc_showing queue\n");
+
+  /* Work with a copy, for reentrancy. The allowed reentrancy isn't
+   * complete; destroying a window while we're in here would result in
+   * badness. But it's OK to queue/unqueue calc_showings.
+   */
+  copy = g_slist_copy (calc_showing_pending);
+  g_slist_free (calc_showing_pending);
+  calc_showing_pending = NULL;
+  calc_showing_idle = 0;
+
+  destroying_windows_disallowed += 1;
+  
+  /* We map windows from top to bottom and unmap from bottom to
+   * top, to avoid extra expose events. The exception is
+   * for unplaced windows, which have to be mapped from bottom to
+   * top so placement works.
+   */
+  should_show = NULL;
+  should_hide = NULL;
+  unplaced = NULL;
+  displays = NULL;
+
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      if (!window->placed)
+        unplaced = g_slist_prepend (unplaced, window);
+      else if (window_should_be_showing (window))
+        should_show = g_slist_prepend (should_show, window);
+      else
+        should_hide = g_slist_prepend (should_hide, window);
+      
+      tmp = tmp->next;
+    }
+
+  /* bottom to top */
+  unplaced = g_slist_sort (unplaced, stackcmp);
+  should_hide = g_slist_sort (should_hide, stackcmp);
+  /* top to bottom */
+  should_show = g_slist_sort (should_show, stackcmp);
+  should_show = g_slist_reverse (should_show);
+  
+  tmp = unplaced;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      meta_window_calc_showing (window);
+      
+      tmp = tmp->next;
+    }
+
+  tmp = should_hide;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      implement_showing (window, FALSE);
+      
+      tmp = tmp->next;
+    }
+  
+  tmp = should_show;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      implement_showing (window, TRUE);
+      
+      tmp = tmp->next;
+    }
+  
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      /* important to set this here for reentrancy -
+       * if we queue a window again while it's in "copy",
+       * then queue_calc_showing will just return since
+       * calc_showing_queued = TRUE still
+       */
+      window->calc_showing_queued = FALSE;
+      
+      tmp = tmp->next;
+    }
+
+  /* for all displays used in the queue, set a sentinel property on
+   * the root window so that we can ignore EnterNotify events that
+   * occur before the window maps occur.  This avoids a race
+   * condition. */
+  tmp = should_show;
+  while (tmp != NULL)
+    {
+      MetaWindow *window = tmp->data;
+      
+      if (g_slist_find (displays, window->display) == NULL)
+        {
+          displays = g_slist_prepend (displays, window->display);
+          meta_display_increment_focus_sentinel (window->display);
+        }
+
+      tmp = tmp->next;
+    }
+
+  g_slist_free (copy);
+
+  g_slist_free (unplaced);
+  g_slist_free (should_show);
+  g_slist_free (should_hide);
+  g_slist_free (displays);
+  
+  destroying_windows_disallowed -= 1;
+  
+  return FALSE;
+}
+
+void
+meta_window_unqueue_calc_showing (MetaWindow *window)
+{
+  if (!window->calc_showing_queued)
+    return;
+
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Removing %s from the calc_showing queue\n",
+              window->desc);
+
+  /* Note that window may not actually be in move_resize_pending
+   * because it may have been in "copy" inside the idle handler
+   */  
+  calc_showing_pending = g_slist_remove (calc_showing_pending, window);
+  window->calc_showing_queued = FALSE;
+  
+  if (calc_showing_pending == NULL &&
+      calc_showing_idle != 0)
+    {
+      g_source_remove (calc_showing_idle);
+      calc_showing_idle = 0;
+    }
+}
+
+void
+meta_window_flush_calc_showing (MetaWindow *window)
+{
+  if (window->calc_showing_queued)
+    {
+      meta_window_unqueue_calc_showing (window);
+      meta_window_calc_showing (window);
+    }
+}
+
+void
+meta_window_queue_calc_showing (MetaWindow  *window)
+{
+  /* if withdrawn = TRUE then unmanaging should also be TRUE,
+   * really.
+   */
+  if (window->unmanaging || window->withdrawn)
+    return;
+
+  if (window->calc_showing_queued)
+    return;
+
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Putting %s in the calc_showing queue\n",
+              window->desc);
+  
+  window->calc_showing_queued = TRUE;
+  
+  if (calc_showing_idle == 0)
+    calc_showing_idle = g_idle_add (idle_calc_showing, NULL);
+
+  calc_showing_pending = g_slist_prepend (calc_showing_pending, window);
+}
+
+static gboolean
+window_takes_focus_on_map (MetaWindow *window)
+{
+  /* don't initially focus windows that are intended to not accept
+   * focus
+   */
+  if (!(window->input || window->take_focus))
+    return FALSE;
+
+  switch (window->type)
+    {
+    case META_WINDOW_DOCK:
+    case META_WINDOW_DESKTOP:
+    case META_WINDOW_UTILITY:
+    case META_WINDOW_SPLASHSCREEN:
+    case META_WINDOW_TOOLBAR:
+    case META_WINDOW_MENU:
+      /* don't focus these */
+      break;
+    case META_WINDOW_NORMAL:
+
+      /* Always focus new windows */
+      return TRUE;
+
+      /* Old Windows-XP style rule for reference */
+      /* Focus only if the current focus is on a desktop element
+       * or nonexistent.
+       *
+       * (using display->focus_window is a bit of a race condition,
+       *  but I have no idea how to avoid it)
+       */
+      if (window->display->focus_window == NULL ||
+          (window->display->focus_window &&
+           (window->display->focus_window->type == META_WINDOW_DOCK ||
+            window->display->focus_window->type == META_WINDOW_DESKTOP)))
+        return TRUE;
+      break;
+    case META_WINDOW_DIALOG:
+    case META_WINDOW_MODAL_DIALOG:
+
+      /* Always focus */
+      return TRUE;
+
+      /* Old Windows-XP style rule for reference */
+      /* Focus only if the transient parent has focus */
+      /* (using display->focus_window is a bit of a race condition,
+       *  but I have no idea how to avoid it)
+       */
+      if (window->display->focus_window == NULL ||
+          (window->display->focus_window &&
+           meta_window_is_ancestor_of_transient (window->display->focus_window,
+                                                 window)) ||
+          (window->display->focus_window->type == META_WINDOW_DOCK ||
+           window->display->focus_window->type == META_WINDOW_DESKTOP))
+        return TRUE;
+      break;
+    }
+
+  return FALSE;
+}
+
+void
+meta_window_show (MetaWindow *window)
+{
+  gboolean did_placement;
+  gboolean did_show;
+  
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Showing window %s, shaded: %d iconic: %d placed: %d\n",
+              window->desc, window->shaded, window->iconic, window->placed);
+
+  did_show = FALSE;
+  did_placement = FALSE;
+  if (!window->placed)
+    {
+      /* We have to recalc the placement here since other windows may
+       * have been mapped/placed since we last did constrain_position
+       */
+
+      /* calc_placement is an efficiency hack to avoid
+       * multiple placement calculations before we finally
+       * show the window.
+       */
+      window->calc_placement = TRUE;
+      meta_window_move_resize_now (window);
+      window->calc_placement = FALSE;
+
+      /* don't ever do the initial position constraint thing again.
+       * This is toggled here so that initially-iconified windows
+       * still get placed when they are ultimately shown.
+       */
+      window->placed = TRUE;
+      did_placement = TRUE;
+    }
+  
+  /* Shaded means the frame is mapped but the window is not */
+  
+  if (window->frame && !window->frame->mapped)
+    {
+      meta_topic (META_DEBUG_WINDOW_STATE,
+                  "Frame actually needs map\n");
+      window->frame->mapped = TRUE;
+      meta_ui_map_frame (window->screen->ui, window->frame->xwindow);
+      did_show = TRUE;
+    }
+
+  if (window->shaded)
+    {
+      if (window->mapped)
+        {          
+          meta_topic (META_DEBUG_WINDOW_STATE,
+                      "%s actually needs unmap (shaded)\n", window->desc);
+          meta_topic (META_DEBUG_WINDOW_STATE,
+                      "Incrementing unmaps_pending on %s for shade\n",
+                      window->desc);
+          window->mapped = FALSE;
+          window->unmaps_pending += 1;
+          meta_error_trap_push (window->display);
+          XUnmapWindow (window->display->xdisplay, window->xwindow);
+          meta_error_trap_pop (window->display, FALSE);
+        }
+
+      if (!window->iconic)
+        {
+          window->iconic = TRUE;
+          set_wm_state (window, IconicState);
+        }
+    }
+  else
+    {
+      if (!window->mapped)
+        {
+          meta_topic (META_DEBUG_WINDOW_STATE,
+                      "%s actually needs map\n", window->desc);
+          window->mapped = TRUE;
+          meta_error_trap_push (window->display);
+          XMapWindow (window->display->xdisplay, window->xwindow);
+          meta_error_trap_pop (window->display, FALSE);
+          did_show = TRUE;
+        }      
+      
+      if (window->iconic)
+        {
+          window->iconic = FALSE;
+          set_wm_state (window, NormalState);
+        }
+    }
+
+  if (did_placement)
+    {
+      if (window->xtransient_for != None)
+        {
+          MetaWindow *parent;
+
+          parent =
+            meta_display_lookup_x_window (window->display,
+                                          window->xtransient_for);
+          
+          if (parent && parent->has_focus &&
+              (window->input || window->take_focus))
+            {
+              meta_topic (META_DEBUG_FOCUS,
+                          "Focusing transient window '%s' since parent had focus\n",
+                          window->desc);
+              meta_window_focus (window,
+                                 meta_display_get_current_time (window->display));
+            }
+        }
+
+      if (window_takes_focus_on_map (window))
+        {                
+          meta_window_focus (window,
+                             meta_display_get_current_time (window->display));
+        }
+    }
+
+  if (did_show)
+    {
+      set_net_wm_state (window);
+      
+      if (window->struts)
+        {
+          meta_topic (META_DEBUG_WORKAREA,
+                      "Mapped window %s with struts, so invalidating work areas\n",
+                      window->desc);
+          invalidate_work_areas (window);
+        }
+    }
+}
+
+void
+meta_window_hide (MetaWindow *window)
+{
+  gboolean did_hide;
+  
+  meta_topic (META_DEBUG_WINDOW_STATE,
+              "Hiding window %s\n", window->desc);
+
+  did_hide = FALSE;
+  
+  if (window->frame && window->frame->mapped)
+    {
+      meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n");
+      window->frame->mapped = FALSE;
+      meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
+      did_hide = TRUE;
+    }
+
+  if (window->mapped)
+    {
+      meta_topic (META_DEBUG_WINDOW_STATE,
+                  "%s actually needs unmap\n", window->desc);
+      meta_topic (META_DEBUG_WINDOW_STATE,
+                  "Incrementing unmaps_pending on %s for hide\n",
+                  window->desc);
+      window->mapped = FALSE;
+      window->unmaps_pending += 1;
+      meta_error_trap_push (window->display);      
+      XUnmapWindow (window->display->xdisplay, window->xwindow);
+      meta_error_trap_pop (window->display, FALSE);
+      did_hide = TRUE;
+    }
+
+  if (!window->iconic)
+    {
+      window->iconic = TRUE;
+      set_wm_state (window, IconicState);
+    }
+  
+  if (did_hide)
+    {
+      set_net_wm_state (window);
+      
+      if (window->struts)
+        {
+          meta_topic (META_DEBUG_WORKAREA,
+                      "Unmapped window %s with struts, so invalidating work areas\n",
+                      window->desc);
+          invalidate_work_areas (window);
+        }
+    }
+}
+
+static gboolean
+queue_calc_showing_func (MetaWindow *window,
+                         void       *data)
+{
+  meta_window_queue_calc_showing (window);
+  return TRUE;
+}
+
+void
+meta_window_minimize (MetaWindow  *window)
+{
+  if (!window->minimized)
+    {
+      window->minimized = TRUE;
+      meta_window_queue_calc_showing (window);
+
+      meta_window_foreach_transient (window,
+                                     queue_calc_showing_func,
+                                     NULL);
+      
+      if (window->has_focus)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Focusing top window due to minimization of focus window %s\n",
+                      window->desc);
+          meta_workspace_focus_mru_window (window->screen->active_workspace, window);
+        }
+      else
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Minimizing window %s which doesn't have the focus\n",
+                      window->desc);
+        }
+    }
+}
+
+void
+meta_window_unminimize (MetaWindow  *window)
+{
+  if (window->minimized)
+    {
+      window->minimized = FALSE;
+      meta_window_queue_calc_showing (window);
+
+      meta_window_foreach_transient (window,
+                                     queue_calc_showing_func,
+                                     NULL);
+    }
+}
+
+static void
+meta_window_save_rect (MetaWindow *window)
+{
+  if (!(window->maximized || window->fullscreen))
+    {
+      /* save size/pos as appropriate args for move_resize */
+      window->saved_rect = window->rect;
+      if (window->frame)
+        {
+          window->saved_rect.x += window->frame->rect.x;
+          window->saved_rect.y += window->frame->rect.y;
+        }
+    }
+}
+
+void
+meta_window_maximize_internal (MetaWindow    *window,
+                               MetaRectangle *saved_rect)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Maximizing %s\n", window->desc);
+  
+  if (saved_rect != NULL)
+    window->saved_rect = *saved_rect;
+  else
+    meta_window_save_rect (window);
+  
+  window->maximized = TRUE;
+  
+  recalc_window_features (window);
+  set_net_wm_state (window);
+}
+
+void
+meta_window_maximize (MetaWindow  *window)
+{
+  if (!window->maximized)
+    {
+      if (window->shaded)
+        meta_window_unshade (window);
+      
+      /* if the window hasn't been placed yet, we'll maximize it then
+       */
+      if (!window->placed)
+	{
+	  window->maximize_after_placement = TRUE;
+	  return;
+	}
+
+      meta_window_maximize_internal (window, NULL);
+
+      /* move_resize with new maximization constraints
+       */
+      meta_window_queue_move_resize (window);
+    }
+}
+
+void
+meta_window_unmaximize (MetaWindow  *window)
+{
+  if (window->maximized)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Unmaximizing %s\n", window->desc);
+      
+      window->maximized = FALSE;
+
+      /* When we unmaximize, if we're doing a mouse move also we could
+       * get the window suddenly jumping to the upper left corner of
+       * the workspace, since that's where it was when the grab op
+       * started.  So we need to update the grab state.
+       */
+      if (meta_grab_op_is_moving (window->display->grab_op) &&
+          window->display->grab_window == window)
+        {
+          window->display->grab_anchor_window_pos = window->saved_rect;
+        }
+
+      meta_window_move_resize (window,
+                               TRUE,
+                               window->saved_rect.x,
+                               window->saved_rect.y,
+                               window->saved_rect.width,
+                               window->saved_rect.height);
+
+      recalc_window_features (window);
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_make_above (MetaWindow  *window)
+{
+  window->wm_state_above = TRUE;
+  meta_window_update_layer (window);
+  meta_window_raise (window);
+  set_net_wm_state (window);
+}
+
+void
+meta_window_unmake_above (MetaWindow  *window)
+{
+  window->wm_state_above = FALSE;
+  meta_window_raise (window);
+  meta_window_update_layer (window);
+  set_net_wm_state (window);
+}
+
+void
+meta_window_make_fullscreen (MetaWindow  *window)
+{
+  if (!window->fullscreen)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Fullscreening %s\n", window->desc);
+
+      if (window->shaded)
+        meta_window_unshade (window);
+
+      meta_window_save_rect (window);
+      
+      window->fullscreen = TRUE;
+
+      meta_stack_freeze (window->screen->stack);
+      meta_window_update_layer (window);
+      
+      meta_window_raise (window);
+      meta_stack_thaw (window->screen->stack);
+      
+      /* move_resize with new constraints
+       */
+      meta_window_queue_move_resize (window);
+
+      recalc_window_features (window);
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_unmake_fullscreen (MetaWindow  *window)
+{
+  if (window->fullscreen)
+    {
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Unfullscreening %s\n", window->desc);
+      
+      window->fullscreen = FALSE;
+
+      meta_window_update_layer (window);
+      
+      meta_window_move_resize (window,
+                               TRUE,
+                               window->saved_rect.x,
+                               window->saved_rect.y,
+                               window->saved_rect.width,
+                               window->saved_rect.height);
+
+      recalc_window_features (window);
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_shade (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Shading %s\n", window->desc);
+  if (!window->shaded)
+    {
+#if 0
+      if (window->mapped)
+        {
+          /* Animation */
+          MetaRectangle starting_size;
+          MetaRectangle titlebar_size;
+          
+          meta_window_get_outer_rect (window, &starting_size);
+          if (window->frame)
+            {
+              starting_size.y += window->frame->child_y;
+              starting_size.height -= window->frame->child_y;
+            }
+          titlebar_size = starting_size;
+          titlebar_size.height = 0;
+          
+          meta_effects_draw_box_animation (window->screen,
+                                           &starting_size,
+                                           &titlebar_size,
+                                           META_SHADE_ANIMATION_LENGTH,
+                                           META_BOX_ANIM_SLIDE_UP);
+        }
+#endif
+      
+      window->shaded = TRUE;
+
+      meta_window_queue_move_resize (window);
+      meta_window_queue_calc_showing (window);
+
+      /* After queuing the calc showing, since _focus flushes it,
+       * and we need to focus the frame
+       */
+      meta_topic (META_DEBUG_FOCUS,
+                  "Re-focusing window %s after shading it\n",
+                  window->desc);
+      meta_window_focus (window,
+                         meta_display_get_current_time (window->display));
+      
+      set_net_wm_state (window);
+    }
+}
+
+void
+meta_window_unshade (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Unshading %s\n", window->desc);
+  if (window->shaded)
+    {
+      window->shaded = FALSE;
+      meta_window_queue_move_resize (window);
+      meta_window_queue_calc_showing (window);
+
+      /* focus the window */
+      meta_topic (META_DEBUG_FOCUS,
+                  "Focusing window %s after unshading it\n",
+                  window->desc);
+      meta_window_focus (window, meta_display_get_current_time (window->display));
+
+      set_net_wm_state (window);
+    }
+}
+
+static gboolean
+unminimize_func (MetaWindow *window,
+                 void       *data)
+{
+  meta_window_unminimize (window);
+  return TRUE;
+}
+
+static void
+unminimize_window_and_all_transient_parents (MetaWindow *window)
+{
+  meta_window_unminimize (window);
+  meta_window_foreach_ancestor (window, unminimize_func, NULL);
+}
+
+void
+meta_window_activate (MetaWindow *window,
+                      guint32     timestamp)
+{
+  /* disable show desktop mode unless we're a desktop component */
+  maybe_leave_show_desktop_mode (window);
+ 
+  /* Get window on current workspace */
+  if (!meta_window_visible_on_workspace (window,
+                                         window->screen->active_workspace))
+    meta_window_change_workspace (window,
+                                  window->screen->active_workspace);
+  
+  if (window->shaded)
+    meta_window_unshade (window);
+
+  unminimize_window_and_all_transient_parents (window);
+  
+  meta_window_raise (window);
+  meta_topic (META_DEBUG_FOCUS,
+              "Focusing window %s due to activation\n",
+              window->desc);
+  meta_window_focus (window, timestamp);
+}
+
+/* returns values suitable for meta_window_move
+ * i.e. static gravity
+ */
+static void
+adjust_for_gravity (MetaWindow        *window,
+                    MetaFrameGeometry *fgeom,
+                    gboolean           coords_assume_border,
+                    int                x,
+                    int                y,
+                    int                width,
+                    int                height,
+                    int               *xp,
+                    int               *yp)
+{
+  int ref_x, ref_y;
+  int bw;
+  int child_x, child_y;
+  int frame_width, frame_height;
+  
+  if (coords_assume_border)
+    bw = window->border_width;
+  else
+    bw = 0;
+
+  if (fgeom)
+    {
+      child_x = fgeom->left_width;
+      child_y = fgeom->top_height;
+      frame_width = child_x + width + fgeom->right_width;
+      frame_height = child_y + height + fgeom->bottom_height;
+    }
+  else
+    {
+      child_x = 0;
+      child_y = 0;
+      frame_width = width;
+      frame_height = height;
+    }
+  
+  /* We're computing position to pass to window_move, which is
+   * the position of the client window (StaticGravity basically)
+   *
+   * (see WM spec description of gravity computation, but note that
+   * their formulas assume we're honoring the border width, rather
+   * than compensating for having turned it off)
+   */
+  switch (window->size_hints.win_gravity)
+    {
+    case NorthWestGravity:
+      ref_x = x;
+      ref_y = y;
+      break;
+    case NorthGravity:
+      ref_x = x + width / 2 + bw;
+      ref_y = y;
+      break;
+    case NorthEastGravity:
+      ref_x = x + width + bw * 2;
+      ref_y = y;
+      break;
+    case WestGravity:
+      ref_x = x;
+      ref_y = y + height / 2 + bw;
+      break;
+    case CenterGravity:
+      ref_x = x + width / 2 + bw;
+      ref_y = y + height / 2 + bw;
+      break;
+    case EastGravity:
+      ref_x = x + width + bw * 2;
+      ref_y = y + height / 2 + bw;
+      break;
+    case SouthWestGravity:
+      ref_x = x;
+      ref_y = y + height + bw * 2;
+      break;
+    case SouthGravity:
+      ref_x = x + width / 2 + bw;
+      ref_y = y + height + bw * 2;
+      break;
+    case SouthEastGravity:
+      ref_x = x + width + bw * 2;
+      ref_y = y + height + bw * 2;
+      break;
+    case StaticGravity:
+    default:
+      ref_x = x;
+      ref_y = y;
+      break;
+    }
+
+  switch (window->size_hints.win_gravity)
+    {
+    case NorthWestGravity:
+      *xp = ref_x + child_x;
+      *yp = ref_y + child_y;
+      break;
+    case NorthGravity:
+      *xp = ref_x - frame_width / 2 + child_x;
+      *yp = ref_y + child_y;
+      break;
+    case NorthEastGravity:
+      *xp = ref_x - frame_width + child_x;
+      *yp = ref_y + child_y;
+      break;
+    case WestGravity:
+      *xp = ref_x + child_x;
+      *yp = ref_y - frame_height / 2 + child_y;
+      break;
+    case CenterGravity:
+      *xp = ref_x - frame_width / 2 + child_x;
+      *yp = ref_y - frame_height / 2 + child_y;
+      break;
+    case EastGravity:
+      *xp = ref_x - frame_width + child_x;
+      *yp = ref_y - frame_height / 2 + child_y;
+      break;
+    case SouthWestGravity:
+      *xp = ref_x + child_x;
+      *yp = ref_y - frame_height + child_y;
+      break;
+    case SouthGravity:
+      *xp = ref_x - frame_width / 2 + child_x;
+      *yp = ref_y - frame_height + child_y;
+      break;
+    case SouthEastGravity:
+      *xp = ref_x - frame_width + child_x;
+      *yp = ref_y - frame_height + child_y;
+      break;
+    case StaticGravity:
+    default:
+      *xp = ref_x;
+      *yp = ref_y;
+      break;
+    }
+}
+
+static gboolean
+static_gravity_works (MetaDisplay *display)
+{
+  return display->static_gravity_works;
+}
+
+static void
+get_mouse_deltas_for_resize (MetaWindow *window,
+                             int         resize_gravity,
+                             int         w,
+                             int         h,
+                             int        *x_delta,
+                             int        *y_delta)
+{
+  switch (meta_x_direction_from_gravity (resize_gravity))
+    {
+    case META_RESIZE_LEFT_OR_TOP:
+      *x_delta = window->rect.width - w;
+      break;
+    case META_RESIZE_RIGHT_OR_BOTTOM:
+      *x_delta = w - window->rect.width;
+      break;
+    case META_RESIZE_CENTER:
+      /* FIXME this implies that with center gravity you have to grow
+       * in increments of two
+       */
+      *x_delta = (w - window->rect.width) / 2;
+      break;
+    }
+  
+  switch (meta_y_direction_from_gravity (resize_gravity))
+    {
+    case META_RESIZE_LEFT_OR_TOP:
+      *y_delta = window->rect.height - h;
+      break;
+    case META_RESIZE_RIGHT_OR_BOTTOM:
+      *y_delta = h - window->rect.height;
+      break;
+    case META_RESIZE_CENTER:
+      /* FIXME this implies that with center gravity you have to grow
+       * in increments of two
+       */
+      *y_delta = (h - window->rect.height) / 2;
+      break;
+    }
+}
+
+static void
+meta_window_move_resize_internal (MetaWindow  *window,
+                                  MetaMoveResizeFlags flags,
+                                  int          resize_gravity,
+                                  int          root_x_nw,
+                                  int          root_y_nw,
+                                  int          w,
+                                  int          h)
+{
+  XWindowChanges values;
+  unsigned int mask;
+  gboolean need_configure_notify;
+  MetaFrameGeometry fgeom;
+  gboolean need_move_client = FALSE;
+  gboolean need_move_frame = FALSE;
+  gboolean need_resize_client = FALSE;
+  gboolean need_resize_frame = FALSE;
+  int frame_size_dx;
+  int frame_size_dy;
+  int size_dx;
+  int size_dy;
+  gboolean is_configure_request;
+  gboolean do_gravity_adjust;
+  gboolean is_user_action;
+  gboolean configure_frame_first;
+  gboolean use_static_gravity;
+  /* used for the configure request, but may not be final
+   * destination due to StaticGravity etc.
+   */
+  int client_move_x;
+  int client_move_y;
+  int x_delta;
+  int y_delta;
+  MetaRectangle new_rect;
+  MetaRectangle old_rect;
+  
+  is_configure_request = (flags & META_IS_CONFIGURE_REQUEST) != 0;
+  do_gravity_adjust = (flags & META_DO_GRAVITY_ADJUST) != 0;
+  is_user_action = (flags & META_USER_MOVE_RESIZE) != 0;
+  
+  /* We don't need it in the idle queue anymore. */
+  meta_window_unqueue_move_resize (window);
+
+  old_rect = window->rect;
+  meta_window_get_position (window, &old_rect.x, &old_rect.y);
+  
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Move/resize %s to %d,%d %dx%d%s%s from %d,%d %dx%d\n",
+              window->desc, root_x_nw, root_y_nw, w, h,
+              is_configure_request ? " (configure request)" : "",
+              is_user_action ? " (user move/resize)" : "",
+              old_rect.x, old_rect.y, old_rect.width, old_rect.height);
+  
+  if (window->frame)
+    meta_frame_calc_geometry (window->frame,
+                              &fgeom);
+  
+  if (is_configure_request || do_gravity_adjust)
+    {      
+      adjust_for_gravity (window,
+                          window->frame ? &fgeom : NULL,
+                          /* configure request coords assume
+                           * the border width existed
+                           */
+                          is_configure_request,
+                          root_x_nw,
+                          root_y_nw,
+                          w, h,
+                          &root_x_nw,
+                          &root_y_nw);
+      
+      meta_topic (META_DEBUG_GEOMETRY,
+                  "Compensated position for gravity, new pos %d,%d\n",
+                  root_x_nw, root_y_nw);
+    }
+
+  get_mouse_deltas_for_resize (window, resize_gravity, w, h,
+                               &x_delta, &y_delta);
+  
+  meta_window_constrain (window,
+                         window->frame ? &fgeom : NULL,
+                         &old_rect,
+                         root_x_nw - old_rect.x,
+                         root_y_nw - old_rect.y,
+                         meta_x_direction_from_gravity (resize_gravity),
+                         x_delta,
+                         meta_y_direction_from_gravity (resize_gravity),
+                         y_delta,
+                         &new_rect);
+
+  w = new_rect.width;
+  h = new_rect.height;
+  root_x_nw = new_rect.x;
+  root_y_nw = new_rect.y;
+  
+  if (w != window->rect.width ||
+      h != window->rect.height)
+    need_resize_client = TRUE;  
+  
+  window->rect.width = w;
+  window->rect.height = h;
+  
+  if (window->frame)
+    {
+      int new_w, new_h;
+
+      new_w = window->rect.width + fgeom.left_width + fgeom.right_width;
+
+      if (window->shaded)
+        new_h = fgeom.top_height;
+      else
+        new_h = window->rect.height + fgeom.top_height + fgeom.bottom_height;
+
+      frame_size_dx = new_w - window->frame->rect.width;
+      frame_size_dy = new_h - window->frame->rect.height;
+
+      need_resize_frame = (frame_size_dx != 0 || frame_size_dy != 0);
+      
+      window->frame->rect.width = new_w;
+      window->frame->rect.height = new_h;
+
+      meta_topic (META_DEBUG_GEOMETRY,
+                  "Calculated frame size %dx%d\n",
+                  window->frame->rect.width,
+                  window->frame->rect.height);
+    }
+  else
+    {
+      frame_size_dx = 0;
+      frame_size_dy = 0;
+    }
+
+  /* For nice effect, when growing the window we want to move/resize
+   * the frame first, when shrinking the window we want to move/resize
+   * the client first. If we grow one way and shrink the other,
+   * see which way we're moving "more"
+   *
+   * Mail from Owen subject "Suggestion: Gravity and resizing from the left"
+   * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html
+   *
+   * An annoying fact you need to know in this code is that StaticGravity
+   * does nothing if you _only_ resize or _only_ move the frame;
+   * it must move _and_ resize, otherwise you get NorthWestGravity
+   * behavior. The move and resize must actually occur, it is not
+   * enough to set CWX | CWWidth but pass in the current size/pos.
+   */
+      
+  if (window->frame)
+    {
+      int new_x, new_y;
+      int frame_pos_dx, frame_pos_dy;
+      
+      /* Compute new frame coords */
+      new_x = root_x_nw - fgeom.left_width;
+      new_y = root_y_nw - fgeom.top_height;
+
+      frame_pos_dx = new_x - window->frame->rect.x;
+      frame_pos_dy = new_y - window->frame->rect.y;
+
+      need_move_frame = (frame_pos_dx != 0 || frame_pos_dy != 0);
+      
+      window->frame->rect.x = new_x;
+      window->frame->rect.y = new_y;      
+
+      /* If frame will both move and resize, then StaticGravity
+       * on the child window will kick in and implicitly move
+       * the child with respect to the frame. The implicit
+       * move will keep the child in the same place with
+       * respect to the root window. If frame only moves
+       * or only resizes, then the child will just move along
+       * with the frame.
+       */
+
+      /* window->rect.x, window->rect.y are relative to frame,
+       * remember they are the server coords
+       */
+          
+      new_x = fgeom.left_width;
+      new_y = fgeom.top_height;
+
+      if (need_resize_frame && need_move_frame &&
+          static_gravity_works (window->display))
+        {
+          /* static gravity kicks in because frame
+           * is both moved and resized
+           */
+          /* when we move the frame by frame_pos_dx, frame_pos_dy the
+           * client will implicitly move relative to frame by the
+           * inverse delta.
+           * 
+           * When moving client then frame, we move the client by the
+           * frame delta, to be canceled out by the implicit move by
+           * the inverse frame delta, resulting in a client at new_x,
+           * new_y.
+           *
+           * When moving frame then client, we move the client
+           * by the same delta as the frame, because the client
+           * was "left behind" by the frame - resulting in a client
+           * at new_x, new_y.
+           *
+           * In both cases we need to move the client window
+           * in all cases where we had to move the frame window.
+           */
+          
+          client_move_x = new_x + frame_pos_dx;
+          client_move_y = new_y + frame_pos_dy;
+
+          if (need_move_frame)
+            need_move_client = TRUE;
+
+          use_static_gravity = TRUE;
+        }
+      else
+        {
+          client_move_x = new_x;
+          client_move_y = new_y;
+
+          if (client_move_x != window->rect.x ||
+              client_move_y != window->rect.y)
+            need_move_client = TRUE;
+
+          use_static_gravity = FALSE;
+        }
+
+      /* This is the final target position, but not necessarily what
+       * we pass to XConfigureWindow, due to StaticGravity implicit
+       * movement.
+       */      
+      window->rect.x = new_x;
+      window->rect.y = new_y;
+    }
+  else
+    {
+      if (root_x_nw != window->rect.x ||
+          root_y_nw != window->rect.y)
+        need_move_client = TRUE;
+      
+      window->rect.x = root_x_nw;
+      window->rect.y = root_y_nw;
+
+      client_move_x = window->rect.x;
+      client_move_y = window->rect.y;
+
+      use_static_gravity = FALSE;
+    }
+
+  /* If frame extents have changed, fill in other frame fields and
+     change frame's extents property. */
+  if (window->frame &&
+      (window->frame->child_x != fgeom.left_width ||
+          window->frame->child_y != fgeom.top_height ||
+          window->frame->right_width != fgeom.right_width ||
+          window->frame->bottom_height != fgeom.bottom_height))
+    {
+      window->frame->child_x = fgeom.left_width;
+      window->frame->child_y = fgeom.top_height;
+      window->frame->right_width = fgeom.right_width;
+      window->frame->bottom_height = fgeom.bottom_height;
+
+      update_net_frame_extents (window);
+    }
+
+  /* See ICCCM 4.1.5 for when to send ConfigureNotify */
+  
+  need_configure_notify = FALSE;
+  
+  /* If this is a configure request and we change nothing, then we
+   * must send configure notify.
+   */
+  if  (is_configure_request &&
+       !(need_move_client || need_move_frame ||
+         need_resize_client || need_resize_frame ||
+         window->border_width != 0))
+    need_configure_notify = TRUE;
+
+  /* We must send configure notify if we move but don't resize, since
+   * the client window may not get a real event
+   */
+  if ((need_move_client || need_move_frame) &&
+      !(need_resize_client || need_resize_frame))
+    need_configure_notify = TRUE;
+  
+  /* The rest of this function syncs our new size/pos with X as
+   * efficiently as possible
+   */
+
+  /* configure frame first if we grow more than we shrink
+   */
+  size_dx = w - window->rect.width;
+  size_dy = h - window->rect.height;
+
+  configure_frame_first = (size_dx + size_dy >= 0);
+
+  if (use_static_gravity)
+    meta_window_set_gravity (window, StaticGravity);  
+  
+  if (configure_frame_first && window->frame)
+    meta_frame_sync_to_window (window->frame,
+                               resize_gravity,
+                               need_move_frame, need_resize_frame);
+
+  values.border_width = 0;
+  values.x = client_move_x;
+  values.y = client_move_y;
+  values.width = window->rect.width;
+  values.height = window->rect.height;
+  
+  mask = 0;
+  if (is_configure_request && window->border_width != 0)
+    mask |= CWBorderWidth; /* must force to 0 */
+  if (need_move_client)
+    mask |= (CWX | CWY);
+  if (need_resize_client)
+    mask |= (CWWidth | CWHeight);
+
+  if (mask != 0)
+    {
+      {
+        int newx, newy;
+        meta_window_get_position (window, &newx, &newy);
+        meta_topic (META_DEBUG_GEOMETRY,
+                    "Syncing new client geometry %d,%d %dx%d, border: %s pos: %s size: %s\n",
+                    newx, newy,
+                    window->rect.width, window->rect.height,
+                    mask & CWBorderWidth ? "true" : "false",
+                    need_move_client ? "true" : "false",
+                    need_resize_client ? "true" : "false");
+      }
+      
+      meta_error_trap_push (window->display);
+      XConfigureWindow (window->display->xdisplay,
+                        window->xwindow,
+                        mask,
+                        &values);
+      meta_error_trap_pop (window->display, FALSE);
+    }
+
+  if (!configure_frame_first && window->frame)
+    meta_frame_sync_to_window (window->frame,
+                               resize_gravity,
+                               need_move_frame, need_resize_frame);  
+
+  /* Put gravity back to be nice to lesser window managers */
+  if (use_static_gravity)
+    meta_window_set_gravity (window, NorthWestGravity);  
+  
+  if (need_configure_notify)
+    send_configure_notify (window);
+
+  if (is_user_action)
+    {
+      window->user_has_move_resized = TRUE;
+
+      window->user_rect.width = window->rect.width;
+      window->user_rect.height = window->rect.height;
+
+      meta_window_get_position (window, 
+                                &window->user_rect.x,
+                                &window->user_rect.y);
+    }
+  
+  if (need_move_frame || need_resize_frame ||
+      need_move_client || need_resize_client)
+    {
+      int newx, newy;
+      meta_window_get_position (window, &newx, &newy);
+      meta_topic (META_DEBUG_GEOMETRY,
+                  "New size/position %d,%d %dx%d (user %d,%d %dx%d)\n",
+                  newx, newy, window->rect.width, window->rect.height,
+                  window->user_rect.x, window->user_rect.y,
+                  window->user_rect.width, window->user_rect.height);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_GEOMETRY, "Size/position not modified\n");
+    }
+  
+  meta_window_refresh_resize_popup (window);
+  
+  /* Invariants leaving this function are:
+   *   a) window->rect and frame->rect reflect the actual
+   *      server-side size/pos of window->xwindow and frame->xwindow
+   *   b) all constraints are obeyed by window->rect and frame->rect
+   */
+}
+
+void
+meta_window_resize (MetaWindow  *window,
+                    gboolean     user_op,
+                    int          w,
+                    int          h)
+{
+  int x, y;
+
+  meta_window_get_position (window, &x, &y);
+  
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    NorthWestGravity,
+                                    x, y, w, h);
+}
+
+void
+meta_window_move (MetaWindow  *window,
+                  gboolean     user_op,
+                  int          root_x_nw,
+                  int          root_y_nw)
+{
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    NorthWestGravity,
+                                    root_x_nw, root_y_nw,
+                                    window->rect.width,
+                                    window->rect.height);
+}
+
+void
+meta_window_move_resize (MetaWindow  *window,
+                         gboolean     user_op,
+                         int          root_x_nw,
+                         int          root_y_nw,
+                         int          w,
+                         int          h)
+{
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    NorthWestGravity,
+                                    root_x_nw, root_y_nw,
+                                    w, h);
+}
+
+void
+meta_window_resize_with_gravity (MetaWindow *window,
+                                 gboolean     user_op,
+                                 int          w,
+                                 int          h,
+                                 int          gravity)
+{
+  int x, y;
+
+  meta_window_get_position (window, &x, &y);
+  
+  meta_window_move_resize_internal (window,
+                                    user_op ? META_USER_MOVE_RESIZE : 0,
+                                    gravity,
+                                    x, y, w, h);
+}
+
+void
+meta_window_move_resize_now (MetaWindow  *window)
+{
+  int x, y;
+
+  /* If constraints have changed then we'll snap back to wherever
+   * the user had the window
+   */
+  meta_window_get_user_position (window, &x, &y);
+
+  /* This used to use the user width/height if the user hadn't resized,
+   * but it turns out that breaks things pretty often, because configure
+   * requests from the app or size hints changes from the app frequently
+   * reflect user actions such as changing terminal font size
+   * or expanding a disclosure triangle.
+   */
+  meta_window_move_resize (window, FALSE, x, y,
+                           window->rect.width,
+                           window->rect.height);
+}
+
+static void
+check_maximize_to_work_area (MetaWindow          *window,
+                             const MetaRectangle *work_area)
+{
+  /* If we now fill the screen, maximize.
+   * the point here is that fill horz + fill vert = maximized
+   */
+  MetaRectangle rect;
+
+  if (!window->has_maximize_func)
+    return;
+  
+  meta_window_get_outer_rect (window, &rect);
+
+  if ( rect.x >= work_area->x &&
+       rect.y >= work_area->y &&
+       (((work_area->width - work_area->x) - rect.width) <
+        window->size_hints.width_inc) &&
+       (((work_area->height - work_area->y) - rect.height) <
+        window->size_hints.height_inc) )
+    meta_window_maximize (window);
+}
+
+void
+meta_window_fill_horizontal (MetaWindow  *window)
+{
+  MetaRectangle work_area;
+  int x, y, w, h;
+  
+  meta_window_get_user_position (window, &x, &y);
+
+  w = window->rect.width;
+  h = window->rect.height;
+  
+  meta_window_get_work_area_current_xinerama (window, &work_area);
+  
+  x = work_area.x;
+  w = work_area.width;
+  
+  if (window->frame != NULL)
+    {
+      x += window->frame->child_x;
+      w -= (window->frame->child_x + window->frame->right_width);
+    }
+  
+  meta_window_move_resize (window, TRUE,
+                           x, y, w, h);
+
+  check_maximize_to_work_area (window, &work_area);
+}
+
+void
+meta_window_fill_vertical (MetaWindow  *window)
+{
+  MetaRectangle work_area;
+  int x, y, w, h;
+  
+  meta_window_get_user_position (window, &x, &y);
+
+  w = window->rect.width;
+  h = window->rect.height;
+
+  meta_window_get_work_area_current_xinerama (window, &work_area);
+
+  y = work_area.y;
+  h = work_area.height;
+  
+  if (window->frame != NULL)
+    {
+      y += window->frame->child_y;
+      h -= (window->frame->child_y + window->frame->bottom_height);
+    }
+  
+  meta_window_move_resize (window, TRUE,
+                           x, y, w, h);
+
+  check_maximize_to_work_area (window, &work_area);
+}
+
+static guint move_resize_idle = 0;
+static GSList *move_resize_pending = NULL;
+
+static gboolean
+idle_move_resize (gpointer data)
+{
+  GSList *tmp;
+  GSList *copy;
+
+  meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue\n");
+
+  /* Work with a copy, for reentrancy. The allowed reentrancy isn't
+   * complete; destroying a window while we're in here would result in
+   * badness. But it's OK to queue/unqueue move_resizes.
+   */
+  copy = g_slist_copy (move_resize_pending);
+  g_slist_free (move_resize_pending);
+  move_resize_pending = NULL;
+  move_resize_idle = 0;
+
+  destroying_windows_disallowed += 1;
+  
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      /* As a side effect, sets window->move_resize_queued = FALSE */
+      meta_window_move_resize_now (window); 
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (copy);
+
+  destroying_windows_disallowed -= 1;
+  
+  return FALSE;
+}
+
+void
+meta_window_unqueue_move_resize (MetaWindow *window)
+{
+  if (!window->move_resize_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Removing %s from the move_resize queue\n",
+              window->desc);
+
+  /* Note that window may not actually be in move_resize_pending
+   * because it may have been in "copy" inside the idle handler
+   */
+  move_resize_pending = g_slist_remove (move_resize_pending, window);
+  window->move_resize_queued = FALSE;
+  
+  if (move_resize_pending == NULL &&
+      move_resize_idle != 0)
+    {
+      g_source_remove (move_resize_idle);
+      move_resize_idle = 0;
+    }
+}
+
+void
+meta_window_flush_move_resize (MetaWindow *window)
+{
+  if (window->move_resize_queued)
+    {
+      meta_window_unqueue_move_resize (window);
+      meta_window_move_resize_now (window);
+    }
+}
+
+/* The move/resize queue is only used when we need to
+ * recheck the constraints on the window, e.g. when
+ * maximizing or when changing struts. Configure requests
+ * and such always have to be handled synchronously,
+ * they can't be done via a queue.
+ */
+void
+meta_window_queue_move_resize (MetaWindow  *window)
+{
+  if (window->unmanaging)
+    return;
+
+  if (window->move_resize_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Putting %s in the move_resize queue\n",
+              window->desc);
+  
+  window->move_resize_queued = TRUE;
+  
+  if (move_resize_idle == 0)
+    move_resize_idle = g_idle_add_full (META_PRIORITY_RESIZE,
+                                        idle_move_resize, NULL, NULL);
+  
+  move_resize_pending = g_slist_prepend (move_resize_pending, window);
+}
+
+void
+meta_window_get_position (MetaWindow  *window,
+                          int         *x,
+                          int         *y)
+{
+  if (window->frame)
+    {
+      if (x)
+        *x = window->frame->rect.x + window->frame->child_x;
+      if (y)
+        *y = window->frame->rect.y + window->frame->child_y;
+    }
+  else
+    {
+      if (x)
+        *x = window->rect.x;
+      if (y)
+        *y = window->rect.y;
+    }
+}
+
+void
+meta_window_get_user_position  (MetaWindow  *window,
+                                int         *x,
+                                int         *y)
+{
+  if (window->user_has_move_resized)
+    {
+      if (x)
+        *x = window->user_rect.x;
+      if (y)
+        *y = window->user_rect.y;
+    }
+  else
+    {
+      meta_window_get_position (window, x, y);
+    }
+}
+
+void
+meta_window_get_gravity_position (MetaWindow  *window,
+                                  int         *root_x,
+                                  int         *root_y)
+{
+  MetaRectangle frame_extents;
+  int w, h;
+  int x, y;
+  
+  w = window->rect.width;
+  h = window->rect.height;
+
+  if (window->size_hints.win_gravity == StaticGravity)
+    {
+      frame_extents = window->rect;
+      if (window->frame)
+        {
+          frame_extents.x = window->frame->rect.x + window->frame->child_x;
+          frame_extents.y = window->frame->rect.y + window->frame->child_y;
+        }
+    }
+  else
+    {
+      if (window->frame == NULL)
+        frame_extents = window->rect;
+      else
+        frame_extents = window->frame->rect;
+    }
+
+  x = frame_extents.x;
+  y = frame_extents.y;
+  
+  switch (window->size_hints.win_gravity)
+    {
+    case NorthGravity:
+    case CenterGravity:
+    case SouthGravity:
+      /* Find center of frame. */
+      x += frame_extents.width / 2;
+      /* Center client window on that point. */
+      x -= w / 2;
+      break;
+      
+    case SouthEastGravity:
+    case EastGravity:
+    case NorthEastGravity:
+      /* Find right edge of frame */
+      x += frame_extents.width;
+      /* Align left edge of client at that point. */
+      x -= w;
+      break;
+    default:
+      break;
+    }
+  
+  switch (window->size_hints.win_gravity)
+    {
+    case WestGravity:
+    case CenterGravity:
+    case EastGravity:
+      /* Find center of frame. */
+      y += frame_extents.height / 2;
+      /* Center client window there. */
+      y -= h / 2;
+      break;
+    case SouthWestGravity:
+    case SouthGravity:
+    case SouthEastGravity:
+      /* Find south edge of frame */
+      y += frame_extents.height;
+      /* Place bottom edge of client there */
+      y -= h;
+      break;
+    default:
+      break;
+    }
+  
+  if (root_x)
+    *root_x = x;
+  if (root_y)
+    *root_y = y;
+}
+
+void
+meta_window_get_geometry (MetaWindow  *window,
+                          int         *x,
+                          int         *y,
+                          int         *width,
+                          int         *height)
+{
+  meta_window_get_gravity_position (window, x, y);
+
+  *width = (window->rect.width - window->size_hints.base_width) /
+    window->size_hints.width_inc;
+  *height = (window->rect.height - window->size_hints.base_height) /
+    window->size_hints.height_inc;
+}
+
+void
+meta_window_get_outer_rect (MetaWindow    *window,
+                            MetaRectangle *rect)
+{
+  if (window->frame)
+    *rect = window->frame->rect;
+  else
+    *rect = window->rect;
+}
+
+const char*
+meta_window_get_startup_id (MetaWindow *window)
+{
+  if (window->startup_id == NULL)
+    {
+      MetaGroup *group;
+
+      group = meta_window_get_group (window);
+
+      if (group != NULL)
+        return meta_group_get_startup_id (group);
+    }
+
+  return window->startup_id;
+}
+
+void
+meta_window_focus (MetaWindow  *window,
+                   Time         timestamp)
+{  
+  meta_topic (META_DEBUG_FOCUS,
+              "Setting input focus to window %s, input: %d take_focus: %d\n",
+              window->desc, window->input, window->take_focus);
+  
+  if (window->display->grab_window &&
+      window->display->grab_window->all_keys_grabbed)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Current focus window %s has global keygrab, not focusing window %s after all\n",
+                  window->display->grab_window->desc, window->desc);
+      return;
+    }
+
+  meta_window_flush_calc_showing (window);
+
+  if (!window->mapped && !window->shaded)
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Window %s is not showing, not focusing after all\n",
+                  window->desc);
+      return;
+    }
+  
+  /* For output-only or shaded windows, focus the frame.
+   * This seems to result in the client window getting key events
+   * though, so I don't know if it's icccm-compliant.
+   *
+   * Still, we have to do this or keynav breaks for these windows.
+   */
+  if (window->frame &&
+      (window->shaded ||
+       !(window->input || window->take_focus)))
+    {
+      if (window->frame)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Focusing frame of %s\n", window->desc);
+          XSetInputFocus (window->display->xdisplay,
+                          window->frame->xwindow,
+                          RevertToPointerRoot,
+                          timestamp);
+          window->display->expected_focus_window = window;
+        }
+    }
+  else
+    {
+      meta_error_trap_push (window->display);
+      
+      if (window->input)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Calling XSetInputFocus() on client window %s since input = true\n",
+                      window->desc);
+          XSetInputFocus (window->display->xdisplay,
+                          window->xwindow,
+                          RevertToPointerRoot,
+                          timestamp);
+          window->display->expected_focus_window = window;
+        }
+      
+      if (window->take_focus)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Sending WM_TAKE_FOCUS to %s since take_focus = true\n",
+                      window->desc);
+          meta_window_send_icccm_message (window,
+                                          window->display->atom_wm_take_focus,
+                                          timestamp);
+          window->display->expected_focus_window = window;
+        }
+      
+      meta_error_trap_pop (window->display, FALSE);
+    }
+}
+
+static void
+meta_window_change_workspace_without_transients (MetaWindow    *window,
+                                                 MetaWorkspace *workspace)
+{
+  GList *next;
+  
+  meta_verbose ("Changing window %s to workspace %d\n",
+                window->desc, meta_workspace_index (workspace));
+  
+  /* unstick if stuck. meta_window_unstick would call 
+   * meta_window_change_workspace recursively if the window
+   * is not in the active workspace.
+   */
+  if (window->on_all_workspaces)
+    meta_window_unstick (window);
+
+  /* See if we're already on this space. If not, make sure we are */
+  if (g_list_find (window->workspaces, workspace) == NULL)
+    meta_workspace_add_window (workspace, window);
+  
+  /* Remove from all other spaces */
+  next = window->workspaces;
+  while (next != NULL)
+    {
+      MetaWorkspace *remove;
+      remove = next->data;
+      next = next->next;
+
+      if (remove != workspace)
+        meta_workspace_remove_window (remove, window);
+    }
+
+  /* list size == 1 */
+  g_assert (window->workspaces != NULL);
+  g_assert (window->workspaces->next == NULL);
+}
+
+static gboolean
+change_workspace_foreach (MetaWindow *window,
+                          void       *data)
+{
+  meta_window_change_workspace_without_transients (window, data);
+  return TRUE;
+}
+
+void
+meta_window_change_workspace (MetaWindow    *window,
+                              MetaWorkspace *workspace)
+{
+  meta_window_change_workspace_without_transients (window, workspace);
+
+  meta_window_foreach_transient (window, change_workspace_foreach,
+                                 workspace);
+}
+
+void
+meta_window_stick (MetaWindow  *window)
+{
+  GList *tmp; 
+  MetaWorkspace *workspace;
+
+  meta_verbose ("Sticking window %s current on_all_workspaces = %d\n",
+                window->desc, window->on_all_workspaces);
+  
+  if (window->on_all_workspaces)
+    return;
+
+  /* We don't change window->workspaces, because we revert
+   * to that original workspace list if on_all_workspaces is
+   * toggled back off.
+   */
+  window->on_all_workspaces = TRUE;
+
+  /* We do, however, change the MRU lists of all the workspaces
+   */
+  tmp = window->screen->workspaces;
+  while (tmp)
+    {
+      workspace = (MetaWorkspace *) tmp->data;
+      if (!g_list_find (workspace->mru_list, window))
+        workspace->mru_list = g_list_append (workspace->mru_list, window);
+
+      tmp = tmp->next;
+    }
+
+  meta_window_set_current_workspace_hint (window);
+  
+  meta_window_queue_calc_showing (window);
+}
+
+void
+meta_window_unstick (MetaWindow  *window)
+{
+  GList *tmp;
+  MetaWorkspace *workspace;
+
+  if (!window->on_all_workspaces)
+    return;
+
+  /* Revert to window->workspaces */
+
+  window->on_all_workspaces = FALSE;
+
+  /* Remove window from MRU lists that it doesn't belong in */
+  tmp = window->screen->workspaces;
+  while (tmp)
+    {
+      workspace = (MetaWorkspace *) tmp->data;
+      if (!meta_workspace_contains_window (workspace, window))
+        workspace->mru_list = g_list_remove (workspace->mru_list, window);
+      tmp = tmp->next;
+    }
+
+  /* We change ourselves to the active workspace, since otherwise you'd get
+   * a weird window-vaporization effect. Once we have UI for being
+   * on more than one workspace this should probably be add_workspace
+   * not change_workspace.
+   */
+  if (!meta_workspace_contains_window (window->screen->active_workspace,
+                                       window))
+    meta_window_change_workspace (window, window->screen->active_workspace);
+  
+  meta_window_set_current_workspace_hint (window);
+  
+  meta_window_queue_calc_showing (window);
+}
+
+unsigned long
+meta_window_get_net_wm_desktop (MetaWindow *window)
+{
+  if (window->on_all_workspaces ||
+      g_list_length (window->workspaces) > 1)
+    return 0xFFFFFFFF;
+  else
+    return meta_workspace_index (window->workspaces->data);
+}
+
+static void
+update_net_frame_extents (MetaWindow *window)
+{
+  unsigned long data[4] = { 0, 0, 0, 0 };
+
+  if (window->frame)
+    {
+      /* Left */
+      data[0] = window->frame->child_x;
+      /* Right */
+      data[1] = window->frame->right_width;
+      /* Top */
+      data[2] = window->frame->child_y;
+      /* Bottom */
+      data[3] = window->frame->bottom_height;
+    }
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Setting _NET_FRAME_EXTENTS on managed window 0x%lx "
+              "to top = %ld, left = %ld, bottom = %ld, right = %ld\n",
+              window->xwindow, data[0], data[1], data[2], data[3]);
+
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_frame_extents,
+                   XA_CARDINAL,
+                   32, PropModeReplace, (guchar*) data, 4);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+void
+meta_window_set_current_workspace_hint (MetaWindow *window)
+{
+  /* FIXME if on more than one workspace, we claim to be "sticky",
+   * the WM spec doesn't say what to do here.
+   */
+  unsigned long data[1];
+
+  if (window->workspaces == NULL)
+    {
+      /* this happens when unmanaging windows */      
+      return;
+    }
+  
+  data[0] = meta_window_get_net_wm_desktop (window);
+
+  meta_verbose ("Setting _NET_WM_DESKTOP of %s to %ld\n",
+                window->desc, data[0]);
+  
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_wm_desktop,
+                   XA_CARDINAL,
+                   32, PropModeReplace, (guchar*) data, 1);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+void
+meta_window_raise (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Raising window %s\n", window->desc);
+
+  meta_stack_raise (window->screen->stack, window);
+}
+
+void
+meta_window_lower (MetaWindow  *window)
+{
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Lowering window %s\n", window->desc);
+
+  meta_stack_lower (window->screen->stack, window);
+}
+
+void
+meta_window_send_icccm_message (MetaWindow *window,
+                                Atom        atom,
+                                Time        timestamp)
+{
+  /* This comment and code are from twm, copyright
+   * Open Group, Evans & Sutherland, etc.
+   */
+  
+  /*
+   * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
+   * client messages will have the following form:
+   *
+   *     event type	ClientMessage
+   *     message type	_XA_WM_PROTOCOLS
+   *     window		tmp->w
+   *     format		32
+   *     data[0]		message atom
+   *     data[1]		time stamp
+   */
+  
+    XClientMessageEvent ev;
+    
+    ev.type = ClientMessage;
+    ev.window = window->xwindow;
+    ev.message_type = window->display->atom_wm_protocols;
+    ev.format = 32;
+    ev.data.l[0] = atom;
+    ev.data.l[1] = timestamp;
+
+    meta_error_trap_push (window->display);
+    XSendEvent (window->display->xdisplay,
+                window->xwindow, False, 0, (XEvent*) &ev);
+    meta_error_trap_pop (window->display, FALSE);
+}
+
+gboolean
+meta_window_configure_request (MetaWindow *window,
+                               XEvent     *event)
+{
+  int x, y, width, height;
+  gboolean only_resize;
+  gboolean allow_position_change;
+  gboolean in_grab_op;
+
+  /* We ignore configure requests while the user is moving/resizing
+   * the window, since these represent the app sucking and fighting
+   * the user, most likely due to a bug in the app (e.g. pfaedit
+   * seemed to do this)
+   *
+   * Still have to do the ConfigureNotify and all, but pretend the
+   * app asked for the current size/position instead of the new one.
+   */  
+  in_grab_op = FALSE;
+  if (window->display->grab_op != META_GRAB_OP_NONE &&
+      window == window->display->grab_window)
+    {
+      switch (window->display->grab_op)
+        {
+        case META_GRAB_OP_MOVING:
+        case META_GRAB_OP_RESIZING_SE:
+        case META_GRAB_OP_RESIZING_S:
+        case META_GRAB_OP_RESIZING_SW:
+        case META_GRAB_OP_RESIZING_N:
+        case META_GRAB_OP_RESIZING_NE:
+        case META_GRAB_OP_RESIZING_NW:
+        case META_GRAB_OP_RESIZING_W:
+        case META_GRAB_OP_RESIZING_E:
+          in_grab_op = TRUE;
+          break;
+        default:
+          break;
+        }
+    }
+  
+  /* it's essential to use only the explicitly-set fields,
+   * and otherwise use our current up-to-date position.
+   *
+   * Otherwise you get spurious position changes when the app changes
+   * size, for example, if window->rect is not in sync with the
+   * server-side position in effect when the configure request was
+   * generated.
+   */
+
+  meta_window_get_gravity_position (window, &x, &y);
+
+  only_resize = TRUE;
+
+  allow_position_change = FALSE;
+  
+  if (meta_prefs_get_disable_workarounds ())
+    {
+      if (window->type == META_WINDOW_DIALOG ||
+          window->type == META_WINDOW_MODAL_DIALOG ||
+          window->type == META_WINDOW_SPLASHSCREEN)
+        ; /* No position change for these */
+      else if ((window->size_hints.flags & PPosition) ||
+               /* USPosition is just stale if window is placed;
+                * no --geometry involved here.
+                */
+               ((window->size_hints.flags & USPosition) &&
+                !window->placed))
+        allow_position_change = TRUE;
+    }
+  else
+    {
+      allow_position_change = TRUE;
+    }
+
+  if (in_grab_op)
+    allow_position_change = FALSE;
+  
+  if (allow_position_change)
+    {
+      if (event->xconfigurerequest.value_mask & CWX)
+        x = event->xconfigurerequest.x;
+      
+      if (event->xconfigurerequest.value_mask & CWY)
+        y = event->xconfigurerequest.y;
+
+      if (event->xconfigurerequest.value_mask & (CWX | CWY))
+        {
+          only_resize = FALSE;
+
+          /* Once manually positioned, windows shouldn't be placed
+           * by the window manager.
+           */
+          window->placed = TRUE;
+        }
+    }
+  else
+    {
+      meta_topic (META_DEBUG_GEOMETRY,
+		  "Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %d\n", 
+		  window->desc, window->size_hints.flags & PPosition, 
+		  window->size_hints.flags & USPosition,
+		  window->type);     
+    }
+
+  width = window->rect.width;
+  height = window->rect.height;
+
+  if (!in_grab_op)
+    {
+      if (event->xconfigurerequest.value_mask & CWWidth)
+        width = event->xconfigurerequest.width;
+      
+      if (event->xconfigurerequest.value_mask & CWHeight)
+        height = event->xconfigurerequest.height;
+    }
+
+  /* ICCCM 4.1.5 */
+  
+  /* Note that x, y is the corner of the window border,
+   * and width, height is the size of the window inside
+   * its border, but that we always deny border requests
+   * and give windows a border of 0. But we save the
+   * requested border here.
+   */
+  window->border_width = event->xconfigurerequest.border_width;
+
+  /* We're ignoring the value_mask here, since sizes
+   * not in the mask will be the current window geometry.
+   */
+  
+  window->size_hints.x = x;
+  window->size_hints.y = y;
+  window->size_hints.width = width;
+  window->size_hints.height = height;
+
+  /* FIXME passing the gravity on only_resize thing is kind of crack-rock.
+   * Basically I now have several ways of handling gravity, and things
+   * don't make too much sense. I think I am doing the math in a couple
+   * places and could do it in only one function, and remove some of the
+   * move_resize_internal arguments.
+   */
+  
+  meta_window_move_resize_internal (window, META_IS_CONFIGURE_REQUEST,
+                                    only_resize ?
+                                    window->size_hints.win_gravity : NorthWestGravity,
+                                    window->size_hints.x,
+                                    window->size_hints.y,
+                                    window->size_hints.width,
+                                    window->size_hints.height);
+
+  /* Handle stacking. We only handle raises/lowers, mostly because
+   * stack.c really can't deal with anything else.  I guess we'll fix
+   * that if a client turns up that really requires it. Only a very
+   * few clients even require the raise/lower (and in fact all client
+   * attempts to deal with stacking order are essentially broken,
+   * since they have no idea what other clients are involved or how
+   * the stack looks).
+   *
+   * I'm pretty sure no interesting client uses TopIf, BottomIf, or
+   * Opposite anyway, so the only possible missing thing is
+   * Above/Below with a sibling set. For now we just pretend there's
+   * never a sibling set and always do the full raise/lower instead of
+   * the raise-just-above/below-sibling.
+   */
+  if (event->xconfigurerequest.value_mask & CWStackMode)
+    {
+      switch (event->xconfigurerequest.detail)
+        {
+        case Above:
+          meta_window_raise (window);
+          break;
+        case Below:
+          meta_window_lower (window);
+          break;
+        case TopIf:
+        case BottomIf:
+        case Opposite:
+          break;
+        }
+    }      
+  
+  return TRUE;
+}
+
+gboolean
+meta_window_property_notify (MetaWindow *window,
+                             XEvent     *event)
+{
+  return process_property_notify (window, &event->xproperty);  
+}
+
+#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
+#define _NET_WM_MOVERESIZE_SIZE_TOP          1
+#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
+#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
+#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
+#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
+#define _NET_WM_MOVERESIZE_MOVE              8
+#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9
+#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10
+
+gboolean
+meta_window_client_message (MetaWindow *window,
+                            XEvent     *event)
+{
+  MetaDisplay *display;
+
+  display = window->display;
+  
+  if (event->xclient.message_type ==
+      display->atom_net_close_window)
+    {
+      /* I think the wm spec should maybe put a time
+       * in this message, CurrentTime here is sort of
+       * bogus. But it rarely matters most likely.
+       */
+      meta_window_delete (window, meta_display_get_current_time (window->display));
+
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_wm_desktop)
+    {
+      int space;
+      MetaWorkspace *workspace;
+              
+      space = event->xclient.data.l[0];
+              
+      meta_verbose ("Request to move %s to workspace %d\n",
+                    window->desc, space);
+
+      workspace =
+        meta_screen_get_workspace_by_index (window->screen,
+                                            space);
+
+      if (workspace)
+        {
+          if (window->on_all_workspaces)
+            meta_window_unstick (window);
+          meta_window_change_workspace (window, workspace);
+        }
+      else if (space == (int) 0xFFFFFFFF)
+        {
+          meta_window_stick (window);
+        }
+      else
+        {
+          meta_verbose ("No such workspace %d for screen\n", space);
+        }
+
+      meta_verbose ("Window %s now on_all_workspaces = %d\n",
+                    window->desc, window->on_all_workspaces);
+      
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_wm_state)
+    {
+      gulong action;
+      Atom first;
+      Atom second;
+
+      action = event->xclient.data.l[0];
+      first = event->xclient.data.l[1];
+      second = event->xclient.data.l[2];
+      
+      if (meta_is_verbose ())
+        {
+          char *str1;
+          char *str2;
+
+          meta_error_trap_push_with_return (display);
+          str1 = XGetAtomName (display->xdisplay, first);
+          if (meta_error_trap_pop_with_return (display, TRUE) != Success)
+            str1 = NULL;
+
+          meta_error_trap_push_with_return (display);
+          str2 = XGetAtomName (display->xdisplay, second); 
+          if (meta_error_trap_pop_with_return (display, TRUE) != Success)
+            str2 = NULL;
+          
+          meta_verbose ("Request to change _NET_WM_STATE action %ld atom1: %s atom2: %s\n",
+                        action,
+                        str1 ? str1 : "(unknown)",
+                        str2 ? str2 : "(unknown)");
+
+          meta_XFree (str1);
+          meta_XFree (str2);
+        }
+
+      if (first == display->atom_net_wm_state_shaded ||
+          second == display->atom_net_wm_state_shaded)
+        {
+          gboolean shade;
+
+          shade = (action == _NET_WM_STATE_ADD ||
+                   (action == _NET_WM_STATE_TOGGLE && !window->shaded));
+          if (shade && window->has_shade_func)
+            meta_window_shade (window);
+          else
+            meta_window_unshade (window);
+        }
+
+      if (first == display->atom_net_wm_state_fullscreen ||
+          second == display->atom_net_wm_state_fullscreen)
+        {
+          gboolean make_fullscreen;
+
+          make_fullscreen = (action == _NET_WM_STATE_ADD ||
+                             (action == _NET_WM_STATE_TOGGLE && !window->fullscreen));
+          if (make_fullscreen && window->has_fullscreen_func)
+            meta_window_make_fullscreen (window);
+          else
+            meta_window_unmake_fullscreen (window);
+        }
+      
+      if (first == display->atom_net_wm_state_maximized_horz ||
+          second == display->atom_net_wm_state_maximized_horz ||
+          first == display->atom_net_wm_state_maximized_vert ||
+          second == display->atom_net_wm_state_maximized_vert)
+        {
+          gboolean max;
+
+          max = (action == _NET_WM_STATE_ADD ||
+                 (action == _NET_WM_STATE_TOGGLE && !window->maximized));
+          if (max && window->has_maximize_func)
+            meta_window_maximize (window);
+          else
+            meta_window_unmaximize (window);
+        }
+
+      if (first == display->atom_net_wm_state_modal ||
+          second == display->atom_net_wm_state_modal)
+        {
+          window->wm_state_modal =
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->wm_state_modal);
+          
+          recalc_window_type (window);
+          meta_window_queue_move_resize (window);
+        }
+
+      if (first == display->atom_net_wm_state_skip_pager ||
+          second == display->atom_net_wm_state_skip_pager)
+        {
+          window->wm_state_skip_pager = 
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->skip_pager);
+
+          recalc_window_features (window);
+          set_net_wm_state (window);
+        }
+
+      if (first == display->atom_net_wm_state_skip_taskbar ||
+          second == display->atom_net_wm_state_skip_taskbar)
+        {
+          window->wm_state_skip_taskbar =
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar);
+
+          recalc_window_features (window);
+          set_net_wm_state (window);
+        }
+
+      if (first == display->atom_net_wm_state_above ||
+          second == display->atom_net_wm_state_above)
+        {
+          window->wm_state_above = 
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->wm_state_above);
+
+          meta_window_update_layer (window);
+          set_net_wm_state (window);
+        }
+
+      if (first == display->atom_net_wm_state_below ||
+          second == display->atom_net_wm_state_below)
+        {
+          window->wm_state_below = 
+            (action == _NET_WM_STATE_ADD) ||
+            (action == _NET_WM_STATE_TOGGLE && !window->wm_state_below);
+
+          meta_window_update_layer (window);
+          set_net_wm_state (window);
+        }
+      
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_wm_change_state)
+    {
+      meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n",
+                    event->xclient.data.l[0]);
+      if (event->xclient.data.l[0] == IconicState &&
+          window->has_minimize_func)
+        meta_window_minimize (window);
+
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_wm_moveresize)
+    {
+      int x_root;
+      int y_root;
+      int action;
+      MetaGrabOp op;
+      int button;
+      
+      x_root = event->xclient.data.l[0];
+      y_root = event->xclient.data.l[1];
+      action = event->xclient.data.l[2];
+      button = event->xclient.data.l[3];
+
+      meta_topic (META_DEBUG_WINDOW_OPS,
+                  "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n",
+                  window->desc,
+                  x_root, y_root, action, button);
+      
+      op = META_GRAB_OP_NONE;
+      switch (action)
+        {
+        case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
+          op = META_GRAB_OP_RESIZING_NW;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_TOP:
+          op = META_GRAB_OP_RESIZING_N;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
+          op = META_GRAB_OP_RESIZING_NE;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_RIGHT:
+          op = META_GRAB_OP_RESIZING_E;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
+          op = META_GRAB_OP_RESIZING_SE;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_BOTTOM:          
+          op = META_GRAB_OP_RESIZING_S;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
+          op = META_GRAB_OP_RESIZING_SW;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_LEFT:
+          op = META_GRAB_OP_RESIZING_W;
+          break;
+        case _NET_WM_MOVERESIZE_MOVE:
+          op = META_GRAB_OP_MOVING;
+          break;
+        case _NET_WM_MOVERESIZE_SIZE_KEYBOARD:
+          op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN;
+          break;
+        case _NET_WM_MOVERESIZE_MOVE_KEYBOARD:
+          op = META_GRAB_OP_KEYBOARD_MOVING;
+          break;
+        default:
+          break;
+        }
+
+      if (op != META_GRAB_OP_NONE &&
+          ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) ||
+           (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN)))
+        {
+
+          meta_window_begin_grab_op (window,
+                                     op,
+                                     meta_display_get_current_time (window->display));
+        }
+      else if (op != META_GRAB_OP_NONE &&
+               ((window->has_move_func && op == META_GRAB_OP_MOVING) ||
+               (window->has_resize_func && 
+                (op != META_GRAB_OP_MOVING && 
+                 op != META_GRAB_OP_KEYBOARD_MOVING))))
+        {
+          /*
+           * the button SHOULD already be included in the message
+           */
+          if (button == 0)
+            {
+              int x, y, query_root_x, query_root_y;
+              Window root, child;
+              guint mask;
+
+              /* The race conditions in this _NET_WM_MOVERESIZE thing
+               * are mind-boggling
+               */
+              mask = 0;
+              meta_error_trap_push (window->display);
+              XQueryPointer (window->display->xdisplay,
+                             window->xwindow,
+                             &root, &child,
+                             &query_root_x, &query_root_y,
+                             &x, &y,
+                             &mask);
+              meta_error_trap_pop (window->display, TRUE);
+
+              if (mask & Button1Mask)
+                button = 1;
+              else if (mask & Button2Mask)
+                button = 2;
+              else if (mask & Button3Mask)
+                button = 3;
+              else
+                button = 0;
+            }
+
+          if (button != 0)
+            {
+              meta_topic (META_DEBUG_WINDOW_OPS,
+                          "Beginning move/resize with button = %d\n", button);
+              meta_display_begin_grab_op (window->display,
+                                          window->screen,
+                                          window,
+                                          op,
+                                          FALSE, 0 /* event_serial */,
+                                          button, 0,
+                                          meta_display_get_current_time (window->display),
+                                          x_root,
+                                          y_root);
+            }
+        }
+
+      return TRUE;
+    }
+  else if (event->xclient.message_type ==
+           display->atom_net_active_window)
+    {
+      meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating",
+                    window->desc);
+
+      meta_window_activate (window, meta_display_get_current_time (window->display));
+
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+is_in_dock_group (MetaWindow *window)
+{
+  MetaGroup *group;
+  GSList *list;
+  GSList *tmp;
+  
+  if (META_WINDOW_IN_DOCK_TAB_CHAIN (window))
+    return TRUE;
+
+  if (window->type == META_WINDOW_NORMAL)
+    return FALSE;
+
+  /* If a transient-type window is in dock group,
+   * return TRUE
+   */
+  group = meta_window_get_group (window);
+  list = meta_group_list_windows (group);
+  tmp = list;
+  while (tmp != NULL)
+    {
+      MetaWindow *gw = tmp->data;
+
+      if (META_WINDOW_IN_DOCK_TAB_CHAIN (gw))
+        {
+          g_slist_free (list);
+          return TRUE;
+        }
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (list);
+  return FALSE;
+}
+
+static int
+docks_at_end_cmp (const void *a,
+                  const void *b)
+{
+  MetaWindow *window_a = (MetaWindow*) a;
+  MetaWindow *window_b = (MetaWindow*) b;
+
+  if (META_WINDOW_IN_DOCK_TAB_CHAIN (window_a))
+    {
+      if (META_WINDOW_IN_DOCK_TAB_CHAIN (window_b))
+        return 0;
+      else
+        return 1; /* a > b since a is a dock */
+    }
+  else
+    {
+      if (META_WINDOW_IN_DOCK_TAB_CHAIN (window_b))
+        return -1; /* b > a since b is a dock */
+      else
+        return 0;
+    }
+}
+
+static void
+shuffle_docks_to_end (GList **mru_list_p)
+{
+  *mru_list_p = g_list_sort (*mru_list_p,
+                             docks_at_end_cmp);
+}
+
+gboolean
+meta_window_notify_focus (MetaWindow *window,
+                          XEvent     *event)
+{
+  /* note the event can be on either the window or the frame,
+   * we focus the frame for shaded windows
+   */
+  
+  /* The event can be FocusIn, FocusOut, or UnmapNotify.
+   * On UnmapNotify we have to pretend it's focus out,
+   * because we won't get a focus out if it occurs, apparently.
+   */
+
+  /* We ignore grabs, though this is questionable.
+   * It may be better to increase the intelligence of
+   * the focus window tracking.
+   *
+   * The problem is that keybindings for windows are done with
+   * XGrabKey, which means focus_window disappears and the front of
+   * the MRU list gets confused from what the user expects once a
+   * keybinding is used.
+   */  
+  meta_topic (META_DEBUG_FOCUS,
+              "Focus %s event received on %s 0x%lx (%s) "
+              "mode %s detail %s\n",
+              event->type == FocusIn ? "in" :
+              event->type == FocusOut ? "out" :
+              event->type == UnmapNotify ? "unmap" :
+              "???",
+              window->desc, event->xany.window,
+              event->xany.window == window->xwindow ?
+              "client window" :
+              (window->frame && event->xany.window == window->frame->xwindow) ?
+              "frame window" :
+              "unknown window",
+              event->type != UnmapNotify ?
+              meta_event_mode_to_string (event->xfocus.mode) : "n/a",
+              event->type != UnmapNotify ?
+              meta_event_detail_to_string (event->xfocus.detail) : "n/a");
+
+  /* FIXME our pointer tracking is broken; see how
+   * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
+   * handle it for the correct way.  In brief you need to track
+   * pointer focus and regular focus, and handle EnterNotify in
+   * PointerRoot mode with no window manager.  However as noted above,
+   * accurate focus tracking will break things because we want to keep
+   * windows "focused" when using keybindings on them, and also we
+   * sometimes "focus" a window by focusing its frame or
+   * no_focus_window; so this all needs rethinking massively.
+   *
+   * My suggestion is to change it so that we clearly separate
+   * actual keyboard focus tracking using the xterm algorithm,
+   * and metacity's "pretend" focus window, and go through all
+   * the code and decide which one should be used in each place;
+   * a hard bit is deciding on a policy for that.
+   *
+   * http://bugzilla.gnome.org/show_bug.cgi?id=90382
+   */
+  
+  if ((event->type == FocusIn ||
+       event->type == FocusOut) &&
+      (event->xfocus.mode == NotifyGrab ||
+       event->xfocus.mode == NotifyUngrab ||
+       /* From WindowMaker, ignore all funky pointer root events */
+       event->xfocus.detail > NotifyNonlinearVirtual))
+    {
+      meta_topic (META_DEBUG_FOCUS,
+                  "Ignoring focus event generated by a grab or other weirdness\n");
+      return TRUE;
+    }
+    
+  if (event->type == FocusIn)
+    {
+      if (window->display->expected_focus_window == window)
+        window->display->expected_focus_window = NULL;
+
+      if (window != window->display->focus_window)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "* Focus --> %s\n", window->desc);
+          window->display->focus_window = window;
+          window->has_focus = TRUE;
+
+          /* Move to the front of the focusing workspace's MRU list.
+           * We should only be "removing" it from the MRU list if it's
+           * not already there.  Note that it's possible that we might
+           * be processing this FocusIn after we've changed to a
+           * different workspace; we should therefore update the MRU
+           * list only if the window is actually on the active
+           * workspace.
+           */
+          if (window->screen->active_workspace &&
+              meta_window_visible_on_workspace (window, 
+                                                window->screen->active_workspace))
+            {
+              GList* link;
+              link = g_list_find (window->screen->active_workspace->mru_list, 
+                                  window);
+              g_assert (link);
+
+              window->screen->active_workspace->mru_list = 
+                g_list_remove_link (window->screen->active_workspace->mru_list,
+                                    link);
+              g_list_free (link);
+
+              window->screen->active_workspace->mru_list = 
+                g_list_prepend (window->screen->active_workspace->mru_list, 
+                                window);
+              if (!is_in_dock_group (window))
+                shuffle_docks_to_end (&window->screen->active_workspace->mru_list);
+            }
+
+          if (window->frame)
+            meta_frame_queue_draw (window->frame);
+          
+          meta_error_trap_push (window->display);
+          XInstallColormap (window->display->xdisplay,
+                            window->colormap);
+          meta_error_trap_pop (window->display, FALSE);
+
+          /* move into FOCUSED_WINDOW layer */
+          meta_window_update_layer (window);
+
+          /* Ungrab click to focus button since the sync grab can interfere
+           * with some things you might do inside the focused window, by
+           * causing the client to get funky enter/leave events.
+           */
+          if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
+            meta_display_ungrab_focus_window_button (window->display, window);
+        }
+    }
+  else if (event->type == FocusOut ||
+           event->type == UnmapNotify)
+    {
+      if (event->type == FocusOut &&
+          event->xfocus.detail == NotifyInferior)
+        {
+          /* This event means the client moved focus to a subwindow */
+          meta_topic (META_DEBUG_FOCUS,
+                      "Ignoring focus out on %s with NotifyInferior\n",
+                      window->desc);
+          return TRUE;
+        }
+      
+      if (window == window->display->focus_window)
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "%s is now the previous focus window due to being focused out or unmapped\n",
+                      window->desc);
+
+          meta_topic (META_DEBUG_FOCUS,
+                      "* Focus --> NULL (was %s)\n", window->desc);
+          
+          window->display->focus_window = NULL;
+          window->has_focus = FALSE;
+          if (window->frame)
+            meta_frame_queue_draw (window->frame);
+
+          meta_error_trap_push (window->display);
+          XUninstallColormap (window->display->xdisplay,
+                              window->colormap);
+          meta_error_trap_pop (window->display, FALSE);
+
+          /* move out of FOCUSED_WINDOW layer */
+          meta_window_update_layer (window);
+
+          /* Re-grab for click to focus, if necessary */
+          if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
+            meta_display_grab_focus_window_button (window->display, window);
+       }
+    }
+
+  /* Now set _NET_ACTIVE_WINDOW hint */
+  meta_display_update_active_window_hint (window->display);
+  
+  return FALSE;
+}
+
+static gboolean
+process_property_notify (MetaWindow     *window,
+                         XPropertyEvent *event)
+{
+  /* FIXME once we move entirely to the window-props.h framework, we
+   * can just call reload on the property in the event and get rid of
+   * this if-else chain.
+   */
+  
+  if (event->atom == XA_WM_NAME)
+    {
+      meta_verbose ("Property notify on %s for WM_NAME\n", window->desc);
+
+      /* don't bother reloading WM_NAME if using _NET_WM_NAME already */
+      if (!window->using_net_wm_name)
+        meta_window_reload_property (window, XA_WM_NAME);
+    }
+  else if (event->atom == window->display->atom_net_wm_name)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_NAME\n", window->desc);
+      meta_window_reload_property (window, window->display->atom_net_wm_name);
+      
+      /* if _NET_WM_NAME was unset, reload WM_NAME */
+      if (!window->using_net_wm_name)
+        meta_window_reload_property (window, XA_WM_NAME);      
+    }
+  else if (event->atom == XA_WM_ICON_NAME)
+    {
+      meta_verbose ("Property notify on %s for WM_ICON_NAME\n", window->desc);
+
+      /* don't bother reloading WM_ICON_NAME if using _NET_WM_ICON_NAME already */
+      if (!window->using_net_wm_icon_name)
+        meta_window_reload_property (window, XA_WM_ICON_NAME);
+    }
+  else if (event->atom == window->display->atom_net_wm_icon_name)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_ICON_NAME\n", window->desc);
+      meta_window_reload_property (window, window->display->atom_net_wm_icon_name);
+      
+      /* if _NET_WM_ICON_NAME was unset, reload WM_ICON_NAME */
+      if (!window->using_net_wm_icon_name)
+        meta_window_reload_property (window, XA_WM_ICON_NAME);
+    }  
+  else if (event->atom == XA_WM_NORMAL_HINTS)
+    {
+      meta_verbose ("Property notify on %s for WM_NORMAL_HINTS\n", window->desc);
+      
+      meta_window_reload_property (window, XA_WM_NORMAL_HINTS);
+      
+      /* See if we need to constrain current size */
+      meta_window_queue_move_resize (window);
+    }
+  else if (event->atom == window->display->atom_wm_protocols)
+    {
+      meta_verbose ("Property notify on %s for WM_PROTOCOLS\n", window->desc);
+      
+      meta_window_reload_property (window, window->display->atom_wm_protocols);
+    }
+  else if (event->atom == XA_WM_HINTS)
+    {
+      meta_verbose ("Property notify on %s for WM_HINTS\n", window->desc);
+
+      meta_window_reload_property (window, XA_WM_HINTS);
+    }
+  else if (event->atom == window->display->atom_motif_wm_hints)
+    {
+      meta_verbose ("Property notify on %s for MOTIF_WM_HINTS\n", window->desc);
+      
+      update_mwm_hints (window);
+      
+      if (window->decorated)
+        meta_window_ensure_frame (window);
+      else
+        meta_window_destroy_frame (window);
+
+      meta_window_queue_move_resize (window);
+      /* because ensure/destroy frame may unmap */
+      meta_window_queue_calc_showing (window);
+    }
+  else if (event->atom == XA_WM_CLASS)
+    {
+      meta_verbose ("Property notify on %s for WM_CLASS\n", window->desc);
+      
+      update_wm_class (window);
+    }
+  else if (event->atom == XA_WM_TRANSIENT_FOR)
+    {
+      meta_verbose ("Property notify on %s for WM_TRANSIENT_FOR\n", window->desc);
+      
+      update_transient_for (window);
+
+      meta_window_queue_move_resize (window);
+    }
+  else if (event->atom ==
+           window->display->atom_wm_window_role)
+    {
+      meta_verbose ("Property notify on %s for WM_WINDOW_ROLE\n", window->desc);
+      
+      update_role (window);
+    }
+  else if (event->atom ==
+           window->display->atom_wm_client_leader ||
+           event->atom ==
+           window->display->atom_sm_client_id)
+    {
+      meta_warning ("Broken client! Window %s changed client leader window or SM client ID\n", window->desc);
+    }
+  else if (event->atom ==
+           window->display->atom_net_wm_window_type)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_WINDOW_TYPE\n", window->desc);
+      update_net_wm_type (window);
+    }
+  else if (event->atom == window->display->atom_net_wm_icon)
+    {
+      meta_verbose ("Property notify on %s for NET_WM_ICON\n", window->desc);
+      meta_icon_cache_property_changed (&window->icon_cache,
+                                        window->display,
+                                        event->atom);
+      meta_window_queue_update_icon (window);
+    }
+  else if (event->atom == window->display->atom_kwm_win_icon)
+    {
+      meta_verbose ("Property notify on %s for KWM_WIN_ICON\n", window->desc);
+
+      meta_icon_cache_property_changed (&window->icon_cache,
+                                        window->display,
+                                        event->atom);
+      meta_window_queue_update_icon (window);
+    }
+  else if ((event->atom == window->display->atom_net_wm_strut) ||
+	   (event->atom == window->display->atom_net_wm_strut_partial))
+    {
+      meta_verbose ("Property notify on %s for _NET_WM_STRUT\n", window->desc);
+      meta_window_update_struts (window);
+    }
+  else if (event->atom == window->display->atom_net_startup_id)
+    {
+      meta_verbose ("Property notify on %s for _NET_STARTUP_ID\n", window->desc);
+      
+      meta_window_reload_property (window,
+                                   window->display->atom_net_startup_id);
+    }
+  else if (event->atom == window->display->atom_metacity_update_counter)
+    {
+      meta_verbose ("Property notify on %s for _METACITY_UPDATE_COUNTER\n", window->desc);
+      
+      meta_window_reload_property (window,
+                                   window->display->atom_metacity_update_counter);
+    }
+
+  return TRUE;
+}
+
+static void
+send_configure_notify (MetaWindow *window)
+{
+  XEvent event;
+
+  /* from twm */
+  
+  event.type = ConfigureNotify;
+  event.xconfigure.display = window->display->xdisplay;
+  event.xconfigure.event = window->xwindow;
+  event.xconfigure.window = window->xwindow;
+  event.xconfigure.x = window->rect.x - window->border_width;
+  event.xconfigure.y = window->rect.y - window->border_width;
+  if (window->frame)
+    {
+      /* Need to be in root window coordinates */
+      event.xconfigure.x += window->frame->rect.x;
+      event.xconfigure.y += window->frame->rect.y;
+    }
+  event.xconfigure.width = window->rect.width;
+  event.xconfigure.height = window->rect.height;
+  event.xconfigure.border_width = window->border_width; /* requested not actual */
+  event.xconfigure.above = None; /* FIXME */
+  event.xconfigure.override_redirect = False;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
+              window->desc,
+              event.xconfigure.x, event.xconfigure.y,
+              event.xconfigure.width, event.xconfigure.height);
+  
+  meta_error_trap_push (window->display);
+  XSendEvent (window->display->xdisplay,
+              window->xwindow,
+              False, StructureNotifyMask, &event);
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+static void
+update_net_wm_state (MetaWindow *window)
+{
+  /* We know this is only on initial window creation,
+   * clients don't change the property.
+   */
+
+  int n_atoms;
+  Atom *atoms;
+
+  window->shaded = FALSE;
+  window->maximized = FALSE;
+  window->wm_state_modal = FALSE;
+  window->wm_state_skip_taskbar = FALSE;
+  window->wm_state_skip_pager = FALSE;
+  window->wm_state_above = FALSE;
+  window->wm_state_below = FALSE;
+  
+  if (meta_prop_get_atom_list (window->display, window->xwindow,
+                               window->display->atom_net_wm_state,
+                               &atoms, &n_atoms))
+    {
+      int i;
+      
+      i = 0;
+      while (i < n_atoms)
+        {
+          if (atoms[i] == window->display->atom_net_wm_state_shaded)
+            window->shaded = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_maximized_horz)
+            window->maximize_after_placement = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_maximized_vert)
+            window->maximize_after_placement = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_modal)
+            window->wm_state_modal = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_skip_taskbar)
+            window->wm_state_skip_taskbar = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_skip_pager)
+            window->wm_state_skip_pager = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_fullscreen)
+            window->fullscreen = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_above)
+            window->wm_state_above = TRUE;
+          else if (atoms[i] == window->display->atom_net_wm_state_below)
+            window->wm_state_below = TRUE;
+          
+          ++i;
+        }
+  
+      meta_XFree (atoms);
+    }
+  
+  recalc_window_type (window);
+}
+
+
+static void
+update_mwm_hints (MetaWindow *window)
+{
+  MotifWmHints *hints;
+
+  window->mwm_decorated = TRUE;
+  window->mwm_border_only = FALSE;
+  window->mwm_has_close_func = TRUE;
+  window->mwm_has_minimize_func = TRUE;
+  window->mwm_has_maximize_func = TRUE;
+  window->mwm_has_move_func = TRUE;
+  window->mwm_has_resize_func = TRUE;
+
+  if (!meta_prop_get_motif_hints (window->display, window->xwindow,
+                                  window->display->atom_motif_wm_hints,
+                                  &hints))
+    {
+      meta_verbose ("Window %s has no MWM hints\n", window->desc);
+      return;
+    }
+  
+  /* We support those MWM hints deemed non-stupid */
+
+  meta_verbose ("Window %s has MWM hints\n",
+                window->desc);
+  
+  if (hints->flags & MWM_HINTS_DECORATIONS)
+    {
+      meta_verbose ("Window %s sets MWM_HINTS_DECORATIONS 0x%lx\n",
+                    window->desc, hints->decorations);
+
+      if (hints->decorations == 0)
+        window->mwm_decorated = FALSE;
+      /* some input methods use this */
+      else if (hints->decorations == MWM_DECOR_BORDER)
+        window->mwm_border_only = TRUE;
+    }
+  else
+    meta_verbose ("Decorations flag unset\n");
+  
+  if (hints->flags & MWM_HINTS_FUNCTIONS)
+    {
+      gboolean toggle_value;
+      
+      meta_verbose ("Window %s sets MWM_HINTS_FUNCTIONS 0x%lx\n",
+                    window->desc, hints->functions);
+
+      /* If _ALL is specified, then other flags indicate what to turn off;
+       * if ALL is not specified, flags are what to turn on.
+       * at least, I think so
+       */
+      
+      if ((hints->functions & MWM_FUNC_ALL) == 0)
+        {
+          toggle_value = TRUE;
+
+          meta_verbose ("Window %s disables all funcs then reenables some\n",
+                        window->desc);
+          window->mwm_has_close_func = FALSE;
+          window->mwm_has_minimize_func = FALSE;
+          window->mwm_has_maximize_func = FALSE;
+          window->mwm_has_move_func = FALSE;
+          window->mwm_has_resize_func = FALSE;
+        }
+      else
+        {
+          meta_verbose ("Window %s enables all funcs then disables some\n",
+                        window->desc);
+          toggle_value = FALSE;
+        }
+      
+      if ((hints->functions & MWM_FUNC_CLOSE) != 0)
+        {
+          meta_verbose ("Window %s toggles close via MWM hints\n",
+                        window->desc);
+          window->mwm_has_close_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_MINIMIZE) != 0)
+        {
+          meta_verbose ("Window %s toggles minimize via MWM hints\n",
+                        window->desc);
+          window->mwm_has_minimize_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_MAXIMIZE) != 0)
+        {
+          meta_verbose ("Window %s toggles maximize via MWM hints\n",
+                        window->desc);
+          window->mwm_has_maximize_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_MOVE) != 0)
+        {
+          meta_verbose ("Window %s toggles move via MWM hints\n",
+                        window->desc);
+          window->mwm_has_move_func = toggle_value;
+        }
+      if ((hints->functions & MWM_FUNC_RESIZE) != 0)
+        {
+          meta_verbose ("Window %s toggles resize via MWM hints\n",
+                        window->desc);
+          window->mwm_has_resize_func = toggle_value;
+        }
+    }
+  else
+    meta_verbose ("Functions flag unset\n");
+
+  meta_XFree (hints);
+
+  recalc_window_features (window);
+}
+
+gboolean
+meta_window_get_icon_geometry (MetaWindow    *window,
+                               MetaRectangle *rect)
+{
+  gulong *geometry = NULL;
+  int nitems;
+
+  if (meta_prop_get_cardinal_list (window->display,
+                                   window->xwindow,
+                                   window->display->atom_net_wm_icon_geometry,
+                                   &geometry, &nitems))
+    {
+      if (nitems != 4)
+        {
+          meta_verbose ("_NET_WM_ICON_GEOMETRY on %s has %d values instead of 4\n",
+                        window->desc, nitems);
+          meta_XFree (geometry);
+          return FALSE;
+        }
+  
+      if (rect)
+        {
+          rect->x = geometry[0];
+          rect->y = geometry[1];
+          rect->width = geometry[2];
+          rect->height = geometry[3];
+        }
+
+      meta_XFree (geometry);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+update_wm_class (MetaWindow *window)
+{
+  XClassHint ch;
+  
+  if (window->res_class)
+    g_free (window->res_class);
+  if (window->res_name)
+    g_free (window->res_name);
+
+  window->res_class = NULL;
+  window->res_name = NULL;
+
+  ch.res_name = NULL;
+  ch.res_class = NULL;
+
+  meta_prop_get_class_hint (window->display,
+                            window->xwindow,
+                            XA_WM_CLASS,
+                            &ch);
+
+  if (ch.res_name)
+    {
+      window->res_name = g_strdup (ch.res_name);
+      XFree (ch.res_name);
+    }
+
+  if (ch.res_class)
+    {
+      window->res_class = g_strdup (ch.res_class);
+      XFree (ch.res_class);
+    }
+
+  meta_verbose ("Window %s class: '%s' name: '%s'\n",
+                window->desc,
+                window->res_class ? window->res_class : "none",
+                window->res_name ? window->res_name : "none");
+}
+
+static Window
+read_client_leader (MetaDisplay *display,
+                    Window       xwindow)
+{
+  Window retval = None;
+  
+  meta_prop_get_window (display, xwindow,
+                        display->atom_wm_client_leader,
+                        &retval);
+
+  return retval;
+}
+
+typedef struct
+{
+  Window leader;  
+} ClientLeaderData;
+
+static gboolean
+find_client_leader_func (MetaWindow *ancestor,
+                         void       *data)
+{
+  ClientLeaderData *d;
+
+  d = data;
+
+  d->leader = read_client_leader (ancestor->display,
+                                  ancestor->xwindow);
+
+  /* keep going if no client leader found */
+  return d->leader == None;
+}
+
+static void
+update_sm_hints (MetaWindow *window)
+{
+  Window leader;
+  
+  window->xclient_leader = None;
+  window->sm_client_id = NULL;
+
+  /* If not on the current window, we can get the client
+   * leader from transient parents. If we find a client
+   * leader, we read the SM_CLIENT_ID from it.
+   */
+  leader = read_client_leader (window->display, window->xwindow);
+  if (leader == None)
+    {
+      ClientLeaderData d;
+      d.leader = None;
+      meta_window_foreach_ancestor (window, find_client_leader_func,
+                                    &d);
+      leader = d.leader;
+    }
+      
+  if (leader != None)
+    {
+      char *str;
+      
+      window->xclient_leader = leader;
+
+      if (meta_prop_get_latin1_string (window->display, leader,
+                                       window->display->atom_sm_client_id,
+                                       &str))
+        {
+          window->sm_client_id = g_strdup (str);
+          meta_XFree (str);
+        }
+    }
+  else
+    {
+      meta_verbose ("Didn't find a client leader for %s\n", window->desc);
+
+      if (!meta_prefs_get_disable_workarounds ())
+        {
+          /* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app
+           * instead of the client leader
+           */
+          char *str;
+
+          str = NULL;
+          if (meta_prop_get_latin1_string (window->display, window->xwindow,
+                                           window->display->atom_sm_client_id,
+                                           &str))
+            {
+              if (window->sm_client_id == NULL) /* first time through */
+                meta_warning (_("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n"),
+                              window->desc);
+              
+              window->sm_client_id = g_strdup (str);
+              meta_XFree (str);
+            }
+        }
+    }
+
+  meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n",
+                window->desc, window->xclient_leader,
+                window->sm_client_id ? window->sm_client_id : "none");
+}
+
+static void
+update_role (MetaWindow *window)
+{
+  char *str;
+  
+  if (window->role)
+    g_free (window->role);
+  window->role = NULL;
+
+  if (meta_prop_get_latin1_string (window->display, window->xwindow,
+                                   window->display->atom_wm_window_role,
+                                   &str))
+    {
+      window->role = g_strdup (str);
+      meta_XFree (str);
+    }
+
+  meta_verbose ("Updated role of %s to '%s'\n",
+                window->desc, window->role ? window->role : "null");
+}
+
+static void
+update_transient_for (MetaWindow *window)
+{
+  Window w;
+
+  meta_error_trap_push (window->display);
+  w = None;
+  XGetTransientForHint (window->display->xdisplay,
+                        window->xwindow,
+                        &w);
+  meta_error_trap_pop (window->display, TRUE);
+  window->xtransient_for = w;
+
+  window->transient_parent_is_root_window =
+    window->xtransient_for == window->screen->xroot;
+  
+  if (window->xtransient_for != None)
+    meta_verbose ("Window %s transient for 0x%lx (root = %d)\n", window->desc,
+                  window->xtransient_for, window->transient_parent_is_root_window);
+  else
+    meta_verbose ("Window %s is not transient\n", window->desc);
+  
+  /* may now be a dialog */
+  recalc_window_type (window);
+
+  /* update stacking constraints */
+  meta_stack_update_transient (window->screen->stack, window);
+}
+
+/* some legacy cruft */
+typedef enum
+{
+  WIN_LAYER_DESKTOP     = 0,
+  WIN_LAYER_BELOW       = 2,
+  WIN_LAYER_NORMAL      = 4,
+  WIN_LAYER_ONTOP       = 6,
+  WIN_LAYER_DOCK        = 8,
+  WIN_LAYER_ABOVE_DOCK  = 10
+} GnomeWinLayer;
+
+static void
+update_net_wm_type (MetaWindow *window)
+{
+  int n_atoms;
+  Atom *atoms;
+  int i;
+
+  window->type_atom = None;
+  n_atoms = 0;
+  atoms = NULL;
+  
+  meta_prop_get_atom_list (window->display, window->xwindow, 
+                           window->display->atom_net_wm_window_type,
+                           &atoms, &n_atoms);
+
+  i = 0;
+  while (i < n_atoms)
+    {
+      /* We break as soon as we find one we recognize,
+       * supposed to prefer those near the front of the list
+       */
+      if (atoms[i] == window->display->atom_net_wm_window_type_desktop ||
+          atoms[i] == window->display->atom_net_wm_window_type_dock ||
+          atoms[i] == window->display->atom_net_wm_window_type_toolbar ||
+          atoms[i] == window->display->atom_net_wm_window_type_menu ||
+          atoms[i] == window->display->atom_net_wm_window_type_dialog ||
+          atoms[i] == window->display->atom_net_wm_window_type_normal ||
+          atoms[i] == window->display->atom_net_wm_window_type_utility ||
+          atoms[i] == window->display->atom_net_wm_window_type_splash)
+        {
+          window->type_atom = atoms[i];
+          break;
+        }
+      
+      ++i;
+    }
+  
+  meta_XFree (atoms);
+
+  if (meta_is_verbose ())
+    {
+      char *str;
+
+      str = NULL;
+      if (window->type_atom != None)
+        {
+          meta_error_trap_push (window->display);
+          str = XGetAtomName (window->display->xdisplay, window->type_atom);
+          meta_error_trap_pop (window->display, TRUE);
+        }
+
+      meta_verbose ("Window %s type atom %s\n", window->desc,
+                    str ? str : "(none)");
+
+      if (str)
+        meta_XFree (str);
+    }
+  
+  recalc_window_type (window);
+}
+
+static void
+redraw_icon (MetaWindow *window)
+{
+  /* We could probably be smart and just redraw the icon here,
+   * instead of the whole frame.
+   */
+  if (window->frame && (window->mapped || window->frame->mapped))
+    meta_ui_queue_frame_draw (window->screen->ui, window->frame->xwindow);
+}
+
+static void
+meta_window_update_icon_now (MetaWindow *window)
+{
+  GdkPixbuf *icon;
+  GdkPixbuf *mini_icon;
+
+  icon = NULL;
+  mini_icon = NULL;
+  
+  if (meta_read_icons (window->screen,
+                       window->xwindow,
+                       &window->icon_cache,
+                       window->wm_hints_pixmap,
+                       window->wm_hints_mask,
+                       &icon,
+                       META_ICON_WIDTH, META_ICON_HEIGHT,
+                       &mini_icon,
+                       META_MINI_ICON_WIDTH,
+                       META_MINI_ICON_HEIGHT))
+    {
+      if (window->icon)
+        g_object_unref (G_OBJECT (window->icon));
+      
+      if (window->mini_icon)
+        g_object_unref (G_OBJECT (window->mini_icon));
+      
+      window->icon = icon;
+      window->mini_icon = mini_icon;
+
+      redraw_icon (window);
+    }
+  
+  g_assert (window->icon);
+  g_assert (window->mini_icon);
+}
+
+static guint update_icon_idle = 0;
+static GSList *update_icon_pending = NULL;
+
+static gboolean
+idle_update_icon (gpointer data)
+{
+  GSList *tmp;
+  GSList *copy;
+
+  meta_topic (META_DEBUG_GEOMETRY, "Clearing the update_icon queue\n");
+
+  /* Work with a copy, for reentrancy. The allowed reentrancy isn't
+   * complete; destroying a window while we're in here would result in
+   * badness. But it's OK to queue/unqueue update_icons.
+   */
+  copy = g_slist_copy (update_icon_pending);
+  g_slist_free (update_icon_pending);
+  update_icon_pending = NULL;
+  update_icon_idle = 0;
+
+  destroying_windows_disallowed += 1;
+  
+  tmp = copy;
+  while (tmp != NULL)
+    {
+      MetaWindow *window;
+
+      window = tmp->data;
+
+      meta_window_update_icon_now (window); 
+      window->update_icon_queued = FALSE;
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (copy);
+
+  destroying_windows_disallowed -= 1;
+  
+  return FALSE;
+}
+
+void
+meta_window_unqueue_update_icon (MetaWindow *window)
+{
+  if (!window->update_icon_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Removing %s from the update_icon queue\n",
+              window->desc);
+
+  /* Note that window may not actually be in update_icon_pending
+   * because it may have been in "copy" inside the idle handler
+   */
+  update_icon_pending = g_slist_remove (update_icon_pending, window);
+  window->update_icon_queued = FALSE;
+  
+  if (update_icon_pending == NULL &&
+      update_icon_idle != 0)
+    {
+      g_source_remove (update_icon_idle);
+      update_icon_idle = 0;
+    }
+}
+
+void
+meta_window_flush_update_icon (MetaWindow *window)
+{
+  if (window->update_icon_queued)
+    {
+      meta_window_unqueue_update_icon (window);
+      meta_window_update_icon_now (window);
+    }
+}
+
+void
+meta_window_queue_update_icon (MetaWindow *window)
+{
+  if (window->unmanaging)
+    return;
+
+  if (window->update_icon_queued)
+    return;
+
+  meta_topic (META_DEBUG_GEOMETRY,
+              "Putting %s in the update_icon queue\n",
+              window->desc);
+  
+  window->update_icon_queued = TRUE;
+  
+  if (update_icon_idle == 0)
+    update_icon_idle = g_idle_add (idle_update_icon, NULL);
+  
+  update_icon_pending = g_slist_prepend (update_icon_pending, window);
+}
+
+GList*
+meta_window_get_workspaces (MetaWindow *window)
+{
+  if (window->on_all_workspaces)
+    return window->screen->workspaces;
+  else
+    return window->workspaces;
+}
+
+static void
+invalidate_work_areas (MetaWindow *window)
+{
+  GList *tmp;
+
+  tmp = meta_window_get_workspaces (window);
+  
+  while (tmp != NULL)
+    {
+      meta_workspace_invalidate_work_area (tmp->data);
+      tmp = tmp->next;
+    }
+}
+
+void
+meta_window_update_struts (MetaWindow *window)
+{
+  gulong *struts = NULL;
+  int nitems;
+  gboolean old_has_struts;
+  gboolean new_has_struts;
+
+  MetaRectangle old_left;
+  MetaRectangle old_right;
+  MetaRectangle old_top;
+  MetaRectangle old_bottom;
+
+  MetaRectangle new_left;
+  MetaRectangle new_right;
+  MetaRectangle new_top;
+  MetaRectangle new_bottom;
+
+#define MIN_EMPTY (75)
+  
+  meta_verbose ("Updating struts for %s\n", window->desc);
+  
+  if (window->struts)
+    {
+      old_has_struts = TRUE;
+      old_left = window->struts->left;
+      old_right = window->struts->right;
+      old_top = window->struts->top;
+      old_bottom = window->struts->bottom;
+    }
+  else
+    {
+      old_has_struts = FALSE;
+    }
+
+  new_has_struts = FALSE;
+  new_left.width = 0;
+  new_left.x = 0;
+  new_left.y = 0;
+  new_left.height = window->screen->height;
+
+  new_right.width = 0;
+  new_right.x = window->screen->width;
+  new_right.y = 0;
+  new_right.height = window->screen->height;
+
+  new_top.height = 0;
+  new_top.y = 0;
+  new_top.x = 0;
+  new_top.width = window->screen->width;
+
+  new_bottom.height = 0;
+  new_bottom.y = window->screen->height;
+  new_bottom.x = 0;
+  new_bottom.width = window->screen->width;
+  
+  if (meta_prop_get_cardinal_list (window->display,
+                                   window->xwindow,
+                                   window->display->atom_net_wm_strut_partial,
+                                   &struts, &nitems))
+    {
+      if (nitems != 12)
+        {
+          meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead of 12\n",
+                        window->desc, nitems);
+        }
+      else
+        {
+          new_has_struts = TRUE;
+          new_left.width = MIN ((int)struts[0], 
+                                window->screen->width/2 - MIN_EMPTY);
+          new_right.width = MIN ((int)struts[1], 
+                                 window->screen->width/2 - MIN_EMPTY);
+          new_top.height = MIN ((int)struts[2], 
+                                window->screen->height/2 - MIN_EMPTY);
+          new_bottom.height = MIN ((int)struts[3], 
+                                   window->screen->height/2 - MIN_EMPTY);
+          new_right.x = window->screen->width - 
+            new_right.width;
+          new_bottom.y = window->screen->height - 
+            new_bottom.height;
+          new_left.y = struts[4];
+          new_left.height = struts[5] - new_left.y + 1;
+          new_right.y = struts[6];
+          new_right.height = struts[7] - new_right.y + 1;
+          new_top.x = struts[8];
+          new_top.width = struts[9] - new_top.x + 1;
+          new_bottom.x = struts[10];
+          new_bottom.width = struts[11] - new_bottom.x + 1;
+          
+          meta_verbose ("_NET_WM_STRUT_PARTIAL struts %d %d %d %d for window %s\n",
+                        new_left.width,
+                        new_right.width,
+                        new_top.height,
+                        new_bottom.height,
+                        window->desc);
+          
+        }
+      meta_XFree (struts);
+    }
+  else
+    {
+      meta_verbose ("No _NET_WM_STRUT property for %s\n",
+                    window->desc);
+    }
+
+  if (!new_has_struts)
+    {
+      if (meta_prop_get_cardinal_list (window->display,
+                                       window->xwindow,
+                                       window->display->atom_net_wm_strut,
+                                       &struts, &nitems))
+        {
+          if (nitems != 4)
+            {
+              meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n",
+                            window->desc, nitems);
+            }
+          else
+            {
+              new_has_struts = TRUE;
+              new_left.width = MIN ((int)struts[0], 
+                                    window->screen->width/2 - MIN_EMPTY);
+              new_right.width = MIN ((int)struts[1], 
+                                     window->screen->width/2 - MIN_EMPTY);
+              new_top.height = MIN ((int)struts[2], 
+                                    window->screen->height/2 - MIN_EMPTY);
+              new_bottom.height = MIN ((int)struts[3], 
+                                       window->screen->height/2 - MIN_EMPTY);
+              new_left.x = 0;
+              new_right.x = window->screen->width - 
+                new_right.width;
+              new_top.y = 0;
+              new_bottom.y = window->screen->height -
+                new_bottom.height;
+          
+              meta_verbose ("_NET_WM_STRUT struts %d %d %d %d for window %s\n",
+                            new_left.width,
+                            new_right.width,
+                            new_top.height,
+                            new_bottom.height,
+                            window->desc);
+              
+            }
+          meta_XFree (struts);
+        }
+      else
+        {
+          meta_verbose ("No _NET_WM_STRUT property for %s\n",
+                        window->desc);
+        }
+    }
+ 
+  if (old_has_struts != new_has_struts ||
+      (new_has_struts && old_has_struts &&
+       (!meta_rectangle_equal(&old_left, &new_left) ||
+        !meta_rectangle_equal(&old_right, &new_right) ||
+        !meta_rectangle_equal(&old_top, &new_top) ||
+        !meta_rectangle_equal(&old_bottom, &new_bottom))))
+    {
+      if (new_has_struts)
+        {
+          if (!window->struts)
+            window->struts = g_new (MetaStruts, 1);
+              
+          window->struts->left = new_left;
+          window->struts->right = new_right;
+          window->struts->top = new_top;
+          window->struts->bottom = new_bottom;
+        }
+      else
+        {
+          g_free (window->struts);
+          window->struts = NULL;
+        }
+      meta_topic (META_DEBUG_WORKAREA,
+                  "Invalidating work areas of window %s due to struts update\n",
+                  window->desc);
+      invalidate_work_areas (window);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_WORKAREA,
+                  "Struts on %s were unchanged\n", window->desc);
+    }
+}
+
+static void
+recalc_window_type (MetaWindow *window)
+{
+  MetaWindowType old_type;
+
+  old_type = window->type;
+  
+  if (window->type_atom != None)
+    {
+      if (window->type_atom  == window->display->atom_net_wm_window_type_desktop)
+        window->type = META_WINDOW_DESKTOP;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_dock)
+        window->type = META_WINDOW_DOCK;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_toolbar)
+        window->type = META_WINDOW_TOOLBAR;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_menu)
+        window->type = META_WINDOW_MENU;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_dialog)
+        window->type = META_WINDOW_DIALOG;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_normal)
+        window->type = META_WINDOW_NORMAL;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_utility)
+        window->type = META_WINDOW_UTILITY;
+      else if (window->type_atom  == window->display->atom_net_wm_window_type_splash)
+        window->type = META_WINDOW_SPLASHSCREEN;
+      else
+        meta_bug ("Set a type atom for %s that wasn't handled in recalc_window_type\n",
+                  window->desc);
+    }
+  else if (window->xtransient_for != None)
+    {
+      window->type = META_WINDOW_DIALOG;
+    }
+  else
+    {
+      window->type = META_WINDOW_NORMAL;
+    }
+
+  if (window->type == META_WINDOW_DIALOG &&
+      window->wm_state_modal)
+    window->type = META_WINDOW_MODAL_DIALOG;
+  
+  meta_verbose ("Calculated type %d for %s, old type %d\n",
+                window->type, window->desc, old_type);
+
+  if (old_type != window->type)
+    {
+      recalc_window_features (window);
+      
+      set_net_wm_state (window);
+      
+      /* Update frame */
+      if (window->decorated)
+        meta_window_ensure_frame (window);
+      else
+        meta_window_destroy_frame (window);
+      
+      /* update stacking constraints */
+      meta_window_update_layer (window);
+
+      meta_window_grab_keys (window);
+    }
+}
+
+static void
+set_allowed_actions_hint (MetaWindow *window)
+{
+#define MAX_N_ACTIONS 10
+  unsigned long data[MAX_N_ACTIONS];
+  int i;
+
+  i = 0;
+  if (window->has_move_func)
+    {
+      data[i] = window->display->atom_net_wm_action_move;
+      ++i;
+    }
+  if (window->has_resize_func)
+    {
+      data[i] = window->display->atom_net_wm_action_resize;
+      ++i;
+      data[i] = window->display->atom_net_wm_action_fullscreen;
+      ++i;
+    }
+  if (window->has_minimize_func)
+    {
+      data[i] = window->display->atom_net_wm_action_minimize;
+      ++i;
+    }
+  if (window->has_shade_func)
+    {
+      data[i] = window->display->atom_net_wm_action_shade;
+      ++i;
+    }
+  /* sticky according to EWMH is different from metacity's sticky;
+   * metacity doesn't support EWMH sticky
+   */
+  if (window->has_maximize_func)
+    {
+      data[i] = window->display->atom_net_wm_action_maximize_horz;
+      ++i;
+      data[i] = window->display->atom_net_wm_action_maximize_vert;
+      ++i;
+    }
+  /* We always allow this */
+  data[i] = window->display->atom_net_wm_action_change_desktop;
+  ++i;
+  if (window->has_close_func)
+    {
+      data[i] = window->display->atom_net_wm_action_close;
+      ++i;
+    }
+
+  g_assert (i <= MAX_N_ACTIONS);
+
+  meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i);
+  
+  meta_error_trap_push (window->display);
+  XChangeProperty (window->display->xdisplay, window->xwindow,
+                   window->display->atom_net_wm_allowed_actions,
+                   XA_ATOM,
+                   32, PropModeReplace, (guchar*) data, i);
+  meta_error_trap_pop (window->display, FALSE);
+#undef MAX_N_ACTIONS
+}
+
+void
+meta_window_recalc_features (MetaWindow *window)
+{
+  recalc_window_features (window);
+}
+
+static void
+recalc_window_features (MetaWindow *window)
+{
+  gboolean old_has_close_func;
+  gboolean old_has_minimize_func;
+  gboolean old_has_move_func;
+  gboolean old_has_resize_func;
+  gboolean old_has_shade_func;
+  gboolean old_always_sticky;
+
+  old_has_close_func = window->has_close_func;
+  old_has_minimize_func = window->has_minimize_func;
+  old_has_move_func = window->has_move_func;
+  old_has_resize_func = window->has_resize_func;
+  old_has_shade_func = window->has_shade_func;
+  old_always_sticky = window->always_sticky;
+
+  /* Use MWM hints initially */
+  window->decorated = window->mwm_decorated;
+  window->border_only = window->mwm_border_only;
+  window->has_close_func = window->mwm_has_close_func;
+  window->has_minimize_func = window->mwm_has_minimize_func;
+  window->has_maximize_func = window->mwm_has_maximize_func;
+  window->has_move_func = window->mwm_has_move_func;
+    
+  window->has_resize_func = TRUE;  
+
+  /* If min_size == max_size, then don't allow resize */
+  if (window->size_hints.min_width == window->size_hints.max_width &&
+      window->size_hints.min_height == window->size_hints.max_height)
+    window->has_resize_func = FALSE;
+  else if (!window->mwm_has_resize_func)
+    {
+      /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the
+       * authoritative source for that info. Some apps such as mplayer or
+       * xine disable resize via MWM but not WM_NORMAL_HINTS, but that
+       * leads to e.g. us not fullscreening their windows.  Apps that set
+       * MWM but not WM_NORMAL_HINTS are basically broken. We complain
+       * about these apps but make them work.
+       */
+      
+      meta_warning (_("Window %s sets an MWM hint indicating it isn't resizable, but sets min size %d x %d and max size %d x %d; this doesn't make much sense.\n"),
+                    window->desc,
+                    window->size_hints.min_width,
+                    window->size_hints.min_height,
+                    window->size_hints.max_width,
+                    window->size_hints.max_height);
+    }
+
+  window->has_shade_func = TRUE;
+  window->has_fullscreen_func = TRUE;
+
+  window->always_sticky = FALSE;
+  
+  /* Semantic category overrides the MWM hints */
+  if (window->type == META_WINDOW_TOOLBAR)
+    window->decorated = FALSE;
+
+  if (window->type == META_WINDOW_DESKTOP ||
+      window->type == META_WINDOW_DOCK)
+    window->always_sticky = TRUE;
+  
+  if (window->type == META_WINDOW_DESKTOP ||
+      window->type == META_WINDOW_DOCK ||
+      window->type == META_WINDOW_SPLASHSCREEN)
+    {
+      window->decorated = FALSE;
+      window->has_close_func = FALSE;
+      window->has_shade_func = FALSE;
+
+      /* FIXME this keeps panels and things from using
+       * NET_WM_MOVERESIZE; the problem is that some
+       * panels (edge panels) have fixed possible locations,
+       * and others ("floating panels") do not.
+       *
+       * Perhaps we should require edge panels to explicitly
+       * disable movement?
+       */
+      window->has_move_func = FALSE;
+      window->has_resize_func = FALSE;
+    }
+  
+  if (window->type != META_WINDOW_NORMAL)
+    {
+      window->has_minimize_func = FALSE;
+      window->has_maximize_func = FALSE;
+      window->has_fullscreen_func = FALSE;
+    }
+
+  if (!window->has_resize_func)
+    {      
+      window->has_maximize_func = FALSE;
+      
+      /* don't allow fullscreen if we can't resize, unless the size
+       * is entire screen size (kind of broken, because we
+       * actually fullscreen to xinerama head size not screen size)
+       */
+      if (window->size_hints.min_width == window->screen->width &&
+          window->size_hints.min_height == window->screen->height &&
+          !window->decorated)
+        ; /* leave fullscreen available */
+      else
+        window->has_fullscreen_func = FALSE;
+    }
+
+  /* We leave fullscreen windows decorated, just push the frame outside
+   * the screen. This avoids flickering to unparent them.
+   *
+   * Note that setting has_resize_func = FALSE here must come after the
+   * above code that may disable fullscreen, because if the window
+   * is not resizable purely due to fullscreen, we don't want to
+   * disable fullscreen mode.
+   */
+  if (window->fullscreen)
+    {
+      window->has_shade_func = FALSE;
+      window->has_move_func = FALSE;
+      window->has_resize_func = FALSE;
+      window->has_maximize_func = FALSE;
+    }
+
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d\n",
+              window->desc,
+              window->fullscreen,
+              window->has_maximize_func, window->has_fullscreen_func,
+              window->size_hints.min_width,
+              window->size_hints.min_height,
+              window->size_hints.max_width,
+              window->size_hints.max_height);
+  
+  /* no shading if not decorated */
+  if (!window->decorated || window->border_only)
+    window->has_shade_func = FALSE;
+
+  window->skip_taskbar = FALSE;
+  window->skip_pager = FALSE;
+
+  if (window->wm_state_skip_taskbar)
+    window->skip_taskbar = TRUE;
+  
+  if (window->wm_state_skip_pager)
+    window->skip_pager = TRUE;
+  
+  switch (window->type)
+    {
+      /* Force skip taskbar/pager on these window types */
+    case META_WINDOW_DESKTOP:
+    case META_WINDOW_DOCK:
+    case META_WINDOW_TOOLBAR:
+    case META_WINDOW_MENU:
+    case META_WINDOW_UTILITY:
+    case META_WINDOW_SPLASHSCREEN:
+      window->skip_taskbar = TRUE;
+      window->skip_pager = TRUE;
+      break;
+
+    case META_WINDOW_DIALOG:
+    case META_WINDOW_MODAL_DIALOG:
+      /* only skip taskbar if we have a real transient parent */
+      if (window->xtransient_for != None &&
+          window->xtransient_for != window->screen->xroot)
+        window->skip_taskbar = TRUE;
+      break;
+      
+    case META_WINDOW_NORMAL:
+      break;
+    }
+
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Window %s decorated = %d border_only = %d has_close = %d has_minimize = %d has_maximize = %d has_move = %d has_shade = %d skip_taskbar = %d skip_pager = %d\n",
+              window->desc,
+              window->decorated,
+              window->border_only,
+              window->has_close_func,
+              window->has_minimize_func,
+              window->has_maximize_func,
+              window->has_move_func,
+              window->has_shade_func,
+              window->skip_taskbar,
+              window->skip_pager);
+  
+  /* FIXME:
+   * Lame workaround for recalc_window_features
+   * being used overzealously. The fix is to
+   * only recalc_window_features when something
+   * has actually changed.
+   */
+  if (old_has_close_func != window->has_close_func       ||
+      old_has_minimize_func != window->has_minimize_func ||
+      old_has_move_func != window->has_move_func         ||
+      old_has_resize_func != window->has_resize_func     ||
+      old_has_shade_func != window->has_shade_func       ||
+      old_always_sticky != window->always_sticky)
+    set_allowed_actions_hint (window);
+    
+  /* FIXME perhaps should ensure if we don't have a shade func,
+   * we aren't shaded, etc.
+   */
+}
+
+static void
+menu_callback (MetaWindowMenu *menu,
+               Display        *xdisplay,
+               Window          client_xwindow,
+               MetaMenuOp      op,
+               int             workspace_index,
+               gpointer        data)
+{
+  MetaDisplay *display;
+  MetaWindow *window;
+  MetaWorkspace *workspace;
+  
+  display = meta_display_for_x_display (xdisplay);
+  window = meta_display_lookup_x_window (display, client_xwindow);
+  workspace = NULL;
+  
+  if (window != NULL) /* window can be NULL */
+    {
+      meta_verbose ("Menu op %d on %s\n", op, window->desc);
+      
+      /* op can be 0 for none */
+      switch (op)
+        {
+        case META_MENU_OP_DELETE:
+          meta_window_delete (window, meta_display_get_current_time (window->display));
+          break;
+
+        case META_MENU_OP_MINIMIZE:
+          meta_window_minimize (window);
+          break;
+
+        case META_MENU_OP_UNMAXIMIZE:
+          meta_window_unmaximize (window);
+          break;
+      
+        case META_MENU_OP_MAXIMIZE:
+          meta_window_maximize (window);
+          break;
+
+        case META_MENU_OP_UNSHADE:
+          meta_window_unshade (window);
+          break;
+      
+        case META_MENU_OP_SHADE:
+          meta_window_shade (window);
+          break;
+      
+        case META_MENU_OP_MOVE_LEFT:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_LEFT);
+          break;
+
+        case META_MENU_OP_MOVE_RIGHT:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_RIGHT);
+          break;
+
+        case META_MENU_OP_MOVE_UP:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_UP);
+          break;
+
+        case META_MENU_OP_MOVE_DOWN:
+          workspace = meta_workspace_get_neighbor (window->screen->active_workspace,
+                                                   META_MOTION_DOWN);
+          break;
+
+        case META_MENU_OP_WORKSPACES:
+          workspace = meta_screen_get_workspace_by_index (window->screen,
+                                                          workspace_index);
+          break;
+
+        case META_MENU_OP_STICK:
+          meta_window_stick (window);
+          break;
+
+        case META_MENU_OP_UNSTICK:
+          meta_window_unstick (window);
+          break;
+
+        case META_MENU_OP_ABOVE:
+          meta_window_make_above (window);
+          break;
+
+        case META_MENU_OP_UNABOVE:
+          meta_window_unmake_above (window);
+          break;
+
+        case META_MENU_OP_MOVE:
+          meta_window_begin_grab_op (window,
+                                     META_GRAB_OP_KEYBOARD_MOVING,
+                                     meta_display_get_current_time (window->display));
+          break;
+
+        case META_MENU_OP_RESIZE:
+          meta_window_begin_grab_op (window,
+                                     META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN,
+                                     meta_display_get_current_time (window->display));
+          break;
+          
+        case 0:
+          /* nothing */
+          break;
+          
+        default:
+          meta_warning (G_STRLOC": Unknown window op\n");
+          break;
+        }
+
+      if (workspace)
+	{
+	  meta_window_change_workspace (window,
+					workspace);
+#if 0
+	  meta_workspace_activate (workspace);
+	  meta_window_raise (window);
+#endif
+	}
+    }
+  else
+    {
+      meta_verbose ("Menu callback on nonexistent window\n");
+    }
+
+  if (display->window_menu == menu)
+    {
+      display->window_menu = NULL;
+      display->window_with_menu = NULL;
+    }
+  
+  meta_ui_window_menu_free (menu);
+}
+
+void
+meta_window_show_menu (MetaWindow *window,
+                       int         root_x,
+                       int         root_y,
+                       int         button,
+                       Time        timestamp)
+{
+  MetaMenuOp ops;
+  MetaMenuOp insensitive;
+  MetaWindowMenu *menu;
+  MetaWorkspaceLayout layout;
+  
+  if (window->display->window_menu)
+    {
+      meta_ui_window_menu_free (window->display->window_menu);
+      window->display->window_menu = NULL;
+      window->display->window_with_menu = NULL;
+    }
+
+  ops = 0;
+  insensitive = 0;
+
+  ops |= (META_MENU_OP_DELETE | META_MENU_OP_WORKSPACES | META_MENU_OP_MINIMIZE | META_MENU_OP_MOVE | META_MENU_OP_RESIZE);
+
+  meta_screen_calc_workspace_layout (window->screen,
+                                     meta_screen_get_n_workspaces (window->screen),
+                                     meta_workspace_index ( window->screen->active_workspace),
+                                     &layout);
+
+  if (!window->on_all_workspaces)
+    {
+      if (layout.current_col > 0)
+        ops |= META_MENU_OP_MOVE_LEFT;
+      if (layout.current_col < layout.cols - 1)
+        ops |= META_MENU_OP_MOVE_RIGHT;
+      if (layout.current_row > 0)
+        ops |= META_MENU_OP_MOVE_UP;
+      if (layout.current_row < layout.rows - 1)
+        ops |= META_MENU_OP_MOVE_DOWN;
+    }
+
+  if (window->maximized)
+    ops |= META_MENU_OP_UNMAXIMIZE;
+  else
+    ops |= META_MENU_OP_MAXIMIZE;
+  
+#if 0
+  if (window->shaded)
+    ops |= META_MENU_OP_UNSHADE;
+  else
+    ops |= META_MENU_OP_SHADE;
+#endif
+
+  if (window->on_all_workspaces)
+    ops |= META_MENU_OP_UNSTICK;
+  else
+    ops |= META_MENU_OP_STICK;
+
+  if (window->wm_state_above)
+    ops |= META_MENU_OP_UNABOVE;
+  else
+    ops |= META_MENU_OP_ABOVE;
+  
+  if (!window->has_maximize_func)
+    insensitive |= META_MENU_OP_UNMAXIMIZE | META_MENU_OP_MAXIMIZE;
+  
+  if (!window->has_minimize_func)
+    insensitive |= META_MENU_OP_MINIMIZE;
+  
+  if (!window->has_close_func)
+    insensitive |= META_MENU_OP_DELETE;
+
+  if (!window->has_shade_func)
+    insensitive |= META_MENU_OP_SHADE | META_MENU_OP_UNSHADE;
+
+  if (!META_WINDOW_ALLOWS_MOVE (window))
+    insensitive |= META_MENU_OP_MOVE;
+
+  if (!META_WINDOW_ALLOWS_RESIZE (window))
+    insensitive |= META_MENU_OP_RESIZE;
+
+   if (window->always_sticky)
+     insensitive |= META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES;
+
+  if ((window->type == META_WINDOW_DESKTOP) ||
+      (window->type == META_WINDOW_DOCK) ||
+      (window->type == META_WINDOW_SPLASHSCREEN))
+    insensitive |= META_MENU_OP_ABOVE | META_MENU_OP_UNABOVE;
+  
+  menu =
+    meta_ui_window_menu_new (window->screen->ui,
+                             window->xwindow,
+                             ops,
+                             insensitive,
+                             meta_window_get_net_wm_desktop (window),
+                             meta_screen_get_n_workspaces (window->screen),
+                             menu_callback,
+                             NULL); 
+
+  window->display->window_menu = menu;
+  window->display->window_with_menu = window;
+  
+  meta_verbose ("Popping up window menu for %s\n", window->desc);
+  
+  meta_ui_window_menu_popup (menu, root_x, root_y, button, timestamp);
+}
+
+static void
+clear_moveresize_time (MetaWindow *window)
+{
+  /* Forces the next update to actually do something */
+  window->display->grab_last_moveresize_time.tv_sec = 0;
+  window->display->grab_last_moveresize_time.tv_usec = 0;
+}
+
+static gboolean
+check_moveresize_frequency (MetaWindow *window)
+{
+  GTimeVal current_time;
+  double elapsed;
+  double max_resizes_per_second;
+  
+  g_get_current_time (&current_time);
+
+  /* use milliseconds, 1000 milliseconds/second */
+  elapsed =
+    ((((double)current_time.tv_sec - window->display->grab_last_moveresize_time.tv_sec) * G_USEC_PER_SEC +
+      (current_time.tv_usec - window->display->grab_last_moveresize_time.tv_usec))) / 1000.0;
+
+#ifdef HAVE_XSYNC
+  if (window->display->grab_update_alarm != None)
+    max_resizes_per_second = 1.0;   /* this is max resizes without
+                                     * getting any alarms; we resize
+                                     * immediately if we get one.
+                                     * i.e. this is a timeout for the
+                                     * client getting stuck.
+                                     */
+  else
+#endif /* HAVE_XSYNC */
+    max_resizes_per_second = 20.0;
+  
+#define EPSILON (1e-6)  
+  if (elapsed >= 0.0 && elapsed < (1000.0 / max_resizes_per_second))
+    {
+      meta_topic (META_DEBUG_RESIZING,
+                  "Delaying move/resize as only %g of %g seconds elapsed\n",
+                  elapsed / 1000.0, 1.0 / max_resizes_per_second);
+      return FALSE;
+    }
+  else if (elapsed < (0.0 - EPSILON)) /* handle clock getting set backward */
+    clear_moveresize_time (window);
+  
+  /* store latest time */
+  window->display->grab_last_moveresize_time = current_time;
+  
+  meta_topic (META_DEBUG_RESIZING,
+              " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n",
+              elapsed / 1000.0, 1.0 / max_resizes_per_second);
+  
+  return TRUE;
+}
+
+static void
+update_move (MetaWindow  *window,
+             unsigned int mask,
+             int          x,
+             int          y)
+{
+  int dx, dy;
+  int new_x, new_y;
+  int shake_threshold;
+  
+  window->display->grab_latest_motion_x = x;
+  window->display->grab_latest_motion_y = y;
+  
+  dx = x - window->display->grab_anchor_root_x;
+  dy = y - window->display->grab_anchor_root_y;
+
+  new_x = window->display->grab_anchor_window_pos.x + dx;
+  new_y = window->display->grab_anchor_window_pos.y + dy;
+
+  meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n",
+                x, y,
+                window->display->grab_anchor_root_x,
+                window->display->grab_anchor_root_y,
+                window->display->grab_anchor_window_pos.x,
+                window->display->grab_anchor_window_pos.y,
+                dx, dy);
+  
+  /* shake loose (unmaximize) maximized window if dragged beyond the threshold
+   * in the Y direction. You can't pull a window loose via X motion.
+   */
+
+#define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6
+  shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
+    DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
+    
+  if (window->maximized && ABS (dy) >= shake_threshold)
+    {
+      double prop;
+
+      /* Shake loose */
+      window->shaken_loose = TRUE;
+                  
+      /* move the unmaximized window to the cursor */
+      prop = 
+        ((double)(x - window->display->grab_initial_window_pos.x)) / 
+        ((double)window->display->grab_initial_window_pos.width);
+
+      window->display->grab_initial_window_pos.x = 
+        x - window->saved_rect.width * prop;
+      window->display->grab_initial_window_pos.y = y;
+
+      if (window->frame)
+        {
+          window->display->grab_initial_window_pos.y += window->frame->child_y / 2;
+        }
+
+      window->saved_rect.x = window->display->grab_initial_window_pos.x;
+      window->saved_rect.y = window->display->grab_initial_window_pos.y;
+      window->display->grab_anchor_root_x = x;
+      window->display->grab_anchor_root_y = y;
+
+      meta_window_unmaximize (window);
+
+      return;
+    }
+  /* remaximize window on an other xinerama monitor if window has
+   * been shaken loose or it is still maximized (then move straight)
+   */
+  else if (window->shaken_loose || window->maximized)
+    {
+      const MetaXineramaScreenInfo *wxinerama;
+      MetaRectangle work_area;
+      int monitor;
+
+      wxinerama = meta_screen_get_xinerama_for_window (window->screen, window);
+
+      for (monitor = 0; monitor < window->screen->n_xinerama_infos; monitor++)
+        {
+          meta_window_get_work_area_for_xinerama (window, monitor, &work_area);
+
+          /* check if cursor is near the top of a xinerama work area */ 
+          if (x >= work_area.x &&
+              x < (work_area.x + work_area.width) &&
+              y >= work_area.y &&
+              y < (work_area.y + shake_threshold))
+            {
+              /* move the saved rect if window will become maximized on an
+               * other monitor so user isn't surprised on a later unmaximize
+               */
+              if (wxinerama->number != monitor)
+                {
+                  window->saved_rect.x = work_area.x;
+                  window->saved_rect.y = work_area.y;
+                  
+                  if (window->frame) 
+                    {
+                      window->saved_rect.x += window->frame->child_x;
+                      window->saved_rect.y += window->frame->child_y;
+                    }
+
+                  meta_window_unmaximize (window);
+                }
+
+              window->display->grab_initial_window_pos = work_area;
+              window->display->grab_anchor_root_x = x;
+              window->display->grab_anchor_root_y = y;
+              window->shaken_loose = FALSE;
+              
+              meta_window_maximize (window);
+
+              return;
+            }
+        }
+    }
+
+  /* don't allow a maximized window to move */
+  if (window->maximized)
+    return;
+
+  if (window->display->grab_wireframe_active)
+    {
+      /* FIXME Horribly broken, does not honor position
+       * constraints
+       */
+      MetaRectangle new_xor;
+      new_xor = window->display->grab_wireframe_rect;
+      new_xor.x = new_x;
+      new_xor.y = new_y;
+
+      meta_effects_update_wireframe (window->screen,
+                                     &window->display->grab_wireframe_rect,
+                                     &new_xor);
+      window->display->grab_wireframe_rect = new_xor;
+    }
+  else
+    {
+      /* FIXME, edge snapping broken in wireframe mode */
+      if (mask & ShiftMask)
+        {
+          /* snap to edges */
+          new_x = meta_window_find_nearest_vertical_edge (window, new_x);
+          new_y = meta_window_find_nearest_horizontal_edge (window, new_y);
+        }
+      
+      meta_window_move (window, TRUE, new_x, new_y);
+    }
+}
+
+static void
+update_resize (MetaWindow *window,
+               int x, int y)
+{
+  int dx, dy;
+  int new_w, new_h;
+  int gravity;
+  MetaRectangle old;
+  int new_x, new_y;
+  
+  window->display->grab_latest_motion_x = x;
+  window->display->grab_latest_motion_y = y;
+  
+  dx = x - window->display->grab_anchor_root_x;
+  dy = y - window->display->grab_anchor_root_y;
+
+  new_w = window->display->grab_anchor_window_pos.width;
+  new_h = window->display->grab_anchor_window_pos.height;
+
+  /* FIXME this is only used in wireframe mode */
+  new_x = window->display->grab_anchor_window_pos.x;
+  new_y = window->display->grab_anchor_window_pos.y;
+  
+  switch (window->display->grab_op)
+    {
+    case META_GRAB_OP_RESIZING_SE:
+    case META_GRAB_OP_RESIZING_NE:
+    case META_GRAB_OP_RESIZING_E:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_E:
+      new_w += dx;
+      break;
+
+    case META_GRAB_OP_RESIZING_NW:
+    case META_GRAB_OP_RESIZING_SW:
+    case META_GRAB_OP_RESIZING_W:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_W:
+      new_w -= dx;
+      new_x += dx;
+      break;
+      
+    default:
+      break;
+    }
+  
+  switch (window->display->grab_op)
+    {
+    case META_GRAB_OP_RESIZING_SE:
+    case META_GRAB_OP_RESIZING_S:
+    case META_GRAB_OP_RESIZING_SW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_S:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+      new_h += dy;
+      break;
+      
+    case META_GRAB_OP_RESIZING_N:
+    case META_GRAB_OP_RESIZING_NE:
+    case META_GRAB_OP_RESIZING_NW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_N:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+      new_h -= dy;
+      new_y += dy;
+      break;
+    default:
+      break;
+    }
+
+  if (!check_moveresize_frequency (window))
+    return;
+  
+  old = window->rect;
+
+  /* compute gravity of client during operation */
+  gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
+  g_assert (gravity >= 0);
+  
+  if (window->display->grab_wireframe_active)
+    {
+      /* FIXME This is crap. For example, the wireframe isn't
+       * constrained in the way that a real resize would be. An
+       * obvious elegant solution is to unmap the window during
+       * wireframe, but still resize it; however, that probably
+       * confuses broken clients that have problems with opaque
+       * resize, they probably don't track their visibility.
+       */
+      MetaRectangle new_xor;
+      
+      if ((new_x + new_w <= new_x) || (new_y + new_h <= new_y))
+        return;
+      
+      new_xor.x = new_x;
+      new_xor.y = new_y;
+      new_xor.width = new_w;
+      new_xor.height = new_h;
+
+      meta_effects_update_wireframe (window->screen,
+                                     &window->display->grab_wireframe_rect,
+                                     &new_xor);
+      window->display->grab_wireframe_rect = new_xor;
+
+      /* do this after drawing the wires, so we don't draw over it */
+      meta_window_refresh_resize_popup (window);
+    }
+  else
+    {
+      meta_window_resize_with_gravity (window, TRUE, new_w, new_h, gravity);
+    }
+
+  /* If we don't actually resize the window, we clear the timestamp,
+   * so we'll quickly try again.  Otherwise you get "stuck" because
+   * the window doesn't increment its _METACITY_UPDATE_COUNTER when
+   * nothing happens.
+   */
+  if (window->rect.width == old.width &&
+      window->rect.height == old.height)
+    clear_moveresize_time (window);
+}
+
+typedef struct
+{
+  const XEvent *current_event;
+  int count;
+  Time last_time;
+} EventScannerData;
+
+static Bool
+find_last_time_predicate (Display  *display,
+                          XEvent   *xevent,
+                          XPointer  arg)
+{
+  EventScannerData *esd = (void*) arg;
+
+  if (esd->current_event->type == xevent->type &&
+      esd->current_event->xany.window == xevent->xany.window)
+    {
+      esd->count += 1;
+      esd->last_time = xevent->xmotion.time;
+    }
+
+  return False;
+}
+
+static gboolean
+check_use_this_motion_notify (MetaWindow *window,
+                              XEvent     *event)
+{
+  EventScannerData esd;
+  XEvent useless;
+
+  /* This code is copied from Owen's GDK code. */
+  
+  if (window->display->grab_motion_notify_time != 0)
+    {
+      /* == is really the right test, but I'm all for paranoia */
+      if (window->display->grab_motion_notify_time <=
+          event->xmotion.time)
+        {
+          meta_topic (META_DEBUG_RESIZING,
+                      "Arrived at event with time %lu (waiting for %lu), using it\n",
+                      (unsigned long) event->xmotion.time,
+                      (unsigned long) window->display->grab_motion_notify_time);
+          window->display->grab_motion_notify_time = 0;
+          return TRUE;
+        }
+      else
+        return FALSE; /* haven't reached the saved timestamp yet */
+    }
+  
+  esd.current_event = event;
+  esd.count = 0;
+  esd.last_time = 0;
+
+  /* "useless" isn't filled in because the predicate never returns True */
+  XCheckIfEvent (window->display->xdisplay,
+                 &useless,
+                 find_last_time_predicate,
+                 (XPointer) &esd);
+
+  if (esd.count > 0)
+    meta_topic (META_DEBUG_RESIZING,
+                "Will skip %d motion events and use the event with time %lu\n",
+                esd.count, (unsigned long) esd.last_time);
+  
+  if (esd.last_time == 0)
+    return TRUE;
+  else
+    {
+      /* Save this timestamp, and ignore all motion notify
+       * until we get to the one with this stamp.
+       */
+      window->display->grab_motion_notify_time = esd.last_time;
+      return FALSE;
+    }
+}
+
+void
+meta_window_handle_mouse_grab_op_event (MetaWindow *window,
+                                        XEvent     *event)
+{
+#ifdef HAVE_XSYNC
+  if (event->type == (window->display->xsync_event_base + XSyncAlarmNotify))
+    {
+      meta_topic (META_DEBUG_RESIZING,
+                  "Alarm event received last motion x = %d y = %d\n",
+                  window->display->grab_latest_motion_x,
+                  window->display->grab_latest_motion_y);
+      
+      /* This means we are ready for another configure. */
+      switch (window->display->grab_op)
+        {
+        case META_GRAB_OP_RESIZING_E:
+        case META_GRAB_OP_RESIZING_W:
+        case META_GRAB_OP_RESIZING_S:
+        case META_GRAB_OP_RESIZING_N:
+        case META_GRAB_OP_RESIZING_SE:
+        case META_GRAB_OP_RESIZING_SW:
+        case META_GRAB_OP_RESIZING_NE:
+        case META_GRAB_OP_RESIZING_NW:
+        case META_GRAB_OP_KEYBOARD_RESIZING_S:
+        case META_GRAB_OP_KEYBOARD_RESIZING_N:
+        case META_GRAB_OP_KEYBOARD_RESIZING_W:
+        case META_GRAB_OP_KEYBOARD_RESIZING_E:
+        case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+        case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+        case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+        case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+          clear_moveresize_time (window); /* force update to do something */
+
+          /* no pointer round trip here, to keep in sync */
+          update_resize (window,
+                         window->display->grab_latest_motion_x,
+                         window->display->grab_latest_motion_y);
+          break;
+          
+        default:
+          break;
+        }
+    }
+#endif /* HAVE_XSYNC */
+  
+  switch (event->type)
+    {
+    case ButtonRelease:      
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          clear_moveresize_time (window);
+          if (event->xbutton.root == window->screen->xroot)
+            update_move (window, event->xbutton.state,
+                         event->xbutton.x_root, event->xbutton.y_root);
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          clear_moveresize_time (window);
+          if (event->xbutton.root == window->screen->xroot)
+            update_resize (window, event->xbutton.x_root, event->xbutton.y_root);
+        }
+
+      meta_display_end_grab_op (window->display, event->xbutton.time);
+      break;    
+
+    case MotionNotify:
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          if (event->xmotion.root == window->screen->xroot)
+            {
+              if (check_use_this_motion_notify (window,
+                                                event))
+                update_move (window,
+                             event->xmotion.state,
+                             event->xmotion.x_root,
+                             event->xmotion.y_root);
+            }
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          if (event->xmotion.root == window->screen->xroot)
+            {
+              if (check_use_this_motion_notify (window,
+                                                event))
+                update_resize (window,
+                               event->xmotion.x_root,
+                               event->xmotion.y_root);
+            }
+        }
+      break;
+
+    case EnterNotify:
+    case LeaveNotify:
+      if (meta_grab_op_is_moving (window->display->grab_op))
+        {
+          if (event->xcrossing.root == window->screen->xroot)
+            update_move (window,
+                         event->xcrossing.state,
+                         event->xcrossing.x_root,
+                         event->xcrossing.y_root);
+        }
+      else if (meta_grab_op_is_resizing (window->display->grab_op))
+        {
+          if (event->xcrossing.root == window->screen->xroot)
+            update_resize (window,
+                           event->xcrossing.x_root,
+                           event->xcrossing.y_root);
+        }
+      break;
+    default:
+      break;
+    }
+}
+
+gboolean
+meta_window_shares_some_workspace (MetaWindow *window,
+                                   MetaWindow *with)
+{
+  GList *tmp;
+  
+  if (window->on_all_workspaces ||
+      with->on_all_workspaces)
+    return TRUE;
+  
+  tmp = window->workspaces;
+  while (tmp != NULL)
+    {
+      if (g_list_find (with->workspaces, tmp->data) != NULL)
+        return TRUE;
+
+      tmp = tmp->next;
+    }
+
+  return FALSE;
+}
+
+void
+meta_window_set_gravity (MetaWindow *window,
+                         int         gravity)
+{
+  XSetWindowAttributes attrs;
+
+  meta_verbose ("Setting gravity of %s to %d\n", window->desc, gravity);
+
+  attrs.win_gravity = gravity;
+  
+  meta_error_trap_push (window->display);
+
+  XChangeWindowAttributes (window->display->xdisplay,
+                           window->xwindow,
+                           CWWinGravity,
+                           &attrs);
+  
+  meta_error_trap_pop (window->display, FALSE);
+}
+
+static void
+get_work_area_xinerama (MetaWindow    *window,
+                        MetaRectangle *area,
+                        int            which_xinerama)
+{
+  MetaRectangle space_area;
+  GList *tmp;  
+  int left_strut;
+  int right_strut;
+  int top_strut;
+  int bottom_strut;  
+  int xinerama_origin_x;
+  int xinerama_origin_y;
+  int xinerama_width;
+  int xinerama_height;
+  
+  g_assert (which_xinerama >= 0);
+
+  xinerama_origin_x = window->screen->xinerama_infos[which_xinerama].x_origin;
+  xinerama_origin_y = window->screen->xinerama_infos[which_xinerama].y_origin;
+  xinerama_width = window->screen->xinerama_infos[which_xinerama].width;
+  xinerama_height = window->screen->xinerama_infos[which_xinerama].height;
+  
+  left_strut = 0;
+  right_strut = 0;
+  top_strut = 0;
+  bottom_strut = 0;
+  
+  tmp = meta_window_get_workspaces (window);  
+  while (tmp != NULL)
+    {
+      meta_workspace_get_work_area_for_xinerama (tmp->data,
+                                                 which_xinerama,
+                                                 &space_area);
+
+      left_strut = MAX (left_strut, space_area.x - xinerama_origin_x);
+      right_strut = MAX (right_strut,
+			 (xinerama_width - 
+			  (space_area.x - xinerama_origin_x) - 
+			  space_area.width));
+      top_strut = MAX (top_strut, space_area.y - xinerama_origin_y);
+      bottom_strut = MAX (bottom_strut,
+			  (xinerama_height - 
+			   (space_area.y - xinerama_origin_y) - 
+			   space_area.height));
+      tmp = tmp->next;
+    }
+  
+  area->x = xinerama_origin_x + left_strut;
+  area->y = xinerama_origin_y + top_strut;
+  area->width = xinerama_width - left_strut - right_strut;
+  area->height = xinerama_height - top_strut - bottom_strut;
+
+  meta_topic (META_DEBUG_WORKAREA,
+              "Window %s xinerama %d has work area %d,%d %d x %d\n",
+              window->desc, which_xinerama,
+              area->x, area->y, area->width, area->height);
+}
+
+void
+meta_window_get_work_area_current_xinerama (MetaWindow    *window,
+					    MetaRectangle *area)
+{
+  const MetaXineramaScreenInfo *xinerama = NULL;
+  xinerama = meta_screen_get_xinerama_for_window (window->screen,
+						  window);
+
+  meta_window_get_work_area_for_xinerama (window,
+                                          xinerama->number,
+                                          area);
+}
+
+void
+meta_window_get_work_area_for_xinerama (MetaWindow    *window,
+					int            which_xinerama,
+					MetaRectangle *area)
+{
+  g_return_if_fail (which_xinerama >= 0);
+  
+  get_work_area_xinerama (window,
+                          area,
+                          which_xinerama);
+}
+
+void
+meta_window_get_work_area_all_xineramas (MetaWindow    *window,
+                                         MetaRectangle *area)
+{
+  MetaRectangle space_area;
+  GList *tmp;  
+  int left_strut;
+  int right_strut;
+  int top_strut;
+  int bottom_strut;  
+  int screen_origin_x;
+  int screen_origin_y;
+  int screen_width;
+  int screen_height;
+
+  screen_origin_x = 0;
+  screen_origin_y = 0;
+  screen_width = window->screen->width;
+  screen_height = window->screen->height;
+  
+  left_strut = 0;
+  right_strut = 0;
+  top_strut = 0;
+  bottom_strut = 0;
+  
+  tmp = meta_window_get_workspaces (window);  
+  while (tmp != NULL)
+    {
+      meta_workspace_get_work_area_all_xineramas (tmp->data,
+                                                  &space_area);
+
+      left_strut = MAX (left_strut, space_area.x - screen_origin_x);
+      right_strut = MAX (right_strut,
+			 (screen_width - 
+			  (space_area.x - screen_origin_x) - 
+			  space_area.width));
+      top_strut = MAX (top_strut, space_area.y - screen_origin_y);
+      bottom_strut = MAX (bottom_strut,
+			  (screen_height - 
+			   (space_area.y - screen_origin_y) - 
+			   space_area.height));      
+      tmp = tmp->next;
+    }
+  
+  area->x = screen_origin_x + left_strut;
+  area->y = screen_origin_y + top_strut;
+  area->width = screen_width - left_strut - right_strut;
+  area->height = screen_height - top_strut - bottom_strut;
+
+  meta_topic (META_DEBUG_WORKAREA,
+              "Window %s has whole-screen work area %d,%d %d x %d\n",
+              window->desc, area->x, area->y, area->width, area->height);
+}
+
+
+gboolean
+meta_window_same_application (MetaWindow *window,
+                              MetaWindow *other_window)
+{
+  return
+    meta_window_get_group (window) ==
+    meta_window_get_group (other_window);
+}
+
+void
+meta_window_refresh_resize_popup (MetaWindow *window)
+{
+  if (window->display->grab_op == META_GRAB_OP_NONE)
+    return;
+
+  if (window->display->grab_window != window)
+    return;
+
+  /* FIXME for now we bail out when doing wireframe, because our
+   * server grab keeps us from being able to redraw the stuff
+   * underneath the resize popup.
+   */
+  if (window->display->grab_wireframe_active)
+    return;
+  
+  switch (window->display->grab_op)
+    {
+    case META_GRAB_OP_RESIZING_SE:
+    case META_GRAB_OP_RESIZING_S:
+    case META_GRAB_OP_RESIZING_SW:
+    case META_GRAB_OP_RESIZING_N:
+    case META_GRAB_OP_RESIZING_NE:
+    case META_GRAB_OP_RESIZING_NW:
+    case META_GRAB_OP_RESIZING_W:
+    case META_GRAB_OP_RESIZING_E:
+    case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+    case META_GRAB_OP_KEYBOARD_RESIZING_S:
+    case META_GRAB_OP_KEYBOARD_RESIZING_N:
+    case META_GRAB_OP_KEYBOARD_RESIZING_W:
+    case META_GRAB_OP_KEYBOARD_RESIZING_E:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+    case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+    case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+      break;
+
+    default:
+      /* Not resizing */
+      return;
+    }
+      
+  if (window->display->grab_resize_popup == NULL)
+    {
+      if (window->size_hints.width_inc > 1 ||
+          window->size_hints.height_inc > 1)
+        window->display->grab_resize_popup =
+          meta_ui_resize_popup_new (window->display->xdisplay,
+                                    window->screen->number);
+    }
+  
+  if (window->display->grab_resize_popup != NULL)
+    {
+      int gravity;
+      int x, y, width, height;
+      MetaFrameGeometry fgeom;
+
+      if (window->frame)
+        meta_frame_calc_geometry (window->frame, &fgeom);
+      else
+        {
+          fgeom.left_width = 0;
+          fgeom.right_width = 0;
+          fgeom.top_height = 0;
+          fgeom.bottom_height = 0;
+        }
+      
+      gravity = meta_resize_gravity_from_grab_op (window->display->grab_op);
+      g_assert (gravity >= 0);
+
+      if (window->display->grab_wireframe_active)
+        {
+          x = window->display->grab_wireframe_rect.x;
+          y = window->display->grab_wireframe_rect.y;
+          width = window->display->grab_wireframe_rect.width;
+          height = window->display->grab_wireframe_rect.height;
+        }
+      else
+        {
+          meta_window_get_position (window, &x, &y);
+          width = window->rect.width;
+          height = window->rect.height;
+        }
+      
+      meta_ui_resize_popup_set (window->display->grab_resize_popup,
+                                gravity,
+                                x, y,
+                                width, height,
+                                window->size_hints.base_width,
+                                window->size_hints.base_height,
+                                window->size_hints.min_width,
+                                window->size_hints.min_height,
+                                window->size_hints.width_inc,
+                                window->size_hints.height_inc,
+                                fgeom.left_width,
+                                fgeom.right_width,
+                                fgeom.top_height,
+                                fgeom.bottom_height);
+
+      meta_ui_resize_popup_set_showing (window->display->grab_resize_popup,
+                                        TRUE);
+    }
+}
+
+void
+meta_window_foreach_transient (MetaWindow            *window,
+                               MetaWindowForeachFunc  func,
+                               void                  *data)
+{
+  GSList *windows;
+  GSList *tmp;
+
+  windows = meta_display_list_windows (window->display);
+
+  tmp = windows;
+  while (tmp != NULL)
+    {
+      MetaWindow *transient = tmp->data;
+      
+      if (meta_window_is_ancestor_of_transient (window, transient))
+        {
+          if (!(* func) (transient, data))
+            break;
+        }
+      
+      tmp = tmp->next;
+    }
+
+  g_slist_free (windows);
+}
+
+void
+meta_window_foreach_ancestor (MetaWindow            *window,
+                              MetaWindowForeachFunc  func,
+                              void                  *data)
+{
+  MetaWindow *w;
+  MetaWindow *tortoise;
+  
+  w = window;
+  tortoise = window;
+  while (TRUE)
+    {
+      if (w->xtransient_for == None ||
+          w->transient_parent_is_root_window)
+        break;
+      
+      w = meta_display_lookup_x_window (w->display, w->xtransient_for);
+      
+      if (w == NULL || w == tortoise)
+        break;
+
+      if (!(* func) (w, data))
+        break;      
+      
+      if (w->xtransient_for == None ||
+          w->transient_parent_is_root_window)
+        break;
+
+      w = meta_display_lookup_x_window (w->display, w->xtransient_for);
+      
+      if (w == NULL || w == tortoise)
+        break;
+
+      if (!(* func) (w, data))
+        break;
+      
+      tortoise = meta_display_lookup_x_window (tortoise->display,
+                                               tortoise->xtransient_for);
+
+      /* "w" should have already covered all ground covered by the
+       * tortoise, so the following must hold.
+       */
+      g_assert (tortoise != NULL);
+      g_assert (tortoise->xtransient_for != None);
+      g_assert (!tortoise->transient_parent_is_root_window);
+    }
+}
+
+typedef struct
+{
+  MetaWindow *ancestor;
+  gboolean found;
+} FindAncestorData;
+
+static gboolean
+find_ancestor_func (MetaWindow *window,
+                    void       *data)
+{
+  FindAncestorData *d = data;
+
+  if (window == d->ancestor)
+    {
+      d->found = TRUE;
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+meta_window_is_ancestor_of_transient (MetaWindow *window,
+                                      MetaWindow *transient)
+{
+  FindAncestorData d;
+
+  d.ancestor = window;
+  d.found = FALSE;
+
+  meta_window_foreach_ancestor (transient, find_ancestor_func, &d);
+
+  return d.found;
+}
+
+/* Warp pointer to location appropriate for grab,
+ * return root coordinates where pointer ended up.
+ */
+static gboolean
+warp_grab_pointer (MetaWindow          *window,
+                   MetaGrabOp           grab_op,
+                   int                 *x,
+                   int                 *y)
+{
+  MetaRectangle rect;
+
+  /* We may not have done begin_grab_op yet, i.e. may not be in a grab
+   */
+  
+  if (window == window->display->grab_window &&
+      window->display->grab_wireframe_active)
+    rect = window->display->grab_wireframe_rect;
+  else
+    {
+      rect = window->rect;
+      meta_window_get_position (window, &rect.x, &rect.y);
+    }
+  
+  switch (grab_op)
+    {
+      case META_GRAB_OP_KEYBOARD_MOVING:
+      case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN:
+        *x = rect.width / 2;
+        *y = rect.height / 2;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_S:
+        *x = rect.width / 2;
+        *y = rect.height;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_N:
+        *x = rect.width / 2;
+        *y = 0;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_W:
+        *x = 0;
+        *y = rect.height / 2;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_E:
+        *x = rect.width;
+        *y = rect.height / 2;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_SE:
+        *x = rect.width;
+        *y = rect.height;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_NE:
+        *x = rect.width;
+        *y = 0;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_SW:
+        *x = 0;
+        *y = rect.height;
+        break;
+
+      case META_GRAB_OP_KEYBOARD_RESIZING_NW:
+        *x = 0;
+        *y = 0;
+        break;
+
+      default:
+        return FALSE;
+    }
+
+  *x += rect.x;
+  *y += rect.y;
+  
+  meta_error_trap_push_with_return (window->display);
+
+  meta_topic (META_DEBUG_WINDOW_OPS,
+              "Warping pointer to %d,%d with window at %d,%d\n",
+              *x, *y, rect.x, rect.y);
+  
+  XWarpPointer (window->display->xdisplay,
+                None,
+                window->screen->xroot,
+                0, 0, 0, 0, 
+                *x, *y);
+
+  if (meta_error_trap_pop_with_return (window->display, FALSE) != Success)
+    {
+      meta_verbose ("Failed to warp pointer for window %s\n",
+                    window->desc);
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+void
+meta_window_begin_grab_op (MetaWindow *window,
+                           MetaGrabOp  op,
+                           Time        timestamp)
+{
+  int x, y;
+  gulong grab_start_serial;
+
+  grab_start_serial = XNextRequest (window->display->xdisplay);
+  
+  meta_window_raise (window);
+
+  warp_grab_pointer (window,
+                     op, &x, &y);
+
+  meta_display_begin_grab_op (window->display,
+                              window->screen,
+                              window,
+                              op,
+                              FALSE,
+                              grab_start_serial /* event_serial */,
+                              0 /* button */,
+                              0,
+                              timestamp,
+                              x, y);
+
+  /* We override the one set in display_begin_grab_op since we
+   * did additional stuff as part of the grabbing process
+   */
+  window->display->grab_start_serial = grab_start_serial;
+}
+
+void
+meta_window_update_keyboard_resize (MetaWindow *window,
+                                    gboolean    update_cursor)
+{
+  int x, y;
+  
+  warp_grab_pointer (window,
+                     window->display->grab_op,
+                     &x, &y);
+
+  {
+    /* As we warped the pointer, we have to reset the anchor state,
+     * since if the mouse moves we want to use those events to do the
+     * right thing. Also, this means that the motion notify
+     * from the pointer warp comes back as a no-op.
+     */
+    int dx, dy;
+
+    dx = x - window->display->grab_anchor_root_x;
+    dy = y - window->display->grab_anchor_root_y;
+    
+    window->display->grab_anchor_root_x += dx;
+    window->display->grab_anchor_root_y += dy;
+    if (window->display->grab_wireframe_active)
+      {
+        window->display->grab_anchor_window_pos =
+          window->display->grab_wireframe_rect;
+      }
+    else
+      {
+        window->display->grab_anchor_window_pos = window->rect;
+        meta_window_get_position (window,
+                                  &window->display->grab_anchor_window_pos.x,
+                                  &window->display->grab_anchor_window_pos.y);
+      }
+  }
+  
+  if (update_cursor)
+    {
+      meta_display_set_grab_op_cursor (window->display,
+                                       NULL,
+                                       window->display->grab_op,
+                                       TRUE,
+                                       window->display->grab_xwindow,
+                                       meta_display_get_current_time (window->display));
+    }
+}
+
+void
+meta_window_update_keyboard_move (MetaWindow *window)
+{
+  int x, y;
+  
+  warp_grab_pointer (window,
+                     window->display->grab_op,
+                     &x, &y);
+}
+
+void
+meta_window_update_layer (MetaWindow *window)
+{
+  MetaGroup *group;
+  
+  meta_stack_freeze (window->screen->stack);
+  group = meta_window_get_group (window);
+  if (group)
+    meta_group_update_layers (group);
+  else
+    meta_stack_update_layer (window->screen->stack, window);
+  meta_stack_thaw (window->screen->stack);
+}
diff -r -N -u metacity-2.8.1/src/window.h metacity-2.8.1-new/src/window.h
--- metacity-2.8.1/src/window.h	2004-04-14 14:58:08.000000000 -0400
+++ metacity-2.8.1-new/src/window.h	2004-09-10 22:35:46.467266862 -0400
@@ -94,6 +94,11 @@
   char *wm_client_machine;
   char *startup_id;
 
+  /* Needed for Window History Tracking */
+  char *orig_name;
+  char *save_id;
+  int num;
+
   int net_wm_pid;
   
   Window xtransient_for;
@@ -320,11 +325,15 @@
 void        meta_window_calc_showing       (MetaWindow  *window);
 void        meta_window_queue_calc_showing (MetaWindow  *window);
 void        meta_window_minimize           (MetaWindow  *window);
+void        meta_window_minimize_internal  (MetaWindow  *window);
 void        meta_window_unminimize         (MetaWindow  *window);
+void        meta_window_unminimize_internal(MetaWindow  *window);
 void        meta_window_maximize           (MetaWindow  *window);
+void        meta_window_maximize_real      (MetaWindow  *window);
 void        meta_window_maximize_internal  (MetaWindow    *window,
                                             MetaRectangle *saved_rect);
 void        meta_window_unmaximize         (MetaWindow  *window);
+void        meta_window_unmaximize_internal(MetaWindow  *window);
 void        meta_window_make_above         (MetaWindow  *window);
 void        meta_window_unmake_above       (MetaWindow  *window);
 void        meta_window_shade              (MetaWindow  *window);
@@ -332,12 +341,16 @@
 void        meta_window_change_workspace   (MetaWindow  *window,
                                             MetaWorkspace *workspace);
 void        meta_window_stick              (MetaWindow  *window);
+void        meta_window_stick_internal     (MetaWindow  *window);
 void        meta_window_unstick            (MetaWindow  *window);
+void 	     meta_window_unstick_internal   (MetaWindow  *window);
 
 void        meta_window_activate           (MetaWindow  *window,
                                             guint32      current_time);
 void        meta_window_make_fullscreen    (MetaWindow  *window);
+void        meta_window_make_fullscreen_internal(MetaWindow  *window);
 void        meta_window_unmake_fullscreen  (MetaWindow  *window);
+void        meta_window_unmake_fullscreen_internal(MetaWindow  *window);
 
 /* args to move are window pos, not frame pos */
 void        meta_window_move               (MetaWindow  *window,
@@ -485,6 +498,12 @@
                                          gboolean    update_cursor);
 void meta_window_update_keyboard_move   (MetaWindow *window);
 
+const char* window_type_to_string      (MetaWindowType  type);
+MetaWindowType window_type_from_string (const char     *str);
+
+const char* window_gravity_to_string (int         gravity);
+int window_gravity_from_string       (const char *str);
+
 void meta_window_update_layer (MetaWindow *window);
 
 gboolean meta_window_get_icon_geometry (MetaWindow    *window,
