]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
have the clientpadding color show during resize, and no flashing on unmap. we win !
[chaz/openbox] / openbox / client.c
index 73696c897518dd98fce6f51df139dc7a16a21bfb..48f5d2a9c86bf69ff5a866a45604ce6aad4b3e3c 100644 (file)
@@ -65,10 +65,8 @@ typedef struct
 GList            *client_list          = NULL;
 
 static GSList *client_destroy_notifies = NULL;
-static GSList *client_hide_notifies    = NULL;
 
 static void client_get_all(ObClient *self, gboolean real);
-static void client_toggle_border(ObClient *self, gboolean show);
 static void client_get_startup_id(ObClient *self);
 static void client_get_session_ids(ObClient *self);
 static void client_get_area(ObClient *self);
@@ -80,7 +78,8 @@ static void client_get_colormap(ObClient *self);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
 static void client_change_wm_state(ObClient *self);
-static void client_apply_startup_state(ObClient *self, gint x, gint y);
+static void client_apply_startup_state(ObClient *self,
+                                       gint x, gint y, gint w, gint h);
 static void client_restore_session_state(ObClient *self);
 static gboolean client_restore_session_stacking(ObClient *self);
 static ObAppSettings *client_get_settings_state(ObClient *self);
@@ -139,29 +138,6 @@ void client_remove_destroy_notify(ObClientCallback func)
     }
 }
 
-void client_add_hide_notify(ObClientCallback func, gpointer data)
-{
-    ClientCallback *d = g_new(ClientCallback, 1);
-    d->func = func;
-    d->data = data;
-    client_hide_notifies = g_slist_prepend(client_hide_notifies, d);
-}
-
-void client_remove_hide_notify(ObClientCallback func)
-{
-    GSList *it;
-
-    for (it = client_hide_notifies; it; it = g_slist_next(it)) {
-        ClientCallback *d = it->data;
-        if (d->func == func) {
-            g_free(d);
-            client_hide_notifies =
-                g_slist_delete_link(client_hide_notifies, it);
-            break;
-        }
-    }
-}
-
 void client_set_list()
 {
     Window *windows, *win_it;
@@ -265,7 +241,8 @@ void client_manage(Window window)
     XWMHints *wmhint;
     gboolean activate = FALSE;
     ObAppSettings *settings;
-    gint newx, newy;
+    gint placex, placey, placew, placeh;
+    gboolean transient = FALSE;
 
     grab_server(TRUE);
 
@@ -302,7 +279,7 @@ void client_manage(Window window)
         XFree(wmhint);
     }
 
-    ob_debug("Managing window: %lx\n", window);
+    ob_debug("Managing window: 0x%lx\n", window);
 
     /* choose the events we want to receive on the CLIENT window */
     attrib_set.event_mask = CLIENT_EVENTMASK;
@@ -339,17 +316,15 @@ void client_manage(Window window)
     grab_server(FALSE);
 
     /* per-app settings override stuff from client_get_all, and return the
-       settings for other uses too */
+       settings for other uses too. the returned settings is a shallow copy,
+       that needs to be freed with g_free(). */
     settings = client_get_settings_state(self);
     /* the session should get the last say thought */
     client_restore_session_state(self);
 
     /* now we have all of the window's information so we can set this up */
-    client_setup_decor_and_functions(self);
+    client_setup_decor_and_functions(self, FALSE);
 
-    /* remove the client's border (and adjust re gravity) */
-    client_toggle_border(self, FALSE);
-     
     {
         Time t = sn_app_started(self->startup_id, self->class);
         if (t) self->user_time = t;
@@ -371,7 +346,7 @@ void client_manage(Window window)
         !self->iconic &&
         /* this means focus=true for window is same as config_focus_new=true */
         ((config_focus_new || (settings && settings->focus == 1)) ||
-         client_search_focus_parent(self)) &&
+         client_search_focus_tree_full(self)) &&
         /* this checks for focus=false for the window */
         (!settings || settings->focus != 0) &&
         /* note the check against Type_Normal/Dialog, not client_normal(self),
@@ -383,20 +358,39 @@ void client_manage(Window window)
         activate = TRUE;
     }
 
-    /* get the current position */
-    newx = self->area.x;
-    newy = self->area.y;
+    /* adjust the frame to the client's size before showing or placing
+       the window */
+    frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+    frame_adjust_client_area(self->frame);
 
-    /* figure out placement for the window */
-    if (ob_state() == OB_STATE_RUNNING) {
-        gboolean transient;
+    /* where the frame was placed is where the window was originally */
+    placex = self->area.x;
+    placey = self->area.y;
+    placew = self->area.width;
+    placeh = self->area.height;
 
-        transient = place_client(self, &newx, &newy, settings);
+    /* figure out placement for the window if the window is new */
+    if (ob_state() == OB_STATE_RUNNING) {
+        ob_debug("Positioned: %s @ %d %d\n",
+                 (!self->positioned ? "no" :
+                  (self->positioned == PPosition ? "program specified" :
+                   (self->positioned == USPosition ? "user specified" :
+                    (self->positioned == (PPosition | USPosition) ?
+                     "program + user specified" :
+                     "BADNESS !?")))), placex, placey);
+
+        ob_debug("Sized: %s @ %d %d\n",
+                 (!self->sized ? "no" :
+                  (self->sized == PSize ? "program specified" :
+                   (self->sized == USSize ? "user specified" :
+                    (self->sized == (PSize | USSize) ?
+                     "program + user specified" :
+                     "BADNESS !?")))), placew, placeh);
+
+        transient = place_client(self, &placex, &placey, settings);
 
         /* make sure the window is visible. */
-        client_find_onscreen(self, &newx, &newy,
-                             self->area.width,
-                             self->area.height,
+        client_find_onscreen(self, &placex, &placey, placew, placeh,
                              /* non-normal clients has less rules, and
                                 windows that are being restored from a
                                 session do also. we can assume you want
@@ -408,25 +402,58 @@ void client_manage(Window window)
                                 it is up to the placement routines to avoid
                                 the xinerama divides) */
                              transient ||
-                             (((self->positioned & PPosition) &&
-                               !(self->positioned & USPosition)) &&
+                             (!(self->positioned & USPosition) &&
                               client_normal(self) &&
                               !self->session));
     }
 
-    /* do this after the window is placed, so the premax/prefullscreen numbers
-       won't be all wacko!!
-       also, this moves the window to the position where it has been placed
-    */
-    ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
-             self->window, newx, newy, self->area.width, self->area.height);
+    /* if the window isn't user-sized, then make it fit inside
+       the visible screen area on its monitor. Use basically the same rules
+       for forcing the window on screen in the client_find_onscreen call.
+
+       do this after place_client, it chooses the monitor! */
+    if (transient ||
+        (!(self->sized & USSize) &&
+         client_normal(self) &&
+         !self->session))
+    {
+        /* make a copy to modify */
+        Rect a = *screen_area_monitor(self->desktop, client_monitor(self));
+
+        /* shrink by the frame's area */
+        a.width -= self->frame->size.left + self->frame->size.right;
+        a.height -= self->frame->size.top + self->frame->size.bottom;
+
+        /* fit the window inside the area */
+        if (placew > a.width || self->area.height > a.height) {
+            placew = MAX(MIN(MIN(self->area.width, a.width),
+                             self->max_size.width),
+                         self->min_size.width);
+            placeh = MAX(MIN(MIN(self->area.height, a.height),
+                             self->max_size.height),
+                         self->min_size.height);
+
+            ob_debug("setting window size to %dx%d\n",
+                     self->area.width, self->area.height);
+        }
+    }
+
+
+    ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
+             "some restrictions may apply\n",
+             self->window, placex, placey, placew, placeh);
     if (self->session)
-        ob_debug("session requested %d %d\n",
-                 self->session->x, self->session->y);
+        ob_debug("  but session requested %d, %d  %d x %d instead, "
+                 "overriding\n",
+                 self->session->x, self->session->y,
+                 self->session->w, self->session->h);
 
-    client_apply_startup_state(self, newx, newy);
+    /* do this after the window is placed, so the premax/prefullscreen numbers
+       won't be all wacko!!
 
-    mouse_grab_for_client(self, TRUE);
+       this also places the window
+    */
+    client_apply_startup_state(self, placex, placey, placew, placeh);
 
     if (activate) {
         guint32 last_time = focus_client ?
@@ -449,8 +476,8 @@ void client_manage(Window window)
                           "Not focusing the window because its on another "
                           "desktop\n");
         }
