Index: src/include/boxes.h
===================================================================
--- src/include/boxes.h	(revision 3754)
+++ src/include/boxes.h	(working copy)
@@ -103,6 +103,11 @@ gboolean meta_rectangle_intersect       
 gboolean meta_rectangle_equal           (const MetaRectangle *src1,
                                          const MetaRectangle *src2);
 
+/* Find the bounding box of the union of two rectangles */
+void     meta_rectangle_union           (const MetaRectangle *rect1,
+                                         const MetaRectangle *rect2,
+                                         MetaRectangle       *dest);
+
 /* overlap is similar to intersect but doesn't provide location of
  * intersection information.
  */
Index: src/core/window.c
===================================================================
--- src/core/window.c	(revision 3754)
+++ src/core/window.c	(working copy)
@@ -248,7 +248,7 @@ meta_window_new_with_attrs (MetaDisplay 
   gulong existing_wm_state;
   gulong event_mask;
   MetaMoveResizeFlags flags;
-#define N_INITIAL_PROPS 18
+#define N_INITIAL_PROPS 19
   Atom initial_props[N_INITIAL_PROPS];
   int i;
   gboolean has_shape;
@@ -460,6 +460,7 @@ meta_window_new_with_attrs (MetaDisplay 
   window->maximize_vertically_after_placement = FALSE;
   window->minimize_after_placement = FALSE;
   window->fullscreen = FALSE;
+  window->fullscreen_monitors[0] = -1;
   window->require_fully_onscreen = TRUE;
   window->require_on_single_xinerama = TRUE;
   window->require_titlebar_visible = TRUE;
@@ -590,6 +591,7 @@ meta_window_new_with_attrs (MetaDisplay 
   initial_props[i++] = display->atom__MOTIF_WM_HINTS;
   initial_props[i++] = XA_WM_TRANSIENT_FOR;
   initial_props[i++] = display->atom__NET_WM_USER_TIME_WINDOW;
+  initial_props[i++] = display->atom__NET_WM_FULLSCREEN_MONITORS;
   g_assert (N_INITIAL_PROPS == i);
   
   meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
@@ -1111,6 +1113,9 @@ meta_window_free (MetaWindow  *window,
       XDeleteProperty (window->display->xdisplay,
                        window->xwindow,
                        window->display->atom__NET_WM_STATE);
+      XDeleteProperty (window->display->xdisplay,
+                       window->xwindow,
+                       window->display->atom__NET_WM_FULLSCREEN_MONITORS);
       set_wm_state (window, WithdrawnState);
       meta_error_trap_pop (window->display, FALSE);
     }
@@ -1291,6 +1296,23 @@ set_net_wm_state (MetaWindow *window)
                    XA_ATOM,
                    32, PropModeReplace, (guchar*) data, i);
   meta_error_trap_pop (window->display, FALSE);
+
+  if (window->fullscreen)
+    {
+      data[0] = window->fullscreen_monitors[0];
+      data[1] = window->fullscreen_monitors[1];
+      data[2] = window->fullscreen_monitors[2];
+      data[3] = window->fullscreen_monitors[3];
+
+      meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
+      meta_error_trap_push (window->display);
+      XChangeProperty (window->display->xdisplay,
+                       window->xwindow,
+                       window->display->atom__NET_WM_FULLSCREEN_MONITORS,
+                       XA_CARDINAL, 32, PropModeReplace,
+                       (guchar*) data, 4);
+      meta_error_trap_pop (window->display, FALSE);
+    }
 }
 
 gboolean
@@ -2770,6 +2792,34 @@ meta_window_unmake_fullscreen (MetaWindo
 }
 
 void
+meta_window_update_fullscreen_monitors (MetaWindow    *window,
+                                        unsigned long  top,
+                                        unsigned long  bottom,
+                                        unsigned long  left,
+                                        unsigned long  right)
+{
+  if ((int)top < window->screen->n_xinerama_infos &&
+      (int)bottom < window->screen->n_xinerama_infos &&
+      (int)left < window->screen->n_xinerama_infos &&
+      (int)right < window->screen->n_xinerama_infos)
+    {
+      window->fullscreen_monitors[0] = top;
+      window->fullscreen_monitors[1] = bottom;
+      window->fullscreen_monitors[2] = left;
+      window->fullscreen_monitors[3] = right;
+    }
+  else
+    {
+      window->fullscreen_monitors[0] = -1;
+    }
+
+  if (window->fullscreen)
+    {
+      meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
+    }
+}
+
+void
 meta_window_shade (MetaWindow  *window,
                    guint32      timestamp)
 {
@@ -5136,6 +5186,23 @@ meta_window_client_message (MetaWindow *
       window_activate (window, timestamp, source_indication, NULL);
       return TRUE;
     }
+  else if (event->xclient.message_type ==
+           display->atom__NET_WM_FULLSCREEN_MONITORS)
+    {
+      MetaClientType source_indication;
+      gulong top, bottom, left, right;
+
+      meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
+                    window->desc);
+
+      top = event->xclient.data.l[0];
+      bottom = event->xclient.data.l[1];
+      left = event->xclient.data.l[2];
+      right = event->xclient.data.l[3];
+      source_indication = event->xclient.data.l[4];
+
+      meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
+    }
   
   return FALSE;
 }
