Index: place.c
===================================================================
RCS file: /cvs/gnome/metacity/src/place.c,v
retrieving revision 1.31
diff -p -u -r1.31 place.c
--- place.c	6 Apr 2003 01:22:56 -0000	1.31
+++ place.c	6 Apr 2003 01:30:45 -0000
@@ -384,66 +384,13 @@ center_tile_rect_in_area (MetaRectangle 
 }
 
 static gboolean
-fit_rect_in_xinerama (MetaWindow    *window,
-                      MetaRectangle *rect)
+rect_fits_in_work_area (MetaRectangle *work_area,
+                        MetaRectangle *rect)
 {
-  int i;
-  int best_index;
-  int best_overlap;
-  MetaScreen *screen;
-  MetaRectangle work_area;
-  const MetaXineramaScreenInfo *xsi;
-
-  screen = window->screen;
-  
-  /* Find xinerama with best fit, then
-   * shift rect to be entirely within it.
-   */
-  best_overlap = -1;
-  best_index = -1;
-  
-  i = 0;
-  while (i < screen->n_xinerama_infos)
-    {
-      MetaRectangle xinerama_rect;
-      MetaRectangle intersect;
-      int overlap;
-
-      xsi = &screen->xinerama_infos[i];
-      
-      xinerama_rect.x = xsi->x_origin;
-      xinerama_rect.y = xsi->y_origin;
-      xinerama_rect.width = xsi->width;
-      xinerama_rect.height = xsi->height;
-
-      if (meta_rectangle_intersect (rect, &xinerama_rect, &intersect))
-        overlap = intersect.width * intersect.height;
-      else
-        overlap = 0;
-
-      if (overlap > best_overlap)
-        {
-          best_index = i;
-          best_overlap = overlap;
-        }
-      
-      ++i;
-    }
-
-  /* some overlap had to be better than -1 */
-  g_assert (best_index >= 0);
-
-  meta_window_get_work_area_for_xinerama (window, best_index, &work_area);
-  
-  if ((rect->x < work_area.x) || (rect->y < work_area.y) ||
-      (rect->x >= work_area.x + work_area.width) ||
-      (rect->y >= work_area.y + work_area.height))
-    center_tile_rect_in_area (rect, &work_area);
-
-  /* Now return whether we are entirely within the work area */
-  return 
-    ((rect->x + rect->width) < (work_area.x + work_area.width)) &&
-    ((rect->y + rect->height) < (work_area.y + work_area.height));
+  return ((rect->x >= work_area->x) &&
+	  (rect->y >= work_area->y) &&
+	  (rect->x + rect->width <= work_area->x + work_area->width) &&
+	  (rect->y + rect->height <= work_area->y + work_area->height));
 }
 
 /* Find the leftmost, then topmost, empty area on the workspace
@@ -472,14 +419,26 @@ find_first_fit (MetaWindow *window,
    * existing window in each of those cases.
    */  
   int retval;
-  GList *sorted;
+  GList *below_sorted;
+  GList *right_sorted;
   GList *tmp;
   MetaRectangle rect;
   MetaRectangle work_area;
   int i;
+  int* xineramas_list;
+  int  n_xineramas;
   
   retval = FALSE;
-  sorted = NULL;
+
+  /* Below each window */
+  below_sorted = g_list_copy (windows);
+  below_sorted = g_list_sort (below_sorted, leftmost_cmp);
+  below_sorted = g_list_sort (below_sorted, topmost_cmp);  
+
+  /* To the right of each window */
+  right_sorted = g_list_copy (windows);
+  right_sorted = g_list_sort (right_sorted, topmost_cmp);
+  right_sorted = g_list_sort (right_sorted, leftmost_cmp);
   
   rect.width = window->rect.width;
   rect.height = window->rect.height;
@@ -490,50 +449,30 @@ find_first_fit (MetaWindow *window,
       rect.height += fgeom->top_height + fgeom->bottom_height;
     }
 