-        /* If something is focused, and it's not our parent... */
-        else if (focus_client && client_search_focus_parent(self) == NULL)
+        /* If something is focused, and it's not our relative... */
+        else if (focus_client && client_search_focus_tree_full(self) == NULL)
         {
             /* If time stamp is old, don't steal focus */
             if (self->user_time && last_time &&
@@ -461,15 +488,37 @@ void client_manage(Window window)
                               "Not focusing the window because the time is "
                               "too old\n");
             }
+            /* If its a transient (and parents aren't focused) and the time
+               is ambiguous (either the current focus target doesn't have
+               a timestamp, or they are the same (we probably inherited it
+               from them) */
+            else if (client_has_parent(self) &&
+                     (!last_time || self->user_time == last_time))
+            {
+                activate = FALSE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because it is a "
+                              "transient, and the time is very ambiguous\n");
+            }
             /* Don't steal focus from globally active clients.
                I stole this idea from KWin. It seems nice.
              */
-            if (!(focus_client->can_focus || focus_client->focus_notify)) {
+            else if (!(focus_client->can_focus ||
+                       focus_client->focus_notify))
+            {
                 activate = FALSE;
                 ob_debug_type(OB_DEBUG_FOCUS,
                               "Not focusing the window because a globally "
                               "active client has focus\n");
             }
+            /* Don't move focus if it's not going to go to this window
+               anyway */
+            else if (client_focus_target(self) != self) {
+                activate = FALSE;
+                ob_debug_type(OB_DEBUG_FOCUS,
+                              "Not focusing the window because another window "
+                              "would get the focus anyway\n");
+            }
         }
 
         if (!activate) {
@@ -494,8 +543,7 @@ void client_manage(Window window)
             stacking_raise(CLIENT_AS_WINDOW(self));
     }
 
-    /* adjust the frame to the client's size before showing the window */
-    frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+    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
@@ -518,7 +566,11 @@ void client_manage(Window window)
     /* update the list hints */
     client_set_list();
 
-    ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
+    /* free the ObAppSettings shallow copy */
+    g_free(settings);
+
+    ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
+             window, self->frame->window, self->class);
 
     return;
 }
@@ -538,14 +590,22 @@ ObClient *client_fake_manage(Window window)
 
     client_get_all(self, FALSE);
     /* per-app settings override stuff, and return the settings for other
-       uses too */
+       uses too. this returns a shallow copy that needs to be freed */
     settings = client_get_settings_state(self);
 
-    client_setup_decor_and_functions(self);
+    client_setup_decor_and_functions(self, FALSE);
 
     /* create the decoration frame for the client window and adjust its size */
     self->frame = frame_new(self);
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+
+    ob_debug("gave extents left %d right %d top %d bottom %d\n",
+             self->frame->size.left, self->frame->size.right, 
+             self->frame->size.top, self->frame->size.bottom);
+
+    /* free the ObAppSettings shallow copy */
+    g_free(settings);
+
     return self;
 }
 
@@ -560,7 +620,8 @@ void client_unmanage(ObClient *self)
     guint j;
     GSList *it;
 
-    ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window,
+    ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
+             self->window, self->frame->window,
              self->class, self->title ? self->title : "");
 
     g_assert(self != NULL);
@@ -575,7 +636,7 @@ void client_unmanage(ObClient *self)
 
     /* ignore enter events from the unmap so it doesnt mess with the
        focus */
-    event_ignore_queued_enters();
+    event_ignore_all_queued_enters();
 
     mouse_grab_for_client(self, FALSE);
 
@@ -632,9 +693,6 @@ void client_unmanage(ObClient *self)
     {
         Rect a;
 
-        /* give the client its border back */
-        client_toggle_border(self, TRUE);
-
         a = self->area;
 
         if (self->fullscreen)
@@ -651,6 +709,8 @@ void client_unmanage(ObClient *self)
         }
 
         self->fullscreen = self->max_horz = self->max_vert = FALSE;
+        /* let it be moved and resized no matter what */
+        self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
         self->decorations = 0; /* unmanaged windows have no decor */
 
         client_move_resize(self, a.x, a.y, a.width, a.height);
@@ -704,73 +764,82 @@ void client_fake_unmanage(ObClient *self)
     g_free(self);
 }
 
+/*! Returns a new structure containing the per-app settings for this client.
+  The returned structure needs to be freed with g_free. */
 static ObAppSettings *client_get_settings_state(ObClient *self)
 {
-    ObAppSettings *settings = NULL;
+    ObAppSettings *settings;
     GSList *it;
 
+    settings = config_create_app_settings();
+
     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
         ObAppSettings *app = it->data;
-        
-        if ((app->name && !app->class && !strcmp(app->name, self->name))
-            || (app->class && !app->name && !strcmp(app->class, self->class))
-            || (app->class && app->name && !strcmp(app->class, self->class)
-                && !strcmp(app->name, self->name)))
-        {
-            /* Match if no role was specified in the per app setting, or if the
-             * string matches the beginning of the role, since apps like to set
-             * the role to things like browser-window-23c4b2f */
-            if (!app->role
-                || !strncmp(app->role, self->role, strlen(app->role)))
-            {
-                ob_debug("Window matching: %s\n", app->name);
-                /* use this one */
-                settings = app;
-                break;
-            }
-        }
+        gboolean match = TRUE;
+
+        g_assert(app->name != NULL || app->class != NULL);
+
+        /* 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;
+        else if (app->class &&
+                !g_pattern_match(app->class,
+                                 strlen(self->class), self->class, NULL))
+            match = FALSE;
+        else if (app->role &&
+                 !g_pattern_match(app->role,
+                                  strlen(self->role), self->role, NULL))
+            match = FALSE;
+
+        if (match) {
+            ob_debug("Window matching: %s\n", app->name);
+
+            /* copy the settings to our struct, overriding the existing
+               settings if they are not defaults */
+            config_app_settings_copy_non_defaults(app, settings);
+        }
+    }
+
+    if (settings->shade != -1)
+        self->shaded = !!settings->shade;
+    if (settings->decor != -1)
+        self->undecorated = !settings->decor;
+    if (settings->iconic != -1)
+        self->iconic = !!settings->iconic;
+    if (settings->skip_pager != -1)
+        self->skip_pager = !!settings->skip_pager;
+    if (settings->skip_taskbar != -1)
+        self->skip_taskbar = !!settings->skip_taskbar;
+
+    if (settings->max_vert != -1)
+        self->max_vert = !!settings->max_vert;
+    if (settings->max_horz != -1)
+        self->max_horz = !!settings->max_horz;
+
+    if (settings->fullscreen != -1)
+        self->fullscreen = !!settings->fullscreen;
+
+    if (settings->desktop) {
+        if (settings->desktop == DESKTOP_ALL)
+            self->desktop = settings->desktop;
+        else if (settings->desktop > 0 &&
+                 settings->desktop <= screen_num_desktops)
+            self->desktop = settings->desktop - 1;
+    }
+
+    if (settings->layer == -1) {
+        self->below = TRUE;
+        self->above = FALSE;
     }
