If a window places itself at (0,0) and there are struts there, assume it is a bug...
[dana/openbox.git] / openbox / client.c
index 98a27dc..6d934bd 100644 (file)
@@ -31,6 +31,7 @@
 #include "grab.h"
 #include "prompt.h"
 #include "focus.h"
+#include "focus_cycle.h"
 #include "stacking.h"
 #include "openbox.h"
 #include "group.h"
@@ -75,7 +76,7 @@ static RrImage *client_default_icon     = NULL;
 static void client_get_all(ObClient *self, gboolean real);
 static void client_get_startup_id(ObClient *self);
 static void client_get_session_ids(ObClient *self);
-static void client_save_session_ids(ObClient *self);
+static void client_save_app_rule_values(ObClient *self);
 static void client_get_area(ObClient *self);
 static void client_get_desktop(ObClient *self);
 static void client_get_state(ObClient *self);
@@ -201,9 +202,10 @@ void client_manage(Window window, ObPrompt *prompt)
     gboolean activate = FALSE;
     ObAppSettings *settings;
     gboolean transient = FALSE;
-    Rect place, *monitor;
+    Rect place, *monitor, *allmonitors;
     Time launch_time, map_time;
     guint32 user_time;
+    gboolean obplaced;
 
     ob_debug("Managing window: 0x%lx", window);
 
@@ -223,6 +225,7 @@ void client_manage(Window window, ObPrompt *prompt)
     self->obwin.type = OB_WINDOW_CLASS_CLIENT;
     self->window = window;
     self->prompt = prompt;
+    self->managed = TRUE;
 
     /* non-zero defaults */
     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
@@ -234,7 +237,8 @@ void client_manage(Window window, ObPrompt *prompt)
 
     ob_debug("Window type: %d", self->type);
     ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
-    ob_debug("Window name: %s class: %s role: %s", self->name, self->class, self->role);
+    ob_debug("Window name: %s class: %s role: %s title: %s",
+             self->name, self->class, self->role, self->title);
 
     /* per-app settings override stuff from client_get_all, and return the
        settings for other uses too. the returned settings is a shallow copy,
@@ -308,6 +312,7 @@ void client_manage(Window window, ObPrompt *prompt)
     /* where the frame was placed is where the window was originally */
     place = self->area;
     monitor = screen_physical_area_monitor(screen_find_monitor(&place));
+    allmonitors = screen_physical_area_all_monitors();
 
     /* figure out placement for the window if the window is new */
     if (ob_state() == OB_STATE_RUNNING) {
@@ -327,9 +332,23 @@ void client_manage(Window window, ObPrompt *prompt)
                      "program + user specified" :
                      "BADNESS !?")))), place.width, place.height);
 
-        /* splash screens are also returned as TRUE for transient,
-           and so will be forced on screen below */
-        transient = place_client(self, &place.x, &place.y, settings);
+        obplaced = place_client(self, &place.x, &place.y, settings);
+
+        /* watch for buggy apps that ask to be placed at (0,0) when there is
+           a strut there */
+        if (!obplaced && place.x == 0 && place.y == 0 &&
+            /* oldschool fullscreen windows are allowed */
+            !(self->decorations == 0 && (RECT_EQUAL(place, *monitor) ||
+                                         RECT_EQUAL(place, *allmonitors))))
+        {
+            Rect *r;
+
+            r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
+            place.x = r->x;
+            place.y = r->y;
+            ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
+            g_free(r);
+        }
 
         /* make sure the window is visible. */
         client_find_onscreen(self, &place.x, &place.y,
@@ -345,11 +364,13 @@ void client_manage(Window window, ObPrompt *prompt)
                                 it is up to the placement routines to avoid
                                 the xinerama divides)
 
-                                splash screens get "transient" set to TRUE by
-                                the place_client call
+                                children and splash screens are forced on
+                                screen, but i don't remember why i decided to
+                                do that.
                              */
                              ob_state() == OB_STATE_RUNNING &&