-  /* Try center-tiling on first xinerama */
-  /* FIXME should use xinerama with mouse pointer
-   * (or better, xinerama where window was launched
-   * determined via startup notification)
-   */
-  meta_window_get_work_area_for_xinerama (window, 0, &work_area);
-
-  center_tile_rect_in_area (&rect, &work_area);
-
-  if (fit_rect_in_xinerama (window, &rect) &&
-      !rectangle_overlaps_some_window (&rect, windows))
-    {
-      *new_x = rect.x;
-      *new_y = rect.y;
-      if (fgeom)
-        {
-          *new_x += fgeom->left_width;
-          *new_y += fgeom->top_height;
-        }
-      
-      retval = TRUE;
-      
-      goto out;
+  meta_screen_get_natural_xinerama_list (window->screen,
+					 &xineramas_list,
+					 &n_xineramas);
+  for (i = 0; i < n_xineramas; i++)
+    {
+      meta_topic (META_DEBUG_XINERAMA,
+		  "Natural xinerama %d is %d,%d %dx%d\n",
+		  i,
+		  window->screen->xinerama_infos[xineramas_list[i]].x_origin,
+		  window->screen->xinerama_infos[xineramas_list[i]].y_origin,
+		  window->screen->xinerama_infos[xineramas_list[i]].width,
+		  window->screen->xinerama_infos[xineramas_list[i]].height);
     }
-  
-  sorted = g_list_copy (windows);
 
-  /* Below each window */
-  sorted = g_list_sort (sorted, leftmost_cmp);
-  sorted = g_list_sort (sorted, topmost_cmp);  
-  
-  tmp = sorted;
-  while (tmp != NULL)
+  /* try each xinerama in the natural ordering in turn */
+  i = 0;
+  while (i < n_xineramas)
     {
-      MetaWindow *w = tmp->data;
-      MetaRectangle outer_rect;
+      meta_window_get_work_area_for_xinerama (window, xineramas_list[i], &work_area);
 
-      meta_window_get_outer_rect (w, &outer_rect);
-      
-      rect.x = outer_rect.x;
-      rect.y = outer_rect.y + outer_rect.height;
-      
-      if (fit_rect_in_xinerama (window, &rect) &&
-          !rectangle_overlaps_some_window (&rect, sorted))
+      center_tile_rect_in_area (&rect, &work_area);
+
+      if (rect_fits_in_work_area (&work_area, &rect) &&
+          !rectangle_overlaps_some_window (&rect, windows))
         {
           *new_x = rect.x;
           *new_y = rect.y;
@@ -542,78 +481,82 @@ find_first_fit (MetaWindow *window,
               *new_x += fgeom->left_width;
               *new_y += fgeom->top_height;
             }
-          
+      
           retval = TRUE;
           
           goto out;
         }
 
-      tmp = tmp->next;
-    }
-
-  /* To the right of each window */
-  sorted = g_list_sort (sorted, topmost_cmp);
-  sorted = g_list_sort (sorted, leftmost_cmp);
-  
-  tmp = sorted;
-  while (tmp != NULL)
-    {
-      MetaWindow *w = tmp->data;
-      MetaRectangle outer_rect;
-     
-      meta_window_get_outer_rect (w, &outer_rect);
-     
-      rect.x = outer_rect.x + outer_rect.width;
-      rect.y = outer_rect.y;
-     
-      if (fit_rect_in_xinerama (window, &rect) &&
-          !rectangle_overlaps_some_window (&rect, sorted))
+      /* try below each window */
+      tmp = below_sorted;
+      while (tmp != NULL)
         {
-          *new_x = rect.x;
-          *new_y = rect.y;
-          if (fgeom)
+          MetaWindow *w = tmp->data;
+          MetaRectangle outer_rect;
+
+          meta_window_get_outer_rect (w, &outer_rect);
+      
+          rect.x = outer_rect.x;
+          rect.y = outer_rect.y + outer_rect.height;
+      
+          if (rect_fits_in_work_area (&work_area, &rect) &&
+              !rectangle_overlaps_some_window (&rect, below_sorted))
             {
-              *new_x += fgeom->left_width;
-              *new_y += fgeom->top_height;
+              *new_x = rect.x;
+              *new_y = rect.y;
+              if (fgeom)
+                {
+                  *new_x += fgeom->left_width;
+                  *new_y += fgeom->top_height;
+                }
+          
+              retval = TRUE;
+          
+              goto out;
             }
-         
-          retval = TRUE;
-         
-          goto out;
-        }
-
-      tmp = tmp->next;
-    }
-
-  /* Try center-tile on each Xinerama screen which isn't the first */
-  i = 1;
-  while (i < window->screen->n_xinerama_infos)
-    {
-      meta_window_get_work_area_for_xinerama (window, i, &work_area);
 
-      center_tile_rect_in_area (&rect, &work_area);
+          tmp = tmp->next;
+        }
 
-      if (fit_rect_in_xinerama (window, &rect) &&
-          !rectangle_overlaps_some_window (&rect, windows))
+      /* try to the right of each window */
+      tmp = right_sorted;
+      while (tmp != NULL)
         {
-          *new_x = rect.x;
-          *new_y = rect.y;
-          if (fgeom)
+          MetaWindow *w = tmp->data;
+          MetaRectangle outer_rect;
+     
+          meta_window_get_outer_rect (w, &outer_rect);
+     
+          rect.x = outer_rect.x + outer_rect.width;
+          rect.y = outer_rect.y;
+     
+          if (rect_fits_in_work_area (&work_area, &rect) &&
+              !rectangle_overlaps_some_window (&rect, right_sorted))
             {
-              *new_x += fgeom->left_width;
-              *new_y += fgeom->top_height;
+              *new_x = rect.x;
+              *new_y = rect.y;
+              if (fgeom)
+                {
+                  *new_x += fgeom->left_width;
+                  *new_y += fgeom->top_height;
+                }
+         
+              retval = TRUE;
+         
+              goto out;
             }
-      
-          retval = TRUE;
-          
-          goto out;
+
+          tmp = tmp->next;
         }
       
       ++i;
     }
   
  out:
