Big changes to placement across multiple monitors.
authorDana Jansens <danakj@orodu.net>
Thu, 14 Oct 2010 23:40:41 +0000 (19:40 -0400)
committerDana Jansens <danakj@orodu.net>
Mon, 24 Jan 2011 19:19:22 +0000 (14:19 -0500)
Add a Primary option for which monitor to place new windows on.  Make "Active" the default instead of "Any", which is just totally crazy.

When a window is being placed in the FOREGROUND, use a monitor chosen in
  the following order:
  1. same monitor as parent
  2. primary monitor if placement=PRIMARY
     active monitor if placement=ACTIVE
     pointer monitor if placement=MOUSE
  3. primary monitor
  4. other monitors where the window has group members on the same desktop
  5. other monitors where the window has group members on other desktops
  6. other monitors

When a window is being placed in the BACKGROUND, use a monitor chosen in the
  following order:
  1. same monitor as parent
  2. other monitors where the window has group members on the same desktop
   2a. primary monitor in this set
   2b. other monitors in this set
  3. other monitors where the window has group members on other desktops
   3a. primary monitor in this set
   3b. other monitors in this set
  4. other monitors
   4a. primary monitor in this set
   4b. other monitors in this set

Decide to focus the new window before placing it, so we know if it will be
  placed in the foreground or background.

Always choose a single monitor, then place on it, rather than possibly moving
  to a "backup" monitor.  Unpredictable monitor placement is horrible.

data/rc.xml
openbox/client.c
openbox/config.c
openbox/place.c
openbox/place.h

index 7faa255..faa401b 100644 (file)
   <center>yes</center>
   <!-- whether to place windows in the center of the free area found or
        the top left corner -->
-  <monitor>Active</monitor>
+  <monitor>Primary</monitor>
   <!-- with Smart placement on a multi-monitor system, try to place new windows
        on: 'Any' - any monitor, 'Mouse' - where the mouse is, 'Active' - where
-       the active window is -->
+       the active window is, 'Primary' - only on the primary monitor -->
   <primaryMonitor>1</primaryMonitor>
   <!-- The monitor where Openbox should place popup dialogs such as the
        focus cycling popup, or the desktop switch popup.  It can be an index
index 8eeb052..bdfe939 100644 (file)
@@ -192,7 +192,8 @@ void client_manage(Window window, ObPrompt *prompt)
 {
     ObClient *self;
     XSetWindowAttributes attrib_set;
-    gboolean activate = FALSE;
+    gboolean try_activate = FALSE;
+    gboolean do_activate;
     ObAppSettings *settings;
     gboolean transient = FALSE;
     Rect place;
@@ -284,7 +285,7 @@ void client_manage(Window window, ObPrompt *prompt)
                            FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
                            settings->focus == 1))
     {
-        activate = TRUE;
+        try_activate = TRUE;
     }
 
     /* remove the client's border */
@@ -298,6 +299,14 @@ void client_manage(Window window, ObPrompt *prompt)
     /* where the frame was placed is where the window was originally */
     place = self->area;
 
+    ob_debug("Going to try activate new window? %s",
+             try_activate ? "yes" : "no");
+    if (try_activate)
+        do_activate = client_can_steal_focus(self, settings->focus,
+                                             event_time(), launch_time);
+    else
+        do_activate = FALSE;
+
     /* figure out placement for the window if the window is new */
     if (ob_state() == OB_STATE_RUNNING) {
         ob_debug("Positioned: %s @ %d %d",
@@ -316,7 +325,8 @@ void client_manage(Window window, ObPrompt *prompt)
                      "program + user specified" :
                      "BADNESS !?")))), place.width, place.height);
 
-        obplaced = place_client(self, &place.x, &place.y, settings);
+        obplaced = place_client(self, do_activate, &place.x, &place.y,
+                                settings);
 
         /* watch for buggy apps that ask to be placed at (0,0) when there is
            a strut there */
@@ -424,13 +434,30 @@ void client_manage(Window window, ObPrompt *prompt)
     client_apply_startup_state(self, place.x, place.y,
                                place.width, place.height);
 