-
-    if (settings) {
-        if (settings->shade != -1)
-            self->shaded = !!settings->shade;
-        if (settings->decor != -1)
-            self->undecorated = !settings->decor;
-        if (settings->iconic != -1)
-            self->iconic = !!settings->iconic;
-        if (settings->skip_pager != -1)
-            self->skip_pager = !!settings->skip_pager;
-        if (settings->skip_taskbar != -1)
-            self->skip_taskbar = !!settings->skip_taskbar;
-
-        if (settings->max_vert != -1)
-            self->max_vert = !!settings->max_vert;
-        if (settings->max_horz != -1)
-            self->max_horz = !!settings->max_horz;
-
-        if (settings->fullscreen != -1)
-            self->fullscreen = !!settings->fullscreen;
-
-        if (settings->desktop) {
-            if (settings->desktop == DESKTOP_ALL)
-                self->desktop = settings->desktop;
-            else if (settings->desktop > 0 &&
-                     settings->desktop <= screen_num_desktops)
-                self->desktop = settings->desktop - 1;
-        }
-
-        if (settings->layer == -1) {
-            self->below = TRUE;
-            self->above = FALSE;
-        }
-        else if (settings->layer == 0) {
-            self->below = FALSE;
-            self->above = FALSE;
-        }
-        else if (settings->layer == 1) {
-            self->below = FALSE;
-            self->above = TRUE;
-        }
+    else if (settings->layer == 0) {
+        self->below = FALSE;
+        self->above = FALSE;
+    }
+    else if (settings->layer == 1) {
+        self->below = FALSE;
+        self->above = TRUE;
     }
     return settings;
 }
@@ -795,6 +864,7 @@ static void client_restore_session_state(ObClient *self)
 
     RECT_SET_POINT(self->area, self->session->x, self->session->y);
     self->positioned = USPosition;
+    self->sized = USSize;
     if (self->session->w > 0)
         self->area.width = self->session->w;
     if (self->session->h > 0)
@@ -863,9 +933,11 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     gint ox = *x, oy = *y;
     gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
     gint fw, fh;
+    Rect desired;
 
+    RECT_SET(desired, *x, *y, w, h);
     all_a = screen_area(self->desktop);
-    mon_a = screen_area_monitor(self->desktop, client_monitor(self));
+    mon_a = screen_area_monitor(self->desktop, screen_find_monitor(&desired));
 
     /* get where the frame would be */
     frame_client_gravity(self->frame, x, y, w, h);
@@ -915,10 +987,10 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
         POINT_SET(newbl, newtl.x, newbr.y);
 
         /* is it moving or just resizing from some corner? */
-        stationary_l = oldtl.x == oldtl.x;
-        stationary_r = oldtr.x == oldtr.x;
-        stationary_t = oldtl.y == oldtl.y;
-        stationary_b = oldbl.y == oldbl.y;
+        stationary_l = oldtl.x == newtl.x;
+        stationary_r = oldtr.x == newtr.x;
+        stationary_t = oldtl.y == newtl.y;
+        stationary_b = oldbl.y == newbl.y;
 
         /* if left edge is growing and didnt move right edge */
         if (stationary_r && newtl.x < oldtl.x)
@@ -940,16 +1012,13 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
      * xterm -geometry resolution-width/2 will work fine. Trying to
      * place it completely offscreen will be handled in the above code.
      * Sorry for this confused comment, i am tired. */
-    if (fw <= mon_a->width) {
-        if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x;
-        if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width)
-            *x = mon_a->x + mon_a->width - fw;
-    }
-    if (fh <= mon_a->height) {
-        if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y;
-        if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height)
-            *y = mon_a->y + mon_a->height - fh;
-    }
+    if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x;
+    if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width)
+        *x = mon_a->x + MAX(0, mon_a->width - fw);
+
+    if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y;
+    if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height)
+        *y = mon_a->y + MAX(0, mon_a->height - fh);
 
     /* get where the client should be */
     frame_frame_gravity(self->frame, x, y, w, h);
@@ -957,71 +1026,6 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
     return ox != *x || oy != *y;
 }
 
-static void client_toggle_border(ObClient *self, gboolean show)
-{
-    /* adjust our idea of where the client is, based on its border. When the
-       border is removed, the client should now be considered to be in a
-       different position.
-       when re-adding the border to the client, the same operation needs to be
-       reversed. */
-    gint oldx = self->area.x, oldy = self->area.y;
-    gint x = oldx, y = oldy;
-    switch(self->gravity) {
-    default:
-    case NorthWestGravity:
-    case WestGravity:
-    case SouthWestGravity:
-        break;
-    case NorthEastGravity:
-    case EastGravity:
-    case SouthEastGravity:
-        if (show) x -= self->border_width * 2;
-        else      x += self->border_width * 2;
-        break;
-    case NorthGravity:
-    case SouthGravity:
-    case CenterGravity:
-    case ForgetGravity:
-    case StaticGravity:
-        if (show) x -= self->border_width;
-        else      x += self->border_width;
-        break;
-    }
-    switch(self->gravity) {
-    default:
-    case NorthWestGravity:
-    case NorthGravity:
-    case NorthEastGravity:
-        break;
-    case SouthWestGravity:
-    case SouthGravity:
-    case SouthEastGravity:
-        if (show) y -= self->border_width * 2;
-        else      y += self->border_width * 2;
-        break;
-    case WestGravity:
-    case EastGravity:
-    case CenterGravity:
-    case ForgetGravity:
-    case StaticGravity:
-        if (show) y -= self->border_width;
-        else      y += self->border_width;
-        break;
-    }
-    self->area.x = x;
-    self->area.y = y;
-
-    if (show) {
-        XSetWindowBorderWidth(ob_display, self->window, self->border_width);
-
-        /* set border_width to 0 because there is no border to add into
-           calculations anymore */
-        self->border_width = 0;
-    } else
-        XSetWindowBorderWidth(ob_display, self->window, 0);
-}
-
-
 static void client_get_all(ObClient *self, gboolean real)
 {
     /* this is needed for the frame to set itself up */
@@ -1099,8 +1103,8 @@ static void client_get_area(ObClient *self)
     POINT_SET(self->root_pos, wattrib.x, wattrib.y);
     self->border_width = wattrib.border_width;
 
-    ob_debug("client area: %d %d  %d %d\n", wattrib.x, wattrib.y,
-             wattrib.width, wattrib.height);
+    ob_debug("client area: %d %d  %d %d  bw %d\n", wattrib.x, wattrib.y,
+             wattrib.width, wattrib.height, wattrib.border_width);
 }
 
 static void client_get_desktop(ObClient *self)
@@ -1112,23 +1116,38 @@ static void client_get_desktop(ObClient *self)
             self->desktop = screen_num_desktops - 1;
         else
             self->desktop = d;