-                             (transient ||
+                             (self->type == OB_CLIENT_TYPE_DIALOG ||
+                              self->type == OB_CLIENT_TYPE_SPLASH ||
                               (!((self->positioned & USPosition) ||
                                  (settings && settings->pos_given)) &&
                                client_normal(self) &&
@@ -359,7 +380,8 @@ void client_manage(Window window, ObPrompt *prompt)
                                   makes its fullscreen window fit the screen
                                   but it is not USSize'd or USPosition'd) */
                                !(self->decorations == 0 &&
-                                 RECT_EQUAL(place, *monitor)))));
+                                 (RECT_EQUAL(place, *monitor) ||
+                                  RECT_EQUAL(place, *allmonitors))))));
     }
 
     /* if the window isn't user-sized, then make it fit inside
@@ -379,7 +401,8 @@ void client_manage(Window window, ObPrompt *prompt)
           /* don't shrink oldschool fullscreen windows to fit inside the
              struts (fixes Acroread, which makes its fullscreen window
              fit the screen but it is not USSize'd or USPosition'd) */
-          !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
+          !(self->decorations == 0 && (RECT_EQUAL(place, *monitor) ||
+                                       RECT_EQUAL(place, *allmonitors))))))
     {
         Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
 
@@ -419,6 +442,8 @@ void client_manage(Window window, ObPrompt *prompt)
 
     g_free(monitor);
     monitor = NULL;
+    g_free(allmonitors);
+    allmonitors = NULL;
 
     ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
                   activate ? "yes" : "no");
@@ -551,6 +576,8 @@ void client_unmanage(ObClient *self)
 
     mouse_grab_for_client(self, FALSE);
 
+    self->managed = FALSE;
+
     /* remove the window from our save set, unless we are managing an internal
        ObPrompt window */
     if (!self->prompt)
@@ -791,10 +818,10 @@ static ObAppSettings *client_get_settings_state(ObClient *self)
         ObAppSettings *app = it->data;
         gboolean match = TRUE;
 
-        g_assert(app->name != NULL || app->class != NULL);
+        g_assert(app->name != NULL || app->class != NULL ||
+                 app->role != NULL || app->title != NULL ||
+                 (signed)app->type >= 0);
 
-        /* we know that either name or class is not NULL so it will have to
-           match to use the rule */
         if (app->name &&
             !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
             match = FALSE;
@@ -806,8 +833,13 @@ static ObAppSettings *client_get_settings_state(ObClient *self)
                  !g_pattern_match(app->role,
                                   strlen(self->role), self->role, NULL))
             match = FALSE;
-        else if ((signed)app->type >= 0 && app->type != self->type)
+        else if (app->title &&
+                 !g_pattern_match(app->title,
+                                  strlen(self->title), self->title, NULL))
+            match = FALSE;
+        else if ((signed)app->type >= 0 && app->type != self->type) {
             match = FALSE;
+        }
 
         if (match) {
             ob_debug("Window matching: %s", app->name);
@@ -1078,7 +1110,6 @@ static void client_get_all(ObClient *self, gboolean real)
     /* get the session related properties, these can change decorations
        from per-app settings */
     client_get_session_ids(self);
-    client_save_session_ids(self);
 
     /* now we got everything that can affect the decorations */
     if (!real)
@@ -1087,6 +1118,9 @@ static void client_get_all(ObClient *self, gboolean real)
     /* get this early so we have it for debugging */
     client_update_title(self);
 
+    /* save the values of the variables used for app rule matching */
+    client_save_app_rule_values(self);
+
     client_update_protocols(self);
 
     client_update_wmhints(self);
@@ -1912,6 +1946,8 @@ void client_update_wmhints(ObClient *self)
 
         XFree(hints);
     }
+
+    focus_cycle_addremove(self, TRUE);
 }
 
 void client_update_title(ObClient *self)
@@ -2292,13 +2328,37 @@ static void client_get_session_ids(ObClient *self)
     }
 }
 