-    ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
-                  activate ? "yes" : "no");
-    if (activate) {
-        activate = client_can_steal_focus(self, settings->focus,
-                                          event_time(), launch_time);
+    /* grab mouse bindings before showing the window */
+    mouse_grab_for_client(self, TRUE);
+
+    /* this has to happen before we try focus the window, but we want it to
+       happen after the client's stacking has been determined or it looks bad
+    */
+    {
+        gulong ignore_start;
+        if (!config_focus_under_mouse)
+            ignore_start = event_start_ignore_all_enters();
+
+        client_show(self);
+
+        if (!config_focus_under_mouse)
+            event_end_ignore_all_enters(ignore_start);
+    }
 
-        if (!activate) {
+    /* activate/hilight/raise the window */
+    if (try_activate) {
+        if (do_activate) {
+            gboolean stacked = client_restore_session_stacking(self);
+            client_present(self, FALSE, !stacked, TRUE);
+        }
+        else {
             /* if the client isn't stealing focus, then hilite it so the user
                knows it is there, but don't do this if we're restoring from a
                session */
@@ -450,27 +477,6 @@ void client_manage(Window window, ObPrompt *prompt)
             stacking_raise(CLIENT_AS_WINDOW(self));
     }
 
-    mouse_grab_for_client(self, TRUE);
-
-    /* this has to happen before we try focus the window, but we want it to
-       happen after the client's stacking has been determined or it looks bad
-    */
-    {
-        gulong ignore_start;
-        if (!config_focus_under_mouse)
-            ignore_start = event_start_ignore_all_enters();
-
-        client_show(self);
-
-        if (!config_focus_under_mouse)
-            event_end_ignore_all_enters(ignore_start);
-    }
-
-    if (activate) {
-        gboolean stacked = client_restore_session_stacking(self);
-        client_present(self, FALSE, !stacked, TRUE);
-    }
-
     /* add to client list/map */
     client_list = g_list_append(client_list, self);
     window_add(&self->window, CLIENT_AS_WINDOW(self));
@@ -693,22 +699,18 @@ static gboolean client_can_steal_focus(ObClient *self,
 {
     gboolean steal;
     gboolean relative_focused;
-    gboolean parent_focused;
 
     steal = TRUE;
 
-    parent_focused = (focus_client != NULL &&
-                      client_search_focus_parent(self));
     relative_focused = (focus_client != NULL &&
                         (client_search_focus_tree_full(self) != NULL ||
                          client_search_focus_group_full(self) != NULL));
 
     /* This is focus stealing prevention */
-    ob_debug_type(OB_DEBUG_FOCUS,
-                  "Want to focus window 0x%x at time %u "
-                  "launched at %u (last user interaction time %u)",
-                  self->window, steal_time, launch_time,
-                  event_last_user_time);
+    ob_debug("Want to focus window 0x%x at time %u "
+             "launched at %u (last user interaction time %u)",
+             self->window, steal_time, launch_time,
+             event_last_user_time);
 
     /* if it's on another desktop... */
     if (!(self->desktop == screen_desktop ||
@@ -721,15 +723,13 @@ static gboolean client_can_steal_focus(ObClient *self,
           !event_time_after(launch_time, screen_desktop_user_time))))
     {
         steal = FALSE;
-        ob_debug_type(OB_DEBUG_FOCUS,
-                      "Not focusing the window because its on another "
-                      "desktop\n");
+        ob_debug("Not focusing the window because its on another desktop\n");
     }
     /* If something is focused... */
     else if (focus_client) {
         /* If the user is working in another window right now, then don't
            steal focus */
-        if (!parent_focused &&
+        if (!relative_focused &&
             event_last_user_time &&
             (!launch_time ||
              (event_time_after(event_last_user_time, launch_time) &&
@@ -738,18 +738,15 @@ static gboolean client_can_steal_focus(ObClient *self,
                              steal_time - OB_EVENT_USER_TIME_DELAY))
         {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because the user is "
-                          "working in another window that is not "
-                          "its parent");
+            ob_debug("Not focusing the window because the user is "
+                     "working in another window that is not its relative");
         }
         /* If the new window is a transient (and its relatives aren't
            focused) */
         else if (client_has_parent(self) && !relative_focused) {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because it is a "
-                          "transient, and its relatives aren't focused");
+            ob_debug("Not focusing the window because it is a "
+                     "transient, and its relatives aren't focused");
         }
         /* Don't steal focus from globally active clients.
            I stole this idea from KWin. It seems nice.
@@ -758,17 +755,15 @@ static gboolean client_can_steal_focus(ObClient *self,
                    focus_client->focus_notify))
         {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because a globally "
-                          "active client has focus");
+            ob_debug("Not focusing the window because a globally "
+                     "active client has focus");
         }
         /* Don't move focus if it's not going to go to this window
            anyway */
         else if (client_focus_target(self) != self) {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because another window "
-                          "would get the focus anyway");
+            ob_debug("Not focusing the window because another window "
+                     "would get the focus anyway");
         }
         /* Don't move focus if the window is not visible on the current
            desktop and none of its relatives are focused */
@@ -777,17 +772,15 @@ static gboolean client_can_steal_focus(ObClient *self,
                  !relative_focused)
         {
             steal = FALSE;
-            ob_debug_type(OB_DEBUG_FOCUS,
-                          "Not focusing the window because it is on "
-                          "another desktop and no relatives are focused ");
+            ob_debug("Not focusing the window because it is on "
+                     "another desktop and no relatives are focused ");
         }
     }
 
     if (!steal)
-        ob_debug_type(OB_DEBUG_FOCUS,
-                      "Focus stealing prevention activated for %s at "
-                      "time %u (last user interaction time %u)",
-                      self->title, steal_time, event_last_user_time);
+        ob_debug("Focus stealing prevention activated for %s at "
+                 "time %u (last user interaction time %u)",
+                 self->title, steal_time, event_last_user_time);
     return steal;
 }
 
index 3cd0b6c..debd9fb 100644 (file)
@@ -582,6 +582,8 @@ static void parse_placement(xmlNodePtr node, gpointer d)
             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
         else if (obt_xml_node_contains(n, "mouse"))
             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
+        else if (obt_xml_node_contains(n, "any"))
+            config_place_monitor = OB_PLACE_MONITOR_ANY;
     }
     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
         config_primary_monitor_index = obt_xml_node_int(n);
@@ -1003,7 +1005,7 @@ void config_startup(ObtXmlInst *i)
 
     config_place_policy = OB_PLACE_POLICY_SMART;
     config_place_center = TRUE;
-    config_place_monitor = OB_PLACE_MONITOR_ANY;
+    config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
 
     config_primary_monitor_index = 1;
     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
index d9919d0..efcec7e 100644 (file)
 
 extern ObDock *dock;
 
-static void add_choice(guint *choice, guint mychoice)
+static Rect *pick_pointer_head(ObClient *c)
 {
-    guint i;
-    for (i = 0; i < screen_num_monitors; ++i) {
-        if (choice[i] == mychoice)
-            return;
-        else if (choice[i] == screen_num_monitors) {
-            choice[i] = mychoice;
-            return;
-        }
-    }
+    return screen_area(c->desktop, screen_monitor_pointer(), NULL);
 }
 
-static Rect *pick_pointer_head(ObClient *c)
+/* use the following priority lists for pick_head()
+
+   When a window is being placed in the FOREGROUND, use a monitor chosen in
+   the following order:
+   1. same monitor as parent
+   2. primary monitor if placement=PRIMARY
+      active monitor if placement=ACTIVE
+      pointer monitor if placement=MOUSE
+   3. primary monitor
+   4. other monitors where the window has group members on the same desktop
+   5. other monitors where the window has group members on other desktops
+   6. other monitors
+
+   When a window is being placed in the BACKGROUND, use a monitor chosen in the
+   following order:
+   1. same monitor as parent
+   2. other monitors where the window has group members on the same desktop
+    2a. primary monitor in this set
+    2b. other monitors in this set
+   3. other monitors where the window has group members on other desktops
+    3a. primary monitor in this set
+    3b. other monitors in this set
+   4. other monitors
+    4a. primary monitor in this set
+    4b. other monitors in this set
+*/
+
+/*! One for each possible head, used to sort them in order of precedence. */
+typedef struct {
+    guint monitor;
+    guint flags;
+} ObPlaceHead;
+
+/*! Flags for ObPlaceHead */
+enum {
+    HEAD_PARENT = 1 << 0, /* parent's monitor */
+    HEAD_PLACED = 1 << 1, /* chosen monitor by placement */
+    HEAD_PRIMARY = 1 << 2, /* primary monitor */
+    HEAD_GROUP_DESK = 1 << 3, /* has a group member on the same desktop */
+    HEAD_GROUP = 1 << 4, /* has a group member on another desktop */
+};
+
+gint cmp_foreground(const void *a, const void *b)
 {
-    return screen_area(c->desktop, screen_monitor_pointer(), NULL);
+    const ObPlaceHead *h1 = a;
+    const ObPlaceHead *h2 = b;
+    gint i = 0;
+
+    if (h1->monitor == h2->monitor) return 0;
+
+    if (h1->flags & HEAD_PARENT) --i;
+    if (h2->flags & HEAD_PARENT) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_PLACED) --i;
+    if (h2->flags & HEAD_PLACED) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_PRIMARY) --i;
+    if (h2->flags & HEAD_PRIMARY) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_GROUP_DESK) --i;
+    if (h2->flags & HEAD_GROUP_DESK) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_GROUP) --i;
+    if (h2->flags & HEAD_GROUP) ++i;
+    if (i) return i;
+
+    return h1->monitor - h2->monitor;
+}
+
+gint cmp_background(const void *a, const void *b)
+{
+    const ObPlaceHead *h1 = a;
+    const ObPlaceHead *h2 = b;
+    gint i = 0;
+
+    if (h1->monitor == h2->monitor) return 0;
+
+    if (h1->flags & HEAD_PARENT) --i;
+    if (h2->flags & HEAD_PARENT) ++i;
+    if (i) return i;
+
+    if (h1->flags & HEAD_GROUP_DESK || h2->flags & HEAD_GROUP_DESK) {
+        if (h1->flags & HEAD_GROUP_DESK) --i;
+        if (h2->flags & HEAD_GROUP_DESK) ++i;
+        if (i) return i;
+        if (h1->flags & HEAD_PRIMARY) --i;
+        if (h2->flags & HEAD_PRIMARY) ++i;
+        if (i) return i;
+    }
+
+    if (h1->flags & HEAD_GROUP || h2->flags & HEAD_GROUP) {
+        if (h1->flags & HEAD_GROUP) --i;
+        if (h2->flags & HEAD_GROUP) ++i;
+        if (i) return i;
+        if (h1->flags & HEAD_PRIMARY) --i;
+        if (h2->flags & HEAD_PRIMARY) ++i;
+        if (i) return i;
+    }
+
+    if (h1->flags & HEAD_PRIMARY) --i;
+    if (h2->flags & HEAD_PRIMARY) ++i;
+    if (i) return i;
+
+    return h1->monitor - h2->monitor;
 }
 
 /*! Pick a monitor to place a window on. */