+        ob_debug("client requested desktop 0x%x\n", self->desktop); 
     } else {
         gboolean trdesk = FALSE;
 
         if (self->transient_for) {
             if (self->transient_for != OB_TRAN_GROUP) {
-                self->desktop = self->transient_for->desktop;
-                trdesk = TRUE;
+                if (self->transient_for->desktop != DESKTOP_ALL) {
+                    self->desktop = self->transient_for->desktop;
+                    trdesk = TRUE;
+                }
             } else {
+                /* if all the group is on one desktop, then open it on the
+                   same desktop */
                 GSList *it;
+                gboolean first = TRUE;
+                guint all = screen_num_desktops; /* not a valid value */
 
-                for (it = self->group->members; it; it = g_slist_next(it))
-                    if (it->data != self &&
-                        !((ObClient*)it->data)->transient_for) {
-                        self->desktop = ((ObClient*)it->data)->desktop;
-                        trdesk = TRUE;
-                        break;
+                for (it = self->group->members; it; it = g_slist_next(it)) {
+                    ObClient *c = it->data;
+                    if (c != self) {
+                        if (first) {
+                            all = c->desktop;
+                            first = FALSE;
+                        }
+                        else if (all != c->desktop)
+                            all = screen_num_desktops; /* make it invalid */
                     }
+                }
+                if (all != screen_num_desktops) {
+                    self->desktop = all;
+                    trdesk = TRUE;
+                }
             }
         }
         if (!trdesk) {
@@ -1174,7 +1193,7 @@ static void client_get_state(ObClient *self)
                 self->below = TRUE;
             else if (state[i] == prop_atoms.net_wm_state_demands_attention)
                 self->demands_attention = TRUE;
-            else if (state[i] == prop_atoms.openbox_wm_state_undecorated)
+            else if (state[i] == prop_atoms.ob_wm_state_undecorated)
                 self->undecorated = TRUE;
         }
 
@@ -1512,14 +1531,22 @@ void client_get_colormap(ObClient *self)
 
 void client_update_colormap(ObClient *self, Colormap colormap)
 {
-    self->colormap = colormap;
+    if (colormap == self->colormap) return;
+
+    ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap);
+
+    if (client_focused(self)) {
+        screen_install_colormap(self, FALSE); /* uninstall old one */
+        self->colormap = colormap;
+        screen_install_colormap(self, FALSE); /* install new one */
+    } else
+        self->colormap = colormap;
 }
 
 void client_update_normal_hints(ObClient *self)
 {
     XSizeHints size;
     glong ret;
-    gint oldgravity = self->gravity;
 
     /* defaults */
     self->min_ratio = 0.0f;
@@ -1535,20 +1562,10 @@ void client_update_normal_hints(ObClient *self)
         if (!client_normal(self))
         */
         self->positioned = (size.flags & (PPosition|USPosition));
+        self->sized = (size.flags & (PSize|USSize));
 
-        if (size.flags & PWinGravity) {
+        if (size.flags & PWinGravity)
             self->gravity = size.win_gravity;
-      
-            /* if the client has a frame, i.e. has already been mapped and
-               is changing its gravity */
-            if (self->frame && self->gravity != oldgravity) {
-                /* move our idea of the client's position based on its new
-                   gravity */
-                client_convert_gravity(self, oldgravity,
-                                       &self->area.x, &self->area.y,
-                                       self->area.width, self->area.height);
-            }
-        }
 
         if (size.flags & PAspect) {
             if (size.min_aspect.y)
@@ -1573,7 +1590,7 @@ void client_update_normal_hints(ObClient *self)
     }
 }
 
-void client_setup_decor_and_functions(ObClient *self)
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
 {
     /* start with everything (cept fullscreen) */
     self->decorations =
@@ -1593,7 +1610,10 @@ void client_setup_decor_and_functions(ObClient *self)
          OB_CLIENT_FUNC_ICONIFY |
          OB_CLIENT_FUNC_MAXIMIZE |
          OB_CLIENT_FUNC_SHADE |
-         OB_CLIENT_FUNC_CLOSE);
+         OB_CLIENT_FUNC_CLOSE |
+         OB_CLIENT_FUNC_BELOW |
+         OB_CLIENT_FUNC_ABOVE |
+         OB_CLIENT_FUNC_UNDECORATE);
 
     if (!(self->min_size.width < self->max_size.width ||
           self->min_size.height < self->max_size.height))
@@ -1608,14 +1628,16 @@ void client_setup_decor_and_functions(ObClient *self)
 
     case OB_CLIENT_TYPE_DIALOG:
     case OB_CLIENT_TYPE_UTILITY:
-        /* these windows cannot be maximized */
-        self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+        /* these windows don't have anything added or removed by default */
         break;
 
     case OB_CLIENT_TYPE_MENU:
     case OB_CLIENT_TYPE_TOOLBAR:
-        /* these windows get less functionality */
-        self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
+        /* these windows can't iconify or maximize */
+        self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
+                               OB_FRAME_DECOR_MAXIMIZE);
+        self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
+                             OB_CLIENT_FUNC_MAXIMIZE);
         break;
 
     case OB_CLIENT_TYPE_SPLASH:
@@ -1623,13 +1645,20 @@ void client_setup_decor_and_functions(ObClient *self)
            do with them is move them */
         self->decorations = 0;
         self->functions = OB_CLIENT_FUNC_MOVE;
+        break;
 
     case OB_CLIENT_TYPE_DESKTOP:
-    case OB_CLIENT_TYPE_DOCK:
         /* these windows are not manipulated by the window manager */
         self->decorations = 0;
         self->functions = 0;
         break;
+
+    case OB_CLIENT_TYPE_DOCK:
+        /* these windows are not manipulated by the window manager, but they
+           can set below layer which has a special meaning */
+        self->decorations = 0;
+        self->functions = OB_CLIENT_FUNC_BELOW;
+        break;
     }
 
     /* Mwm Hints are applied subtractively to what has already been chosen for
@@ -1683,9 +1712,17 @@ void client_setup_decor_and_functions(ObClient *self)
         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
     }
 
-    /* kill the handle on fully maxed windows */
-    if (self->max_vert && self->max_horz)
+    if (self->max_horz && self->max_vert) {
+        /* you can't resize fully maximized windows */
+        self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+        /* kill the handle on fully maxed windows */
         self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
+    }
+
+    /* If there are no decorations to remove, don't allow the user to try
+       toggle the state */
+    if (self->decorations == 0)
+        self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
 
     /* finally, the user can have requested no decorations, which overrides
        everything (but doesnt give it a border if it doesnt have one) */
@@ -1710,15 +1747,13 @@ void client_setup_decor_and_functions(ObClient *self)
 
     client_change_allowed_actions(self);
 
-    if (self->frame) {
-        /* adjust the client's decorations, etc. */
+    if (reconfig)
         client_reconfigure(self);
-    }
 }
 
 static void client_change_allowed_actions(ObClient *self)
 {
-    gulong actions[9];
+    gulong actions[12];
     gint num = 0;
 
     /* desktop windows are kept on all desktops */
@@ -1741,6 +1776,12 @@ static void client_change_allowed_actions(ObClient *self)
         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
     }
+    if (self->functions & OB_CLIENT_FUNC_ABOVE)
+        actions[num++] = prop_atoms.net_wm_action_above;
+    if (self->functions & OB_CLIENT_FUNC_BELOW)
+        actions[num++] = prop_atoms.net_wm_action_below;
+    if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
+        actions[num++] = prop_atoms.ob_wm_action_undecorate;
 
     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
 
@@ -1767,11 +1808,9 @@ static void client_change_allowed_actions(ObClient *self)
 
 void client_reconfigure(ObClient *self)
 {
-    /* by making this pass FALSE for user, we avoid the emacs event storm where
-       every configurenotify causes an update in its normal hints, i think this
-       is generally what we want anyways... */
     client_configure(self, self->area.x, self->area.y,
-                     self->area.width, self->area.height, FALSE, TRUE);
+                     self->area.width, self->area.height,
+                     self->border_width, FALSE, TRUE);
 }
 
 void client_update_wmhints(ObClient *self)
@@ -1852,7 +1891,8 @@ void client_update_wmhints(ObClient *self)
         }
 
         /* the WM_HINTS can contain an icon */
