diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/Makefile.am /home/xkahn/src/cvs/metacity-2.4.8/src/Makefile.am
--- /tmp/metacity-2.4.8/src/Makefile.am	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/Makefile.am	Thu Dec 19 15:38:53 2002
@@ -58,6 +58,8 @@
 	resizepopup.h				\
 	screen.c				\
 	screen.h				\
+	history.c				\
+	history.h				\
 	session.c				\
 	session.h				\
 	stack.c					\
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/display.c /home/xkahn/src/cvs/metacity-2.4.8/src/display.c
--- /tmp/metacity-2.4.8/src/display.c	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/display.c	Thu Dec 19 15:38:53 2002
@@ -33,6 +33,7 @@
 #include "prefs.h"
 #include "resizepopup.h"
 #include "workspace.h"
+#include "history.h"
 #include <X11/Xatom.h>
 #include <X11/cursorfont.h>
 #ifdef HAVE_SOLARIS_XINERAMA
@@ -253,7 +254,8 @@
     "_NET_WM_STATE_ABOVE",
     "_NET_WM_STATE_BELOW",
     "_NET_STARTUP_ID",
-    "_METACITY_TOGGLE_VERBOSE"
+    "_METACITY_TOGGLE_VERBOSE",
+    "_NET_WM_GEOMETRY_SAVE_ID"
   };
   Atom atoms[G_N_ELEMENTS(atom_names)];
   
@@ -389,6 +391,7 @@
   display->atom_net_wm_state_below = atoms[76];
   display->atom_net_startup_id = atoms[77];
   display->atom_metacity_toggle_verbose = atoms[78];
+  display->atom_net_wm_geometry_save_id = atoms[79];
   
   display->prop_hooks = NULL;
   meta_display_init_window_prop_hooks (display);
@@ -2602,6 +2605,8 @@
       meta_ui_resize_popup_free (display->grab_resize_popup);
       display->grab_resize_popup = NULL;
     }
+  
+  meta_history_commit_window ();
 }
 
 static void
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/display.h /home/xkahn/src/cvs/metacity-2.4.8/src/display.h
--- /tmp/metacity-2.4.8/src/display.h	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/display.h	Thu Dec 19 15:38:53 2002
@@ -162,6 +162,7 @@
   Atom atom_net_wm_state_below;
   Atom atom_net_startup_id;
   Atom atom_metacity_toggle_verbose;
+  Atom atom_net_wm_geometry_save_id;
   
   /* This is the actual window from focus events,
    * not the one we last set
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/history.c /home/xkahn/src/cvs/metacity-2.4.8/src/history.c
--- /tmp/metacity-2.4.8/src/history.c	Wed Dec 31 19:00:00 1969
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/history.c	Tue Jan  7 17:32:10 2003
@@ -0,0 +1,1026 @@
+/* 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)
+{
+  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->type        = window->type;
+    win->rect.x      = window->user_rect.x;
+    win->num         = window->num; 
+    win->rect.y      = window->user_rect.y;
+  win->rect.width  = window->user_rect.width;
+  win->rect.height = window->user_rect.height;
+  */
+  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);
+
+  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);
+}
+
+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, "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 ....>
+   *     <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;
+  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.)
+   */
+  
+  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"
+	      "  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->updated, window->type, window->rect.x, window->rect.y, 
+	      window->rect.width, window->rect.height);
+}
+
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/history.h /home/xkahn/src/cvs/metacity-2.4.8/src/history.h
--- /tmp/metacity-2.4.8/src/history.h	Wed Dec 31 19:00:00 1969
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/history.h	Mon Dec 23 17:22:02 2002
@@ -0,0 +1,85 @@
+/* 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;
+
+  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 -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/main.c /home/xkahn/src/cvs/metacity-2.4.8/src/main.c
--- /tmp/metacity-2.4.8/src/main.c	Sat Oct 26 23:03:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/main.c	Thu Dec 19 15:38:53 2002
@@ -25,6 +25,7 @@
 #include "display.h"
 #include "errors.h"
 #include "ui.h"
+#include "history.h"
 #include "session.h"
 #include "prefs.h"
 
@@ -278,6 +279,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 ();
 
@@ -356,6 +360,7 @@
   }
 
   meta_session_shutdown ();
+  meta_history_shutdown ();
   
   if (meta_restart_after_quit)
     {
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/place.c /home/xkahn/src/cvs/metacity-2.4.8/src/place.c
--- /tmp/metacity-2.4.8/src/place.c	Tue Oct  1 22:42:24 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/place.c	Mon Dec 23 17:22:02 2002
@@ -22,6 +22,7 @@
 #include <config.h>
 
 #include "place.h"
+#include "history.h"
 #include "workspace.h"
 #include "prefs.h"
 #include <gdk/gdkregion.h>
@@ -422,6 +423,97 @@
     ((rect->y + rect->height) < (xsi->y_origin + xsi->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.
  *
@@ -807,7 +899,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)
    */