-static Rect **pick_head(ObClient *c)
+static Rect *pick_head(ObClient *c, gboolean foreground)
 {
-    Rect **area;
-    guint *choice;
+    Rect *area;
+    ObPlaceHead *choice;
     guint i;
-    gint px, py;
     ObClient *p;
+    GSList *it;
 
-    area = g_new(Rect*, screen_num_monitors);
-    choice = g_new(guint, screen_num_monitors);
-    for (i = 0; i < screen_num_monitors; ++i)
-        choice[i] = screen_num_monitors; /* make them all invalid to start */
-
-    /* try direct parent first */
-    if ((p = client_direct_parent(c))) {
-        add_choice(choice, client_monitor(p));
-        ob_debug("placement adding choice %d for parent",
-                 client_monitor(p));
+    choice = g_new(ObPlaceHead, screen_num_monitors);
+    for (i = 0; i < screen_num_monitors; ++i) {
+        choice[i].monitor = i;
+        choice[i].flags = 0;
     }
 
-    /* more than one window in its group (more than just this window) */
-    if (client_has_group_siblings(c)) {
-        GSList *it;
-
-        /* try on the client's desktop */
-        for (it = c->group->members; it; it = g_slist_next(it)) {
-            ObClient *itc = it->data;
-            if (itc != c &&
-                (itc->desktop == c->desktop ||
-                 itc->desktop == DESKTOP_ALL || c->desktop == DESKTOP_ALL))
-            {
-                add_choice(choice, client_monitor(it->data));
-                ob_debug("placement adding choice %d for group sibling",
-                         client_monitor(it->data));
-            }
-        }
-
-        /* try on all desktops */
-        for (it = c->group->members; it; it = g_slist_next(it)) {
-            ObClient *itc = it->data;
-            if (itc != c) {
-                add_choice(choice, client_monitor(it->data));
-                ob_debug("placement adding choice %d for group sibling on "
-                         "another desktop", client_monitor(it->data));
+    /* find monitors with group members */
+    for (it = c->group->members; it; it = g_slist_next(it)) {
+        ObClient *itc = it->data;
+        if (itc != c) {
+            guint m = client_monitor(itc);
+
+            if (m < screen_num_monitors) {
+                if (screen_compare_desktops(itc->desktop, c->desktop))
+                    choice[m].flags |= HEAD_GROUP_DESK;
+                else
+                    choice[m].flags |= HEAD_GROUP;
             }
         }
     }
 
-    /* skip this if placing by the mouse position */
-    if (focus_client && client_normal(focus_client) &&
-        config_place_monitor != OB_PLACE_MONITOR_MOUSE)
-    {
-        add_choice(choice, client_monitor(focus_client));
-        ob_debug("placement adding choice %d for normal focused window",
-                 client_monitor(focus_client));
+    i = screen_monitor_primary(FALSE);
+    if (i < screen_num_monitors) {
+        choice[i].flags |= HEAD_PRIMARY;
+        if (config_place_monitor == OB_PLACE_MONITOR_PRIMARY)
+            choice[i].flags |= HEAD_PLACED;
     }
 
-    screen_pointer_pos(&px, &py);
-
-    for (i = 0; i < screen_num_monitors; i++) {
-        const Rect *monitor = screen_physical_area_monitor(i);
-        gboolean contain = RECT_CONTAINS(*monitor, px, py);
-        if (contain) {
-            add_choice(choice, i);
-            ob_debug("placement adding choice %d for mouse pointer", i);
-            break;
-        }
+    /* direct parent takes highest precedence */
+    if ((p = client_direct_parent(c))) {
+        i = client_monitor(p);
+        if (i < screen_num_monitors)
+            choice[i].flags |= HEAD_PARENT;
     }
 
-    /* add any leftover choices */
-    for (i = 0; i < screen_num_monitors; ++i)
-        add_choice(choice, i);
+    qsort(choice, screen_num_monitors, sizeof(ObPlaceHead),
+          foreground ? cmp_foreground : cmp_background);
 
+    /* save the areas of the monitors in order of their being chosen */
     for (i = 0; i < screen_num_monitors; ++i)
-        area[i] = screen_area(c->desktop, choice[i], NULL);
+    {
+        ob_debug("placement choice %d is monitor %d", i, choice[i].monitor);
+        if (choice[i].flags & HEAD_PARENT)
+            ob_debug("  - parent on monitor");
+        if (choice[i].flags & HEAD_PLACED)
+            ob_debug("  - placement choice");
+        if (choice[i].flags & HEAD_PRIMARY)
+            ob_debug("  - primary monitor");
+        if (choice[i].flags & HEAD_GROUP_DESK)
+            ob_debug("  - group on same desktop");
+        if (choice[i].flags & HEAD_GROUP)
+            ob_debug("  - group on other desktop");
+    }
+
+    area = screen_area(c->desktop, choice[0].monitor, NULL);
 
     g_free(choice);
 
+    /* return the area for the chosen monitor */
     return area;
 }
 
-static gboolean place_random(ObClient *client, gint *x, gint *y)
+static gboolean place_random(ObClient *client, Rect *area, gint *x, gint *y)
 {
     gint l, r, t, b;
-    Rect **areas;
-    guint i;
 
-    areas = pick_head(client);
-    i = (config_place_monitor != OB_PLACE_MONITOR_ANY) ?
-        0 : g_random_int_range(0, screen_num_monitors);
+    ob_debug("placing randomly");
 
-    l = areas[i]->x;
-    t = areas[i]->y;
-    r = areas[i]->x + areas[i]->width - client->frame->area.width;
-    b = areas[i]->y + areas[i]->height - client->frame->area.height;
+    l = area->x;
+    t = area->y;
+    r = area->x + area->width - client->frame->area.width;
+    b = area->y + area->height - client->frame->area.height;
 
     if (r > l) *x = g_random_int_range(l, r + 1);
-    else       *x = areas[i]->x;
+    else       *x = area->x;
     if (b > t) *y = g_random_int_range(t, b + 1);
-    else       *y = areas[i]->y;
-
-    for (i = 0; i < screen_num_monitors; ++i)
-        g_slice_free(Rect, areas[i]);
-    g_free(areas);
+    else       *y = area->y;
 
     return TRUE;
 }
@@ -228,118 +306,108 @@ enum {
     IGNORE_END        = 7
 };
 
-static gboolean place_nooverlap(ObClient *c, gint *x, gint *y)
+static gboolean place_nooverlap(ObClient *c, Rect *area, gint *x, gint *y)
 {
-    Rect **areas;
     gint ignore;
     gboolean ret;
     gint maxsize;
     GSList *spaces = NULL, *sit, *maxit;
-    guint i;
 
-    areas = pick_head(c);
+    ob_debug("placing nonoverlap");
+
     ret = FALSE;
     maxsize = 0;
     maxit = NULL;
 
     /* try ignoring different things to find empty space */
     for (ignore = 0; ignore < IGNORE_END && !ret; ignore++) {
-        /* try all monitors in order of preference, but only the first one
-           if config_place_monitor is MOUSE or ACTIVE */
-        for (i = 0; (i < (config_place_monitor != OB_PLACE_MONITOR_ANY ?
-                          1 : screen_num_monitors) && !ret); ++i)
-        {
-            GList *it;
-
-            /* add the whole monitor */
-            spaces = area_add(spaces, areas[i]);
-
-            /* go thru all the windows */
-            for (it = client_list; it; it = g_list_next(it)) {
-                ObClient *test = it->data;
-
-                /* should we ignore this client? */
-                if (screen_showing_desktop) continue;
-                if (c == test) continue;
-                if (test->iconic) continue;
-                if (c->desktop != DESKTOP_ALL) {
-                    if (test->desktop != c->desktop &&
-                        test->desktop != DESKTOP_ALL) continue;
-                } else {
-                    if (test->desktop != screen_desktop &&
-                        test->desktop != DESKTOP_ALL) continue;
-                }
-                if (test->type == OB_CLIENT_TYPE_SPLASH ||
-                    test->type == OB_CLIENT_TYPE_DESKTOP) continue;
-
-
-                if ((ignore >= IGNORE_FULLSCREEN) &&
-                    test->fullscreen) continue;
-                if ((ignore >= IGNORE_MAXIMIZED) &&
-                    test->max_horz && test->max_vert) continue;
-                if ((ignore >= IGNORE_MENUTOOL) &&
-                    (test->type == OB_CLIENT_TYPE_MENU ||
-                     test->type == OB_CLIENT_TYPE_TOOLBAR) &&
-                    client_has_parent(c)) continue;
-                /*
-                if ((ignore >= IGNORE_SHADED) &&
-                    test->shaded) continue;
-                */
-                if ((ignore >= IGNORE_NONGROUP) &&
-                    client_has_group_siblings(c) &&
-                    test->group != c->group) continue;
-                if ((ignore >= IGNORE_BELOW) &&
-                    test->layer < c->layer) continue;
-                /*
-                if ((ignore >= IGNORE_NONFOCUS) &&
-                    focus_client != test) continue;
-                */
-                /* don't ignore this window, so remove it from the available
-                   area */
-                spaces = area_remove(spaces, &test->frame->area);
+        GList *it;
+
+        /* add the whole monitor */
+        spaces = area_add(spaces, area);
+
+        /* go thru all the windows */
+        for (it = client_list; it; it = g_list_next(it)) {
+            ObClient *test = it->data;
+
+            /* should we ignore this client? */
+            if (screen_showing_desktop) continue;
+            if (c == test) continue;
+            if (test->iconic) continue;
+            if (c->desktop != DESKTOP_ALL) {
+                if (test->desktop != c->desktop &&
+                    test->desktop != DESKTOP_ALL) continue;
+            } else {
+                if (test->desktop != screen_desktop &&
+                    test->desktop != DESKTOP_ALL) continue;
             }
+            if (test->type == OB_CLIENT_TYPE_SPLASH ||
+                test->type == OB_CLIENT_TYPE_DESKTOP) continue;
+
+
+            if ((ignore >= IGNORE_FULLSCREEN) &&
+                test->fullscreen) continue;
+            if ((ignore >= IGNORE_MAXIMIZED) &&
+                test->max_horz && test->max_vert) continue;
+            if ((ignore >= IGNORE_MENUTOOL) &&
+                (test->type == OB_CLIENT_TYPE_MENU ||
+                 test->type == OB_CLIENT_TYPE_TOOLBAR) &&
+                client_has_parent(c)) continue;
+            /*
+              if ((ignore >= IGNORE_SHADED) &&
+              test->shaded) continue;
+            */
+            if ((ignore >= IGNORE_NONGROUP) &&
+                client_has_group_siblings(c) &&
+                test->group != c->group) continue;
+            if ((ignore >= IGNORE_BELOW) &&
+                test->layer < c->layer) continue;
+            /*
+              if ((ignore >= IGNORE_NONFOCUS) &&
+              focus_client != test) continue;
+            */
+            /* don't ignore this window, so remove it from the available
+               area */
+            spaces = area_remove(spaces, &test->frame->area);
+        }
 
-            if (ignore < IGNORE_DOCK) {
-                Rect a;
-                dock_get_area(&a);
-                spaces = area_remove(spaces, &a);
-            }
+        if (ignore < IGNORE_DOCK) {
+            Rect a;
+            dock_get_area(&a);
+            spaces = area_remove(spaces, &a);
+        }
 
-            for (sit = spaces; sit; sit = g_slist_next(sit)) {
-                Rect *r = sit->data;
+        for (sit = spaces; sit; sit = g_slist_next(sit)) {
+            Rect *r = sit->data;
 
-                if (r->width >= c->frame->area.width &&
-                    r->height >= c->frame->area.height &&
-                    r->width * r->height > maxsize)
-                {
-                    maxsize = r->width * r->height;
-                    maxit = sit;
-                }
+            if (r->width >= c->frame->area.width &&
+                r->height >= c->frame->area.height &&
+                r->width * r->height > maxsize)
+            {
+                maxsize = r->width * r->height;
+                maxit = sit;
             }
+        }
 
-            if (maxit) {
-                Rect *r = maxit->data;
+        if (maxit) {
+            Rect *r = maxit->data;
 
-                /* center it in the area */
-                *x = r->x;
-                *y = r->y;
-                if (config_place_center) {
-                    *x += (r->width - c->frame->area.width) / 2;
-                    *y += (r->height - c->frame->area.height) / 2;
-                }
-                ret = TRUE;
+            /* center it in the area */
+            *x = r->x;
+            *y = r->y;
+            if (config_place_center) {
+                *x += (r->width - c->frame->area.width) / 2;
+                *y += (r->height - c->frame->area.height) / 2;
             }
+            ret = TRUE;
+        }
 
-            while (spaces) {
-                g_slice_free(Rect, spaces->data);
-                spaces = g_slist_delete_link(spaces, spaces);
-            }
+        while (spaces) {
+            g_slice_free(Rect, spaces->data);
+            spaces = g_slist_delete_link(spaces, spaces);
         }
     }
 
-    for (i = 0; i < screen_num_monitors; ++i)
-        g_slice_free(Rect, areas[i]);
-    g_free(areas);
     return ret;
 }
 
@@ -349,6 +417,8 @@ static gboolean place_under_mouse(ObClient *client, gint *x, gint *y)
     gint px, py;
     Rect *area;
 
+    ob_debug("placing under mouse");
+
     if (!screen_pointer_pos(&px, &py))
         return FALSE;
     area = pick_pointer_head(client);
@@ -376,27 +446,17 @@ static gboolean place_per_app_setting(ObClient *client, gint *x, gint *y,
     if (!settings || (settings && !settings->pos_given))
         return FALSE;
 
+    ob_debug("placing by per-app settings");
+
     /* Find which head the pointer is on */
     if (settings->monitor == 0)
         /* this can return NULL */
         screen = pick_pointer_head(client);
-    else if (settings->monitor > 0 &&
-             (guint)settings->monitor <= screen_num_monitors)
-        screen = screen_area(client->desktop, (guint)settings->monitor - 1,
-                             NULL);
-
-    /* if we have't found a screen yet.. */
-    if (!screen) {
-        Rect **areas;
-        guint i;
-
-        areas = pick_head(client);
-        screen = areas[0];
-
-        /* don't free the first one, it's being set as "screen" */
-        for (i = 1; i < screen_num_monitors; ++i)
-            g_slice_free(Rect, areas[i]);
-        g_free(areas);
+    else {
+        guint m = settings->monitor;
+        if (m < 1 || m > screen_num_monitors)
+            m = screen_monitor_primary(TRUE) + 1;
+        screen = screen_area(client->desktop, m - 1, NULL);
     }
 
     if (settings->position.x.center)
@@ -423,12 +483,16 @@ static gboolean place_per_app_setting(ObClient *client, gint *x, gint *y,
     return TRUE;
 }
 
-static gboolean place_transient_splash(ObClient *client, gint *x, gint *y)
+static gboolean place_transient_splash(ObClient *client, Rect *area,
+                                       gint *x, gint *y)
 {
     if (client->type == OB_CLIENT_TYPE_DIALOG) {
         GSList *it;
         gboolean first = TRUE;
         gint l, r, t, b;
+
+        ob_debug("placing dialog");
+
         for (it = client->parents; it; it = g_slist_next(it)) {
             ObClient *m = it->data;
             if (!m->iconic) {
@@ -456,17 +520,10 @@ static gboolean place_transient_splash(ObClient *client, gint *x, gint *y)
     if (client->type == OB_CLIENT_TYPE_DIALOG ||
         client->type == OB_CLIENT_TYPE_SPLASH)
     {
-        Rect **areas;
-        guint i;
-
-        areas = pick_head(client);
+        ob_debug("placing dialog or splash");
 
-        *x = (areas[0]->width - client->frame->area.width) / 2 + areas[0]->x;
-        *y = (areas[0]->height - client->frame->area.height) / 2 + areas[0]->y;
-
-        for (i = 0; i < screen_num_monitors; ++i)
-            g_slice_free(Rect, areas[i]);
-        g_free(areas);
+        *x = (area->width - client->frame->area.width) / 2 + area->x;
+        *y = (area->height - client->frame->area.height) / 2 + area->y;
         return TRUE;
     }
 
@@ -475,9 +532,10 @@ static gboolean place_transient_splash(ObClient *client, gint *x, gint *y)
 
 /*! Return TRUE if openbox chose the position for the window, and FALSE if
   the application chose it */
-gboolean place_client(ObClient *client, gint *x, gint *y,
+gboolean place_client(ObClient *client, gboolean foreground, gint *x, gint *y,
                       ObAppSettings *settings)
 {
+    Rect *area;
     gboolean ret;
 
     /* per-app settings override program specified position
@@ -488,15 +546,19 @@ gboolean place_client(ObClient *client, gint *x, gint *y,
          !(settings && settings->pos_given)))
         return FALSE;
 
+    area = pick_head(client, foreground);
+
     /* try a number of methods */
     ret = place_per_app_setting(client, x, y, settings) ||
-        place_transient_splash(client, x, y) ||
+        place_transient_splash(client, area, x, y) ||
         (config_place_policy == OB_PLACE_POLICY_MOUSE &&
          place_under_mouse(client, x, y)) ||
-        place_nooverlap(client, x, y) ||
-        place_random(client, x, y);
+        place_nooverlap(client, area, x, y) ||
+        place_random(client, area, x, y);
     g_assert(ret);
 
+    g_slice_free(Rect, area);
+
     /* get where the client should be */
     frame_frame_gravity(client->frame, x, y);
     return TRUE;
index 6a9add4..94e2dc0 100644 (file)
@@ -35,10 +35,11 @@ typedef enum
 {
     OB_PLACE_MONITOR_ANY,
     OB_PLACE_MONITOR_ACTIVE,
-    OB_PLACE_MONITOR_MOUSE
+    OB_PLACE_MONITOR_MOUSE,
+    OB_PLACE_MONITOR_PRIMARY
 } ObPlaceMonitor;
 
-gboolean place_client(struct _ObClient *client, gint *x, gint *y,
-                      struct _ObAppSettings *settings);
+gboolean place_client(struct _ObClient *client, gboolean foreground,
+                      gint *x, gint *y, struct _ObAppSettings *settings);
 
 #endif