-        client_update_icons(self);
+        if (hints->flags & IconPixmapHint)
+            client_update_icons(self);
 
         XFree(hints);
     }
@@ -1904,8 +1944,14 @@ void client_update_title(ObClient *self)
               PROP_GETS(self->window, wm_icon_name, utf8, &data)))
             data = g_strdup(self->title);
 
-    PROP_SETS(self->window, net_wm_visible_icon_name, data);
-    self->icon_title = data;
+    if (self->client_machine) {
+        visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+        g_free(data);
+    } else
+        visible = data;
+
+    PROP_SETS(self->window, net_wm_visible_icon_name, visible);
+    self->icon_title = visible;
 }
 
 void client_update_strut(ObClient *self)
@@ -2278,7 +2324,7 @@ static void client_change_state(ObClient *self)
     if (self->demands_attention)
         netstate[num++] = prop_atoms.net_wm_state_demands_attention;
     if (self->undecorated)
-        netstate[num++] = prop_atoms.openbox_wm_state_undecorated;
+        netstate[num++] = prop_atoms.ob_wm_state_undecorated;
     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
 
     if (self->frame)
@@ -2304,27 +2350,44 @@ ObClient *client_search_focus_tree_full(ObClient *self)
             return client_search_focus_tree_full(self->transient_for);
         } else {
             GSList *it;
-            gboolean recursed = FALSE;
         
-            for (it = self->group->members; it; it = g_slist_next(it))
-                if (!((ObClient*)it->data)->transient_for) {
-                    ObClient *c;
-                    if ((c = client_search_focus_tree_full(it->data)))
-                        return c;
-                    recursed = TRUE;
+            for (it = self->group->members; it; it = g_slist_next(it)) {
+                if (it->data != self) {
+                    ObClient *c = it->data;
+
+                    if (client_focused(c)) return c;
+                    if ((c = client_search_focus_tree(it->data))) return c;
                 }
-            if (recursed)
-                return NULL;
+            }
         }
     }
 
-    /* this function checks the whole tree, the client_search_focus_tree~
+    /* this function checks the whole tree, the client_search_focus_tree
        does not, so we need to check this window */
     if (client_focused(self))
         return self;
     return client_search_focus_tree(self);
 }
 
+gboolean client_has_parent(ObClient *self)
+{
+    if (self->transient_for) {
+        if (self->transient_for != OB_TRAN_GROUP) {
+            if (client_normal(self->transient_for))
+                return TRUE;
+        }
+        else if (self->group) {
+            GSList *it;
+
+            for (it = self->group->members; it; it = g_slist_next(it)) {
+                if (it->data != self && client_normal(it->data))
+                    return TRUE;
+            }
+        }
+    }
+    return FALSE;
+}
+
 static ObStackingLayer calc_layer(ObClient *self)
 {
     ObStackingLayer l;
@@ -2336,13 +2399,14 @@ static ObStackingLayer calc_layer(ObClient *self)
         else l = OB_STACKING_LAYER_ABOVE;
     }
     else if ((self->fullscreen ||
-              /* no decorations and fills the monitor = oldskool fullscreen */
-              (self->frame != NULL &&
-               (self->frame->size.right == 0 && self->frame->size.left == 0 &&
-                self->frame->size.bottom == 0 && self->frame->size.top == 0 &&
-                RECT_EQUAL(self->area,
-                           *screen_physical_area_monitor
-                           (client_monitor(self)))))) &&
+              /* No decorations and fills the monitor = oldskool fullscreen.
+                 But not for maximized windows.
+              */
+              (self->decorations == 0 &&
+               !(self->max_horz && self->max_vert) &&
+               RECT_EQUAL(self->area,
+                          *screen_physical_area_monitor
+                          (client_monitor(self))))) &&
              (client_focused(self) || client_search_focus_tree(self)))
         l = OB_STACKING_LAYER_FULLSCREEN;
     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
@@ -2398,52 +2462,55 @@ gboolean client_should_show(ObClient *self)
     return FALSE;
 }
 
-void client_show(ObClient *self)
+gboolean client_show(ObClient *self)
 {
+    gboolean show = FALSE;
 
     if (client_should_show(self)) {
         frame_show(self->frame);
-    }
+        show = TRUE;
 
-    /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
-       needs to be in IconicState. This includes when it is on another
-       desktop!
-    */
-    client_change_wm_state(self);
+        /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+           it needs to be in IconicState. This includes when it is on another
+           desktop!
+        */
+        client_change_wm_state(self);
+    }
+    return show;
 }
 
-void client_hide(ObClient *self)
+gboolean client_hide(ObClient *self)
 {
+    gboolean hide = FALSE;
+
     if (!client_should_show(self)) {
+        if (self == focus_client) {
+            /* if there is a grab going on, then we need to cancel it. if we
+               move focus during the grab, applications will get
+               NotifyWhileGrabbed events and ignore them !
+
+               actions should not rely on being able to move focus during an
+               interactive grab.
+            */
+            event_cancel_all_key_grabs();
+        }
+
         frame_hide(self->frame);
+        hide = TRUE;
 
-        client_call_notifies(self, client_hide_notifies);
+        /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
+           it needs to be in IconicState. This includes when it is on another
+           desktop!
+        */
+        client_change_wm_state(self);
     }
-
-    /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
-       needs to be in IconicState. This includes when it is on another
-       desktop!
-    */
-    client_change_wm_state(self);
+    return hide;
 }
 
 void client_showhide(ObClient *self)
 {
-
-    if (client_should_show(self)) {
-        frame_show(self->frame);
-    }
-    else {
-        frame_hide(self->frame);
-
-        client_call_notifies(self, client_hide_notifies);
-    }
-
-    /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
-       needs to be in IconicState. This includes when it is on another
-       desktop!
-    */
-    client_change_wm_state(self);
+    if (!client_show(self))
+        client_hide(self);
 }
 
 gboolean client_normal(ObClient *self) {
@@ -2475,69 +2542,59 @@ gboolean client_enter_focusable(ObClient *self)
 }
 
 