-/*! Save the session IDs as seen by Openbox when the window mapped, so that
-  users can still access them later if the app changes them */
-static void client_save_session_ids(ObClient *self)
+/*! Save the properties used for app matching rules, as seen by Openbox when
+  the window mapped, so that users can still access them later if the app
+  changes them */
+static void client_save_app_rule_values(ObClient *self)
 {
-    OBT_PROP_SETS(self->window, OB_ROLE, utf8, self->role);
-    OBT_PROP_SETS(self->window, OB_NAME, utf8, self->name);
-    OBT_PROP_SETS(self->window, OB_CLASS, utf8, self->class);
+    const gchar *type;
+
+    OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role);
+    OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name);
+    OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class);
+    OBT_PROP_SETS(self->window, OB_APP_TITLE, utf8, self->original_title);
+
+    switch (self->type) {
+    case OB_CLIENT_TYPE_NORMAL:
+        type = "normal"; break;
+    case OB_CLIENT_TYPE_DIALOG:
+        type = "dialog"; break;
+    case OB_CLIENT_TYPE_UTILITY:
+        type = "utility"; break;
+    case OB_CLIENT_TYPE_MENU:
+        type = "menu"; break;
+    case OB_CLIENT_TYPE_TOOLBAR:
+        type = "toolbar"; break;
+    case OB_CLIENT_TYPE_SPLASH:
+        type = "splash"; break;
+    case OB_CLIENT_TYPE_DESKTOP:
+        type = "desktop"; break;
+    case OB_CLIENT_TYPE_DOCK:
+        type = "dock"; break;
+    }
+    OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type);
 }
 
 static void client_change_wm_state(ObClient *self)
@@ -2418,9 +2478,10 @@ gboolean client_has_parent(ObClient *self)
 static ObStackingLayer calc_layer(ObClient *self)
 {
     ObStackingLayer l;
-    Rect *monitor;
+    Rect *monitor, *allmonitors;
 
     monitor = screen_physical_area_monitor(client_monitor(self));
+    allmonitors = screen_physical_area_all_monitors();
 
     if (self->type == OB_CLIENT_TYPE_DESKTOP)
         l = OB_STACKING_LAYER_DESKTOP;
@@ -2434,7 +2495,8 @@ static ObStackingLayer calc_layer(ObClient *self)
               */
               (self->decorations == 0 &&
                !(self->max_horz && self->max_vert) &&
-               RECT_EQUAL(self->area, *monitor))) &&
+               (RECT_EQUAL(self->area, *monitor) ||
+                RECT_EQUAL(self->area, *allmonitors)))) &&
              /* you are fullscreen while you or your children are focused.. */
              (client_focused(self) || client_search_focus_tree(self) ||
               /* you can be fullscreen if you're on another desktop */
@@ -2450,6 +2512,7 @@ static ObStackingLayer calc_layer(ObClient *self)
     else l = OB_STACKING_LAYER_NORMAL;
 
     g_free(monitor);
+    g_free(allmonitors);
 
     return l;
 }
@@ -3149,7 +3212,7 @@ static void client_iconify_recursive(ObClient *self,
                 self->iconic = iconic;
 
                 /* update the focus lists.. iconic windows go to the bottom of
-                   the list */
+                   the list. this will also call focus_cycle_addremove(). */
                 focus_order_to_bottom(self);
 
                 changed = TRUE;
@@ -3161,9 +3224,10 @@ static void client_iconify_recursive(ObClient *self,
                 self->desktop != DESKTOP_ALL)
                 client_set_desktop(self, screen_desktop, FALSE, FALSE);
 
-            /* this puts it after the current focused window */
-            focus_order_remove(self);
-            focus_order_add_new(self);
+            /* this puts it after the current focused window, this will
+               also cause focus_cycle_addremove() to be called for the
+               client */
+            focus_order_like_new(self);
 
             changed = TRUE;
         }
@@ -3496,6 +3560,8 @@ static void client_set_desktop_recursive(ObClient *self,
             /* the new desktop's geometry may be different, so we may need to
                resize, for example if we are maximized */
             client_reconfigure(self, FALSE);
+
+        focus_cycle_addremove(self, FALSE);
     }
 
     /* move all transients */
@@ -3511,6 +3577,8 @@ void client_set_desktop(ObClient *self, guint target,
 {
     self = client_search_top_direct_parent(self);
     client_set_desktop_recursive(self, target, donthide, dontraise);
+
+    focus_cycle_addremove(NULL, TRUE);
 }
 
 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
@@ -3724,6 +3792,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
         client_hilite(self, demands_attention);
 
     client_change_state(self); /* change the hint to reflect these changes */
+
+    focus_cycle_addremove(self, TRUE);
 }
 
 ObClient *client_focus_target(ObClient *self)