-  g_list_free (sorted);
+
+  g_free (xineramas_list);
+  g_list_free (below_sorted);
+  g_list_free (right_sorted);
   return retval;
 }
 
@@ -975,7 +918,9 @@ get_vertical_edges (MetaWindow *window,
   windows = get_windows_on_same_workspace (window, &n_windows);
 
   i = 0;
-  n_edges = n_windows * 2 + 4 + window->screen->n_xinerama_infos - 1; /* 4 = workspace/screen edges */
+  /* 4 = workspace/screen edges */
+  n_edges = n_windows * 2 + 4 + window->screen->n_xinerama_infos - 1; 
+
   edges = g_new (int, n_edges);
 
   /* workspace/screen edges */
@@ -1235,7 +1180,6 @@ meta_window_find_next_horizontal_edge (M
   
   return retval;
 }
-
 
 int
 meta_window_find_nearest_vertical_edge (MetaWindow *window,
Index: screen.c
===================================================================
RCS file: /cvs/gnome/metacity/src/screen.c,v
retrieving revision 1.104
diff -p -u -r1.104 screen.c
--- screen.c	15 Mar 2003 02:16:21 -0000	1.104
+++ screen.c	6 Apr 2003 01:30:47 -0000
@@ -1346,6 +1346,138 @@ meta_screen_get_xinerama_for_window (Met
   return meta_screen_get_xinerama_for_rect (screen, &window_rect);
 }
 
+const MetaXineramaScreenInfo* 
+meta_screen_get_xinerama_neighbor (MetaScreen         *screen,
+                                   int                 which_xinerama,
+                                   MetaScreenDirection direction)
+{
+  MetaXineramaScreenInfo* input = screen->xinerama_infos + which_xinerama;
+  MetaXineramaScreenInfo* current;
+  int i;
+
+  for (i = 0; i < screen->n_xinerama_infos; i++)
+    {
+      current = screen->xinerama_infos + i;
+
+      if (((direction == META_SCREEN_RIGHT) && 
+           (current->x_origin == input->x_origin + input->width) &&
+           (current->y_origin >= input->y_origin) &&
+           (current->y_origin <= input->y_origin+input->height)) ||
+          ((direction == META_SCREEN_LEFT) && 
+           (input->x_origin == current->x_origin + current->width) &&
+           (current->y_origin >= input->y_origin) &&
+           (current->y_origin <= input->y_origin + input->height)) ||
+          ((direction == META_SCREEN_UP) && 
+           (input->y_origin == current->y_origin + current->height) &&
+           (current->x_origin >= input->x_origin) &&
+           (current->x_origin <= input->x_origin + input->width)) ||
+          ((direction == META_SCREEN_DOWN) && 
+           (current->y_origin == input->y_origin + input->height) &&
+           (current->x_origin >= input->x_origin) &&
+           (current->x_origin <= input->x_origin + input->width)))
+        {
+          return current;
+        }
+    }
+  
+  return NULL;
+}
+
+void
+meta_screen_get_natural_xinerama_list (MetaScreen *screen,
+                                       int**       xineramas_list,
+                                       int*        n_xineramas)
+{
+  const MetaXineramaScreenInfo* current;
+  const MetaXineramaScreenInfo* tmp;
+  GQueue* xinerama_queue;
+  int* visited;
+  int cur = 0;
+  int i;
+
+  *n_xineramas = screen->n_xinerama_infos;
+  *xineramas_list = g_new (int, screen->n_xinerama_infos);
+
+  /* we calculate a natural ordering by which to choose xineramas for
+   * window placement.  We start at the current xinerama, and perform
+   * a breadth-first search of the xineramas starting from that
+   * xinerama.  We choose preferentially left, then right, then down,
+   * then up.  The visitation order produced by this traversal is the
+   * natural xinerama ordering.
+   */
+
+  visited = g_new (int, screen->n_xinerama_infos);
+  for (i = 0; i < screen->n_xinerama_infos; i++)
+    {
+      visited[i] = FALSE;
+    }
+
+  current = meta_screen_get_current_xinerama (screen);
+  xinerama_queue = g_queue_new ();
+  g_queue_push_tail (xinerama_queue, (gpointer) current);
+  visited[current->number] = TRUE;
+
+  while (!g_queue_is_empty (xinerama_queue))
+    {
+      current = (const MetaXineramaScreenInfo*) 
+        g_queue_pop_head (xinerama_queue);
+
+      (*xineramas_list)[cur++] = current->number;
+
+      /* enqueue each of the directions */
+      tmp = meta_screen_get_xinerama_neighbor (screen,
+                                               current->number,
+                                               META_SCREEN_LEFT);
+      if (tmp && !visited[tmp->number])
+        {
+          g_queue_push_tail (xinerama_queue,
+                             (MetaXineramaScreenInfo*) tmp);
+          visited[tmp->number] = TRUE;
+        }
+      tmp = meta_screen_get_xinerama_neighbor (screen,
+                                               current->number,
+                                               META_SCREEN_RIGHT);
+      if (tmp && !visited[tmp->number])
+        {
+          g_queue_push_tail (xinerama_queue,
+                             (MetaXineramaScreenInfo*) tmp);
+          visited[tmp->number] = TRUE;
+        }
+      tmp = meta_screen_get_xinerama_neighbor (screen,
+                                               current->number,
+                                               META_SCREEN_UP);
+      if (tmp && !visited[tmp->number])
+        {
+          g_queue_push_tail (xinerama_queue,
+                             (MetaXineramaScreenInfo*) tmp);
+          visited[tmp->number] = TRUE;
+        }
+      tmp = meta_screen_get_xinerama_neighbor (screen,
+                                               current->number,
+                                               META_SCREEN_DOWN);
+      if (tmp && !visited[tmp->number])
+        {
+          g_queue_push_tail (xinerama_queue,
+                             (MetaXineramaScreenInfo*) tmp);
+          visited[tmp->number] = TRUE;
+        }
+    }
+
+  /* in case we somehow missed some set of xineramas, go through the
+   * visited list and add in any xineramas that were missed
+   */
+  for (i = 0; i < screen->n_xinerama_infos; i++)
+    {
+      if (visited[i] == 0)
+        {
+          (*xineramas_list)[cur++] = i;
+        }
+    }
+
+  g_free (visited);
+  g_queue_free (xinerama_queue);
+}
+
 gboolean
 meta_screen_window_intersects_xinerama (MetaScreen *screen,
                                         MetaWindow *window,
Index: screen.h
===================================================================
RCS file: /cvs/gnome/metacity/src/screen.h,v
retrieving revision 1.47
diff -p -u -r1.47 screen.h
--- screen.h	23 Feb 2003 17:09:46 -0000	1.47
+++ screen.h	6 Apr 2003 01:30:47 -0000
@@ -48,6 +48,14 @@ typedef enum
   META_SCREEN_BOTTOMRIGHT
 } MetaScreenCorner;
 
+typedef enum
+{
+  META_SCREEN_UP,
+  META_SCREEN_DOWN,
+  META_SCREEN_LEFT,
+  META_SCREEN_RIGHT
+} MetaScreenDirection;
+
 struct _MetaScreen
 {
   MetaDisplay *display;
@@ -140,14 +148,21 @@ const MetaXineramaScreenInfo* meta_scree
 const MetaXineramaScreenInfo* meta_screen_get_xinerama_for_window (MetaScreen    *screen,
                                                                    MetaWindow    *window);
 
-
-gboolean meta_screen_window_intersects_xinerama (MetaScreen *screen, 
-                                                 MetaWindow *window,
-                                                 int         which_xinerama);
+gboolean      meta_screen_window_intersects_xinerama (MetaScreen *screen, 
+                                                      MetaWindow *window,
+                                                      int         which_xinerama);
+
+const MetaXineramaScreenInfo* meta_screen_get_xinerama_neighbor (MetaScreen *screen,
+                                                                 int         which_xinerama,
+                                                                 MetaScreenDirection dir);
+void          meta_screen_get_natural_xinerama_list (MetaScreen *screen,
+                                                     int**       xineramas_list,
+                                                     int*        n_xineramas);
 
 void          meta_screen_update_workspace_layout (MetaScreen             *screen);
 void          meta_screen_update_workspace_names  (MetaScreen             *screen);
 void          meta_screen_queue_workarea_recalc   (MetaScreen             *screen);
+
 
 Window meta_create_offscreen_window (Display *xdisplay,
                                      Window   parent);