-static void client_apply_startup_state(ObClient *self, gint x, gint y)
+static void client_apply_startup_state(ObClient *self,
+                                       gint x, gint y, gint w, gint h)
 {
-    gboolean pos = FALSE; /* has the window's position been configured? */
-    gint ox, oy;
+    /* save the states that we are going to apply */
+    gboolean iconic = self->iconic;
+    gboolean fullscreen = self->fullscreen;
+    gboolean undecorated = self->undecorated;
+    gboolean shaded = self->shaded;
+    gboolean demands_attention = self->demands_attention;
+    gboolean max_horz = self->max_horz;
+    gboolean max_vert = self->max_vert;
 
-    /* save the position, and set self->area for these to use */
-    ox = self->area.x;
-    oy = self->area.y;
-    self->area.x = x;
-    self->area.y = y;
+    /* turn them all off in the client, so they won't affect the window
+       being placed */
+    self->iconic = self->fullscreen = self->undecorated = self->shaded =
+        self->demands_attention = self->max_horz = self->max_vert = FALSE;
 
-    /* set the desktop hint, to make sure that it always exists */
-    PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+    /* move the client to its placed position, or it it's already there,
+       generate a ConfigureNotify telling the client where it is.
 
-    /* these are in a carefully crafted order.. */
+       do this after adjusting the frame. otherwise it gets all weird and
+       clients don't work right
 
-    if (self->iconic) {
-        self->iconic = FALSE;
+       do this before applying the states so they have the correct
+       pre-max/pre-fullscreen values
+    */
+    client_configure(self, x, y, w, h, self->border_width, FALSE, TRUE);
+    ob_debug("placed window 0x%x at %d, %d with size %d x %d\n",
+             self->window, self->area.x, self->area.y,
+             self->area.width, self->area.height);
+
+    /* apply the states. these are in a carefully crafted order.. */
+
+    if (iconic)
         client_iconify(self, TRUE, FALSE, TRUE);
-    }
-    if (self->fullscreen) {
-        self->fullscreen = FALSE;
+    if (fullscreen)
         client_fullscreen(self, TRUE);
-        pos = TRUE;
-    }
-    if (self->undecorated) {
-        self->undecorated = FALSE;
+    if (undecorated)
         client_set_undecorated(self, TRUE);
-    }
-    if (self->shaded) {
-        self->shaded = FALSE;
+    if (shaded)
         client_shade(self, TRUE);
-    }
-    if (self->demands_attention) {
-        self->demands_attention = FALSE;
+    if (demands_attention)
         client_hilite(self, TRUE);
-    }
   
-    if (self->max_vert && self->max_horz) {
-        self->max_vert = self->max_horz = FALSE;
+    if (max_vert && max_horz)
         client_maximize(self, TRUE, 0);
-        pos = TRUE;
-    } else if (self->max_vert) {
-        self->max_vert = FALSE;
+    else if (max_vert)
         client_maximize(self, TRUE, 2);
-        pos = TRUE;
-    } else if (self->max_horz) {
-        self->max_horz = FALSE;
+    else if (max_horz)
         client_maximize(self, TRUE, 1);
-        pos = TRUE;
-    }
 
-    /* if the client didn't get positioned yet, then do so now.
-       call client_move even if the window is not being moved anywhere, because
-       when we reparent it and decorate it, it is getting moved and we need to
-       be telling it so with a ConfigureNotify event.
-    */
-    if (!pos) {
-        /* use the saved position */
-        self->area.x = ox;
-        self->area.y = oy;
-        client_move(self, x, y);
-    }
+    /* set the desktop hint, to make sure that it always exists */
+    PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
 
     /* nothing to do for the other states:
        skip_taskbar
@@ -2548,18 +2605,62 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y)
     */
 }
 
-void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y,
-                            gint w, gint h)
+void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
 {
-    gint oldg = self->gravity;
+    /* these should be the current values. this is for when you're not moving,
+       just resizing */
+    g_assert(*x == self->area.x);
+    g_assert(oldw == self->area.width);
 
-    /* get the frame's position from the requested stuff */
-    self->gravity = gravity;
-    frame_client_gravity(self->frame, x, y, w, h);
-    self->gravity = oldg;
+    /* horizontal */
+    switch (self->gravity) {
+    default:
+    case NorthWestGravity:
+    case WestGravity:
+    case SouthWestGravity:
+    case StaticGravity:
+    case ForgetGravity:
+        break;
+    case NorthGravity:
+    case CenterGravity:
+    case SouthGravity:
+        *x -= (neww - oldw) / 2;
+        break;
+    case NorthEastGravity:
+    case EastGravity:
+    case SouthEastGravity:
+        *x -= neww - oldw;
+        break;
+    }
+}
 
-    /* get the client's position in its true gravity from that */
-    frame_frame_gravity(self->frame, x, y, w, h);
+void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
+{
+    /* these should be the current values. this is for when you're not moving,
+       just resizing */
+    g_assert(*y == self->area.y);
+    g_assert(oldh == self->area.height);
+
+    /* vertical */
+    switch (self->gravity) {
+    default:
+    case NorthWestGravity:
+    case NorthGravity:
+    case NorthEastGravity:
+    case StaticGravity:
+    case ForgetGravity:
+        break;
+    case WestGravity:
+    case CenterGravity:
+    case EastGravity:
+        *y -= (newh - oldh) / 2;
+        break;
+    case SouthWestGravity:
+    case SouthGravity:
+    case SouthEastGravity:
+        *y -= newh - oldh;
+        break;
+    }
 }
 
 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
@@ -2571,7 +2672,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
     /* make the frame recalculate its dimentions n shit without changing
        anything visible for real, this way the constraints below can work with
        the updated frame dimensions. */
-    frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
+    frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
 
     /* work within the prefered sizes given by the window */
     if (!(*w == self->area.width && *h == self->area.height)) {
@@ -2675,7 +2776,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
         *h = a->height;
 
         user = FALSE; /* ignore if the client can't be moved/resized when it
-                         is entering fullscreen */
+                         is fullscreening */
     } else if (self->max_horz || self->max_vert) {
         Rect *a;
         guint i;
@@ -2693,8 +2794,8 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
             *h = a->height - self->frame->size.top - self->frame->size.bottom;
         }
 
-        /* maximizing is not allowed if the user can't move+resize the window
-         */
+        user = FALSE; /* ignore if the client can't be moved/resized when it
+                         is maximizing */
     }
 
     /* gets the client's position */
@@ -2717,14 +2818,16 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
 }
 
 