Index: src/core/constraints.c
===================================================================
--- src/core/constraints.c	(revision 3754)
+++ src/core/constraints.c	(working copy)
@@ -392,7 +392,27 @@ setup_constraint_info (ConstraintInfo   
   meta_window_get_work_area_for_xinerama (window,
                                           xinerama_info->number,
                                           &info->work_area_xinerama);
-  info->entire_xinerama = xinerama_info->rect;
+
+  if (!window->fullscreen || window->fullscreen_monitors[0] == -1)
+    {
+      info->entire_xinerama = xinerama_info->rect;
+    }
+  else
+    {
+      int i = 0;
+      long monitor;
+
+      monitor = window->fullscreen_monitors[i];
+      info->entire_xinerama =
+        window->screen->xinerama_infos[monitor].rect;
+      for (i = 1; i <= 3; i++)
+        {
+          monitor = window->fullscreen_monitors[i];
+          meta_rectangle_union (&info->entire_xinerama,
+                                &window->screen->xinerama_infos[monitor].rect,
+                                &info->entire_xinerama);
+        }
+    }
 
   cur_workspace = window->screen->active_workspace;
   info->usable_screen_region   = 
@@ -784,7 +804,9 @@ constrain_fullscreen (MetaWindow        
   /* Determine whether constraint applies; exit if it doesn't */
   if (!window->fullscreen)
     return TRUE;
+
   xinerama = info->entire_xinerama;
+
   get_size_limits (window, info->fgeom, FALSE, &min_size, &max_size);
   too_big =   !meta_rectangle_could_fit_rect (&xinerama, &min_size);
   too_small = !meta_rectangle_could_fit_rect (&max_size, &xinerama);
Index: src/core/boxes.c
===================================================================
--- src/core/boxes.c	(revision 3754)
+++ src/core/boxes.c	(working copy)
@@ -197,6 +197,40 @@ meta_rectangle_equal (const MetaRectangl
           (src1->height == src2->height));
 }
 
+void
+meta_rectangle_union (const MetaRectangle *rect1,
+                      const MetaRectangle *rect2,
+                      MetaRectangle       *dest)
+{
+  int dest_x, dest_y;
+  int dest_w, dest_h;
+
+  dest_x = rect1->x;
+  dest_y = rect1->y;
+  dest_w = rect1->width;
+  dest_h = rect1->height;
+
+  if (rect2->x < dest_x)
+    {
+      dest_w += dest_x - rect2->x;
+      dest_x = rect2->x;
+    }
+  if (rect2->y < dest_y)
+    {
+      dest_h += dest_y - rect2->y;
+      dest_y = rect2->y;
+    }
+  if (rect2->x + rect2->width > dest_x + dest_w)
+    dest_w = rect2->x + rect2->width - dest_x;
+  if (rect2->y + rect2->height > dest_y + dest_h)
+    dest_h = rect2->y + rect2->height - dest_y;
+
+  dest->x = dest_x;
+  dest->y = dest_y;
+  dest->width = dest_w;
+  dest->height = dest_h;
+}
+
 gboolean
 meta_rectangle_overlap (const MetaRectangle *rect1,
                         const MetaRectangle *rect2)
Index: src/core/window-private.h
===================================================================
--- src/core/window-private.h	(revision 3754)
+++ src/core/window-private.h	(working copy)
@@ -136,6 +136,12 @@ struct _MetaWindow
 
   /* Whether we're fullscreen */
   guint fullscreen : 1;
+
+  /* Area to cover when in fullscreen mode.  If _NET_WM_FULLSCREEN_MONITORS has
+   * been overridden (via a client message), the window will cover the union of
+   * these monitors.  If not, this is the single monitor which the window's
+   * origin is on. */
+  long fullscreen_monitors[4];
   
   /* Whether we're trying to constrain the window to be fully onscreen */
   guint require_fully_onscreen : 1;
@@ -413,6 +419,11 @@ void        meta_window_activate_with_wo
 void        meta_window_make_fullscreen_internal (MetaWindow    *window);
 void        meta_window_make_fullscreen    (MetaWindow  *window);
 void        meta_window_unmake_fullscreen  (MetaWindow  *window);
+void        meta_window_update_fullscreen_monitors (MetaWindow    *window,
+                                                    unsigned long  top,
+                                                    unsigned long  bottom,
+                                                    unsigned long  left,
+                                                    unsigned long  right);
 
 /* args to move are window pos, not frame pos */
 void        meta_window_move               (MetaWindow  *window,
Index: src/core/atomnames.h
===================================================================
--- src/core/atomnames.h	(revision 3754)
+++ src/core/atomnames.h	(working copy)
@@ -154,6 +154,7 @@ item(_NET_DESKTOP_VIEWPORT)
 item(_NET_WM_USER_TIME_WINDOW)
 item(_NET_WM_ACTION_ABOVE)
 item(_NET_WM_ACTION_BELOW)
+item(_NET_WM_FULLSCREEN_MONITORS)
 
 #if 0
 /* We apparently never use: */
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 3754)
+++ ChangeLog	(working copy)
@@ -1,3 +1,25 @@
+2008-06-23  David Trowbridge  <trowbrds@gmail.com>
+
+	This change adds support for the new _NET_WM_FULLSCREEN_MONITORS
+	property and client message.  This allows client applications to request
+	that a fullscreen window cover more than one monitor.
+
+	* src/include/boxes.h, src/core/boxes.c: Add meta_rectangle_union
+
+	* src/core/window-private.h, src/core/window.c
+	(meta_window_new_with_attrs, meta_window_free, set_net_wm_state,
+	meta_window_update_fullscreen_monitors, meta_window_client_message): Add
+	MetaWindow property to store fullscreen monitors field, update
+	_NET_WM_FULLSCREEN_MONITORS property on windows, and handle client
+	message.
+
+	* src/core/atomnames.h: Add _NET_WM_FULLSCREEN_MONITORS atom.
+
+	* src/core/constraints.c (setup_constraint_info): If
+	_NET_WM_FULLSCREEN_MONITORS is interesting, use the data stored in
+	MetaWindow::fullscreen_monitors to determine the fullscreen area instead
+	of the basic xinerama_info area.
+
 2008-06-10  Thomas Thurman  <tthurman@gnome.org>
 
         * test/tokentest/tokentest.c (draw_string_to_spec): doubles are