@@ -822,8 +914,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);
 
@@ -838,6 +929,10 @@
   x = xi->x_origin;
   y = xi->y_origin;
 
+  /* 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, x, y, &x, &y))
     goto done;
   
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/screen.c /home/xkahn/src/cvs/metacity-2.4.8/src/screen.c
--- /tmp/metacity-2.4.8/src/screen.c	Sat Nov 30 22:58:04 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/screen.c	Thu Dec 19 15:38:53 2002
@@ -90,7 +90,7 @@
 static int
 set_supported_hint (MetaScreen *screen)
 {
-#define N_SUPPORTED 45
+#define N_SUPPORTED 46
 #define N_WIN_SUPPORTED 1
   Atom atoms[N_SUPPORTED];
   
@@ -139,6 +139,7 @@
   atoms[42] = screen->display->atom_net_wm_state_above;
   atoms[43] = screen->display->atom_net_wm_state_below;
   atoms[44] = screen->display->atom_net_startup_id;
+  atoms[45] = screen->display->atom_net_wm_geometry_save_id;
   
   XChangeProperty (screen->display->xdisplay, screen->xroot,
                    screen->display->atom_net_supported,
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/session.c /home/xkahn/src/cvs/metacity-2.4.8/src/session.c
--- /tmp/metacity-2.4.8/src/session.c	Wed Oct 16 16:12:24 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/session.c	Thu Dec 19 15:38:53 2002
@@ -662,182 +662,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)
@@ -1538,18 +1362,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 -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/util.c /home/xkahn/src/cvs/metacity-2.4.8/src/util.c
--- /tmp/metacity-2.4.8/src/util.c	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/util.c	Thu Dec 19 15:38:53 2002
@@ -280,6 +280,8 @@
       return "PREFS";
     case META_DEBUG_GROUPS:
       return "GROUPS";
+    case META_DEBUG_HISTORY:
+      return "HISTORY";
     }
 
   return "Window manager";
@@ -443,3 +445,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 -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/util.h /home/xkahn/src/cvs/metacity-2.4.8/src/util.h
--- /tmp/metacity-2.4.8/src/util.h	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/util.h	Thu Dec 19 15:38:53 2002
@@ -64,7 +64,8 @@
   META_DEBUG_ERRORS       = 1 << 14,
   META_DEBUG_STARTUP      = 1 << 15,
   META_DEBUG_PREFS        = 1 << 16,
-  META_DEBUG_GROUPS       = 1 << 17
+  META_DEBUG_GROUPS       = 1 << 17,
+  META_DEBUG_HISTORY      = 1 << 18
 
 } MetaDebugTopic;
 
@@ -79,6 +80,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 -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/window.c /home/xkahn/src/cvs/metacity-2.4.8/src/window.c
--- /tmp/metacity-2.4.8/src/window.c	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/window.c	Tue Jan  7 17:24:57 2003
@@ -30,6 +30,7 @@
 #include "keybindings.h"
 #include "ui.h"
 #include "place.h"
+#include "history.h"
 #include "session.h"
 #include "effects.h"
 #include "prefs.h"
@@ -86,6 +87,7 @@
 static void     update_icon               (MetaWindow     *window);
 static void     redraw_icon               (MetaWindow     *window); 
 static void     update_struts             (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     recalc_do_not_cover_struts(MetaWindow     *window);
@@ -403,6 +405,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;
@@ -461,6 +468,7 @@
   update_role (window);
   update_net_wm_type (window);
   update_icon (window);
+  update_geometry_save_id (window);
   
   if (window->initially_iconic)
     {
@@ -814,6 +822,8 @@
   
   window->unmanaging = TRUE;
 
+  meta_history_forget_window ();
+
   if (window->fullscreen)
     {
       MetaGroup *group;
@@ -985,6 +995,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);
@@ -1679,6 +1690,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;
@@ -1707,6 +1726,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->screen->showing_desktop)
     meta_screen_unshow_desktop (window->screen);
   
@@ -1739,6 +1766,14 @@
 void
 meta_window_maximize (MetaWindow  *window)
 {
+  meta_window_maximize_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_maximize_internal (MetaWindow  *window)
+{
   if (!window->maximized)
     {
       meta_topic (META_DEBUG_WINDOW_OPS,
@@ -1765,6 +1800,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,
@@ -1783,10 +1826,17 @@
     }
 }
 
-
 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,
@@ -1816,6 +1866,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,
@@ -2501,12 +2559,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
     {
@@ -2552,6 +2616,7 @@
                                     user_op ? META_USER_MOVE_RESIZE : 0,
                                     NorthWestGravity,
                                     x, y, w, h);
+
 }
 
 void
@@ -3125,6 +3190,14 @@
 void
 meta_window_stick (MetaWindow  *window)
 {
+  meta_window_stick_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_stick_internal (MetaWindow  *window)
+{
   meta_verbose ("Sticking window %s current on_all_workspaces = %d\n",
                 window->desc, window->on_all_workspaces);
   
@@ -3145,6 +3218,14 @@
 void
 meta_window_unstick (MetaWindow  *window)
 {
+  meta_window_unstick_internal (window);
+
+  meta_history_update_inplace (window);
+}
+
+void
+meta_window_unstick_internal (MetaWindow  *window)
+{
   if (!window->on_all_workspaces)
     return;
 
@@ -4138,6 +4219,12 @@
       meta_window_reload_property (window,
                                    window->display->atom_net_startup_id);
     }
+  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;
 }
@@ -4693,6 +4780,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)
@@ -4753,6 +4868,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);
     }
 
@@ -6724,6 +6845,136 @@
     }
 }
 
+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 -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/window.h /home/xkahn/src/cvs/metacity-2.4.8/src/window.h
--- /tmp/metacity-2.4.8/src/window.h	Mon Dec  9 12:57:31 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/window.h	Thu Jan  2 18:27:39 2003
@@ -81,6 +81,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;
@@ -286,20 +291,28 @@
 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_internal  (MetaWindow  *window);
 void        meta_window_unmaximize         (MetaWindow  *window);
+void        meta_window_unmaximize_internal(MetaWindow  *window);
 void        meta_window_shade              (MetaWindow  *window);
 void        meta_window_unshade            (MetaWindow  *window);
 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,
@@ -438,6 +451,12 @@
 void meta_window_update_resize_grab_op (MetaWindow *window,
                                         gboolean    update_cursor);
 
+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,
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/xprops.c /home/xkahn/src/cvs/metacity-2.4.8/src/xprops.c
--- /tmp/metacity-2.4.8/src/xprops.c	Sat Nov 30 22:58:04 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/xprops.c	Tue Jan  7 17:24:53 2003
@@ -171,7 +171,10 @@
       results->type == None)
     {
       if (results->prop)
+	{
         XFree (results->prop);
+	  results->prop = NULL;
+	}
       meta_error_trap_pop_with_return (display, TRUE);
       return FALSE;
     }
@@ -179,7 +182,10 @@
   if (meta_error_trap_pop_with_return (display, TRUE) != Success)
     {
       if (results->prop)
+	{
         XFree (results->prop);
+	  results->prop = NULL;
+	}
       return FALSE;
     }
 
@@ -301,6 +307,38 @@
   return TRUE;
 }
 
+gboolean
+meta_prop_get_save_id (MetaDisplay   *display,
+		       Window         xwindow,
+		       Atom           xatom,
+		       char         **save_id)
+{
+  GetPropertyResults results;
+
+  if (!get_property (display, xwindow, xatom, AnyPropertyType,
+		     &results))
+    {
+      meta_verbose ("History Geometry Save ID wasn't set on this window.\n");
+      return FALSE;
+    }
+  if (results.type == None || results.n_items <= 0)
+    {
+      meta_verbose ("History Geometry Save ID was set to NULL.  We need to forget this window's state.\n");
+      *save_id = NULL;
+      if (results.prop)
+	{
+	  XFree (results.prop);
+	  results.prop = NULL;
+	}
+      return TRUE;
+    }
+
+  *save_id = g_strdup (results.prop);
+  XFree (results.prop);
+
+  return TRUE;
+}
+
 gboolean
 meta_prop_get_motif_hints (MetaDisplay   *display,
                            Window         xwindow,
diff -N -b --exclude=metacity-properties.desktop --exclude=inlinepixbufs.h --exclude=metacity.schemas --exclude=Makefile.in -u -r /tmp/metacity-2.4.8/src/xprops.h /home/xkahn/src/cvs/metacity-2.4.8/src/xprops.h
--- /tmp/metacity-2.4.8/src/xprops.h	Sun Nov  3 18:42:21 2002
+++ /home/xkahn/src/cvs/metacity-2.4.8/src/xprops.h	Mon Dec 23 17:28:50 2002
@@ -76,6 +76,10 @@
                                       Window         xwindow,
                                       Atom           xatom,
                                       MotifWmHints **hints_p);
+gboolean meta_prop_get_save_id       (MetaDisplay   *display,
+                                      Window         xwindow,
+                                      Atom           xatom,
+				      char         **save_id);
 gboolean meta_prop_get_cardinal_list (MetaDisplay   *display,
                                       Window         xwindow,
                                       Atom           xatom,