-void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
-                           gboolean user, gboolean final)
+void client_configure(ObClient *self, gint x, gint y, gint w, gint h, gint b,
+                      gboolean user, gboolean final)
 {
     gint oldw, oldh;
     gboolean send_resize_client;
     gboolean moved = FALSE, resized = FALSE;
+    gboolean fmoved, fresized;
     guint fdecor = self->frame->decorations;
     gboolean fhorz = self->frame->max_horz;
+    gboolean fvert = self->frame->max_vert;
     gint logicalw, logicalh;
 
     /* find the new x, y, width, and height (and logical size) */
@@ -2736,11 +2839,13 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
 
     /* figure out if we moved or resized or what */
     moved = x != self->area.x || y != self->area.y;
-    resized = w != self->area.width || h != self->area.height;
+    resized = w != self->area.width || h != self->area.height ||
+        b != self->border_width;
 
     oldw = self->area.width;
     oldh = self->area.height;
     RECT_SET(self->area, x, y, w, h);
+    self->border_width = b;
 
     /* for app-requested resizes, always resize if 'resized' is true.
        for user-requested ones, only resize if final is true, or when
@@ -2750,22 +2855,41 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
                                     (resized && config_resize_redraw))));
 
     /* if the client is enlarging, then resize the client before the frame */
-    if (send_resize_client && user && (w > oldw || h > oldh)) {
-        XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
-        /* resize the plate to show the client padding color underneath */
+    if (send_resize_client && (w > oldw || h > oldh)) {
+        XWindowChanges changes;
+        changes.x = self->frame->size.left - self->border_width;
+        changes.y = self->frame->size.top -self->border_width;
+        changes.width = MAX(w, oldw);
+        changes.height = MAX(h, oldh);
+        changes.border_width = self->border_width;
+        XConfigureWindow(ob_display, self->window,
+                         CWX|CWY|CWWidth|CWHeight|CWBorderWidth,
+                         &changes);
+
         frame_adjust_client_area(self->frame);
     }
 
     /* find the frame's dimensions and move/resize it */
-    if (self->decorations != fdecor || self->max_horz != fhorz)
-        moved = resized = TRUE;
-    if (moved || resized)
-        frame_adjust_area(self->frame, moved, resized, FALSE);
+    fmoved = moved;
+    fresized = resized;
+
+    /* if decorations changed, then readjust everything for the frame */
+    if (self->decorations != fdecor ||
+        self->max_horz != fhorz || self->max_vert != fvert)
+    {
+        fmoved = fresized = TRUE;
+    }
+
+    /* adjust the frame */
+    if (fmoved || fresized)
+        frame_adjust_area(self->frame, fmoved, fresized, FALSE);
 
     if ((!user || (user && final)) && !resized)
     {
         XEvent event;
 
+        /* we have reset the client to 0 border width, so don't include
+           it in these coords */
         POINT_SET(self->root_pos,
                   self->frame->area.x + self->frame->size.left -
                   self->border_width,
@@ -2785,19 +2909,27 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
         event.xconfigure.y = self->root_pos.y;
         event.xconfigure.width = w;
         event.xconfigure.height = h;
-        event.xconfigure.border_width = 0;
-        event.xconfigure.above = self->frame->plate;
+        event.xconfigure.border_width = self->border_width;
+        event.xconfigure.above = None;
         event.xconfigure.override_redirect = FALSE;
         XSendEvent(event.xconfigure.display, event.xconfigure.window,
                    FALSE, StructureNotifyMask, &event);
     }
 
     /* if the client is shrinking, then resize the frame before the client */
-    if (send_resize_client && (!user || (w <= oldw || h <= oldh))) {
-        /* resize the plate to show the client padding color underneath */
+    if (send_resize_client && (w <= oldw && h <= oldh)) {
+        XWindowChanges changes;
+
         frame_adjust_client_area(self->frame);
 
-        XResizeWindow(ob_display, self->window, w, h);
+        changes.x = self->frame->size.left - self->border_width;
+        changes.y = self->frame->size.top -self->border_width;
+        changes.width = w;
+        changes.height = h;
+        changes.border_width = self->border_width;
+        XConfigureWindow(ob_display, self->window,
+                         CWX|CWY|CWWidth|CWHeight|CWBorderWidth,
+                         &changes);
     }
 
     XFlush(ob_display);
@@ -2812,7 +2944,6 @@ void client_fullscreen(ObClient *self, gboolean fs)
 
     self->fullscreen = fs;
     client_change_state(self); /* change the state hints on the client */
-    client_calc_layer(self);   /* and adjust out layer/stacking */
 
     if (fs) {
         self->pre_fullscreen_area = self->area;
@@ -2827,36 +2958,35 @@ void client_fullscreen(ObClient *self, gboolean fs)
             self->pre_fullscreen_area.height = self->pre_max_area.height;
         }
 
-        /* these are not actually used cuz client_configure will set them
-           as appropriate when the window is fullscreened */
-        x = y = w = h = 0;
+        /* these will help configure_full figure out where to fullscreen
+           the window */
+        x = self->area.x;
+        y = self->area.y;
+        w = self->area.width;
+        h = self->area.height;
     } else {
-        Rect *a;
+        g_assert(self->pre_fullscreen_area.width > 0 &&
+                 self->pre_fullscreen_area.height > 0);
 
-        if (self->pre_fullscreen_area.width > 0 &&
-            self->pre_fullscreen_area.height > 0)
-        {
-            x = self->pre_fullscreen_area.x;
-            y = self->pre_fullscreen_area.y;
-            w = self->pre_fullscreen_area.width;
-            h = self->pre_fullscreen_area.height;
-            RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
-        } else {
-            /* pick some fallbacks... */
-            a = screen_area_monitor(self->desktop, 0);
-            x = a->x + a->width / 4;
-            y = a->y + a->height / 4;
-            w = a->width / 2;
-            h = a->height / 2;
-        }
+        x = self->pre_fullscreen_area.x;
+        y = self->pre_fullscreen_area.y;
+        w = self->pre_fullscreen_area.width;
+        h = self->pre_fullscreen_area.height;
+        RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
     }
 
-    client_setup_decor_and_functions(self);
-
+    client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
 
-    /* try focus us when we go into fullscreen mode */
-    client_focus(self);
+    /* and adjust our layer/stacking. do this after resizing the window,
+       and applying decorations, because windows which fill the screen are
+       considered "fullscreen" and it affects their layer */
+    client_calc_layer(self);
+
+    if (fs) {
+        /* try focus us when we go into fullscreen mode */
+        client_focus(self);
+    }
 }
 
 static void client_iconify_recursive(ObClient *self,
@@ -2878,9 +3008,8 @@ static void client_iconify_recursive(ObClient *self,
                 self->iconic = iconic;
 
                 /* update the focus lists.. iconic windows go to the bottom of
-                   the list, put the new iconic window at the 'top of the
-                   bottom'. */
-                focus_order_to_top(self);
+                   the list */
+                focus_order_to_bottom(self);
 
                 changed = TRUE;
             }
@@ -2944,8 +3073,8 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
         if (dir == 2 && !self->max_vert) return;
     }
 
-    /* we just tell it to configure in the same place and client_configure
-       worries about filling the screen with the window */
+    /* these will help configure_full figure out which screen to fill with
+       the window */
     x = self->area.x;
     y = self->area.y;
     w = self->area.width;
@@ -2963,34 +3092,23 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
                      self->pre_max_area.width, self->area.height);
         }
     } else {
-        Rect *a;
-
-        a = screen_area_monitor(self->desktop, 0);
         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
-            if (self->pre_max_area.width > 0) {
-                x = self->pre_max_area.x;
-                w = self->pre_max_area.width;
+            g_assert(self->pre_max_area.width > 0);
 
-                RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
-                         0, self->pre_max_area.height);
-            } else {
-                /* pick some fallbacks... */
-                x = a->x + a->width / 4;
-                w = a->width / 2;
-            }
+            x = self->pre_max_area.x;
+            w = self->pre_max_area.width;
+
+            RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
+                     0, self->pre_max_area.height);
         }
         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
-            if (self->pre_max_area.height > 0) {
-                y = self->pre_max_area.y;
-                h = self->pre_max_area.height;
+            g_assert(self->pre_max_area.height > 0);
 
-                RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
-                         self->pre_max_area.width, 0);
-            } else {
-                /* pick some fallbacks... */
-                y = a->y + a->height / 4;
-                h = a->height / 2;
-            }
+            y = self->pre_max_area.y;
+            h = self->pre_max_area.height;
+
+            RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
+                     self->pre_max_area.width, 0);
         }
     }
 
@@ -3001,8 +3119,7 @@ void client_maximize(ObClient *self, gboolean max, gint dir)
 
     client_change_state(self); /* change the state hints on the client */
 
-    client_setup_decor_and_functions(self);
-
+    client_setup_decor_and_functions(self, FALSE);
     client_move_resize(self, x, y, w, h);
 }
 
@@ -3079,7 +3196,7 @@ void client_set_desktop_recursive(ObClient *self,
     guint old;
     GSList *it;
 
-    if (target != self->desktop) {
+    if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
 
         ob_debug("Setting desktop %u\n", target+1);
 
@@ -3229,7 +3346,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 action = self->demands_attention ?
                     prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
-            else if (state == prop_atoms.openbox_wm_state_undecorated)
+            else if (state == prop_atoms.ob_wm_state_undecorated)
                 action = undecorated ? prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
         }
@@ -3259,7 +3376,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 below = TRUE;
             } else if (state == prop_atoms.net_wm_state_demands_attention) {
                 demands_attention = TRUE;
-            } else if (state == prop_atoms.openbox_wm_state_undecorated) {
+            } else if (state == prop_atoms.ob_wm_state_undecorated) {
                 undecorated = TRUE;
             }
 
@@ -3286,11 +3403,12 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 below = FALSE;
             } else if (state == prop_atoms.net_wm_state_demands_attention) {
                 demands_attention = FALSE;
-            } else if (state == prop_atoms.openbox_wm_state_undecorated) {
+            } else if (state == prop_atoms.ob_wm_state_undecorated) {
                 undecorated = FALSE;
             }
         }
     }
+
     if (max_horz != self->max_horz || max_vert != self->max_vert) {
         if (max_horz != self->max_horz && max_vert != self->max_vert) {
             /* toggling both */
@@ -3316,24 +3434,30 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
         client_shade(self, shaded);
     if (undecorated != self->undecorated)
         client_set_undecorated(self, undecorated);
+    if (above != self->above || below != self->below) {
+        self->above = above;
+        self->below = below;
+        client_calc_layer(self);
+    }
+
     if (modal != self->modal) {
         self->modal = modal;
         /* when a window changes modality, then its stacking order with its
            transients needs to change */
         stacking_raise(CLIENT_AS_WINDOW(self));
+
+        /* it also may get focused. if something is focused that shouldn't
+           be focused anymore, then move the focus */
+        if (focus_client && client_focus_target(focus_client) != focus_client)
+            client_focus(focus_client);
     }
+
     if (iconic != self->iconic)
         client_iconify(self, iconic, FALSE, FALSE);
 
     if (demands_attention != self->demands_attention)
         client_hilite(self, demands_attention);
 
-    if (above != self->above || below != self->below) {
-        self->above = above;
-        self->below = below;
-        client_calc_layer(self);
-    }
-
     client_change_state(self); /* change the hint to reflect these changes */
 }
 
@@ -3348,8 +3472,6 @@ ObClient *client_focus_target(ObClient *self)
 
 gboolean client_can_focus(ObClient *self)
 {
-    XEvent ev;
-
     /* choose the correct target */
     self = client_focus_target(self);
 
@@ -3359,29 +3481,15 @@ gboolean client_can_focus(ObClient *self)
     if (!(self->can_focus || self->focus_notify))
         return FALSE;
 
-    /* do a check to see if the window has already been unmapped or destroyed
-       do this intelligently while watching out for unmaps we've generated
-       (ignore_unmaps > 0) */
-    if (XCheckTypedWindowEvent(ob_display, self->window,
-                               DestroyNotify, &ev)) {
-        XPutBackEvent(ob_display, &ev);
-        return FALSE;
-    }
-    while (XCheckTypedWindowEvent(ob_display, self->window,
-                                  UnmapNotify, &ev)) {
-        if (self->ignore_unmaps) {
-            self->ignore_unmaps--;
-        } else {
-            XPutBackEvent(ob_display, &ev);
-            return FALSE;
-        }
-    }
-
     return TRUE;
 }
 
 gboolean client_focus(ObClient *self)
 {
+    /* we might not focus this window, so if we have modal children which would
+       be focused instead, bring them to this desktop */
+    client_bring_modal_windows(self);
+
     /* choose the correct target */
     self = client_focus_target(self);
 
@@ -3390,20 +3498,32 @@ gboolean client_focus(ObClient *self)
             /* update the focus lists */
             focus_order_to_top(self);
         }
+        ob_debug_type(OB_DEBUG_FOCUS,
+                      "Client %s can't be focused\n", self->title);
         return FALSE;
     }
 
     ob_debug_type(OB_DEBUG_FOCUS,
-                  "Focusing client \"%s\" at time %u\n",
-                  self->title, event_curtime);
+                  "Focusing client \"%s\" (0x%x) at time %u\n",
+                  self->title, self->window, event_curtime);
+
+    /* if there is a grab going on, then we need to cancel it. if we move
+       focus during the grab, applications will get NotifyWhileGrabbed events
+       and ignore them !
+
+       actions should not rely on being able to move focus during an
+       interactive grab.
+    */
+    event_cancel_all_key_grabs();
+
+    xerror_set_ignore(TRUE);
+    xerror_occured = FALSE;
 
     if (self->can_focus) {
         /* This can cause a BadMatch error with CurrentTime, or if an app
            passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
-        xerror_set_ignore(TRUE);
         XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
                        event_curtime);
-        xerror_set_ignore(FALSE);
     }
 
     if (self->focus_notify) {
@@ -3421,17 +3541,10 @@ gboolean client_focus(ObClient *self)
         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
     }
 
-#ifdef DEBUG_FOCUS
-    ob_debug("%sively focusing %lx at %d\n",
-             (self->can_focus ? "act" : "pass"),
-             self->window, (gint) event_curtime);
-#endif
+    xerror_set_ignore(FALSE);
 
-    /* Cause the FocusIn to come back to us. Important for desktop switches,
-       since otherwise we'll have no FocusIn on the queue and send it off to
-       the focus_backup. */
-    XSync(ob_display, FALSE);
-    return TRUE;
+    ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", xerror_occured);
+    return !xerror_occured;
 }
 
 /*! Present the client to the user.
@@ -3491,25 +3604,25 @@ void client_activate(ObClient *self, gboolean here, gboolean user)
                   self->window, event_curtime, last_time,
                   (user ? "user" : "application"), allow);
 
-    if (allow) {
-        if (event_curtime != CurrentTime)
-            self->user_time = event_curtime;
-
+    if (allow)
         client_present(self, here, TRUE);
-    else
+    else
         /* don't focus it but tell the user it wants attention */
         client_hilite(self, TRUE);
 }
 
-static void client_bring_helper_windows_recursive(ObClient *self,
-                                                  guint desktop)
+static void client_bring_windows_recursive(ObClient *self,
+                                           guint desktop,
+                                           gboolean helpers,
+                                           gboolean modals)
 {
     GSList *it;
 
     for (it = self->transients; it; it = g_slist_next(it))
-        client_bring_helper_windows_recursive(it->data, desktop);
+        client_bring_windows_recursive(it->data, desktop, helpers, modals);
 
-    if (client_helper(self) &&
+    if (((helpers && client_helper(self)) ||
+         (modals && self->modal))&&
         self->desktop != desktop && self->desktop != DESKTOP_ALL)
     {
         client_set_desktop(self, desktop, FALSE);
@@ -3518,7 +3631,12 @@ static void client_bring_helper_windows_recursive(ObClient *self,
 
 void client_bring_helper_windows(ObClient *self)
 {
-    client_bring_helper_windows_recursive(self, self->desktop);
+    client_bring_windows_recursive(self, self->desktop, TRUE, FALSE);
+}
+
+void client_bring_modal_windows(ObClient *self)
+{
+    client_bring_windows_recursive(self, self->desktop, FALSE, TRUE);
 }
 
 gboolean client_focused(ObClient *self)
@@ -3562,7 +3680,7 @@ static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
     for (i = 1; i < self->nicons; ++i) {
         gulong diff;
 
-        diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
+        diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
         if (diff < min_diff) {
             min_diff = diff;
             min_i = i;
@@ -3601,16 +3719,13 @@ void client_set_layer(ObClient *self, gint layer)
 
 void client_set_undecorated(ObClient *self, gboolean undecorated)
 {
-    if (self->undecorated != undecorated) {
+    if (self->undecorated != undecorated &&
+        /* don't let it undecorate if the function is missing, but let 
+           it redecorate */
+        (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
+    {
         self->undecorated = undecorated;
-        client_setup_decor_and_functions(self);
-        /* Make sure the client knows it might have moved. Maybe there is a
-         * better way of doing this so only one client_configure is sent, but
-         * since 125 of these are sent per second when moving the window (with
-         * user = FALSE) i doubt it matters much.
-         */
-        client_configure(self, self->area.x, self->area.y,
-                         self->area.width, self->area.height, TRUE, TRUE);
+        client_setup_decor_and_functions(self, TRUE);
         client_change_state(self); /* reflect this in the state hints */
     }
 }
@@ -3741,8 +3856,6 @@ ObClient *client_search_transient(ObClient *self, ObClient *search)
             if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
                 continue;                                                     \
             if(cur->iconic)                                                   \
-                continue;                                                     \
-            if(cur->layer == c->layer)                                        \
                 continue;
 
 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
This page took 0.060629 seconds and 4 git commands to generate.