]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
make keeping windows on screen much more clever
[chaz/openbox] / openbox / client.c
index 4b8e621b486acea3cf82a6389ac6b659371916d3..5e1a78513992929a178fb871bfbffa0b89a1b3b2 100644 (file)
@@ -483,11 +483,8 @@ void client_unmanage(ObClient *self)
     /* flush to send the hide to the server quickly */
     XFlush(ob_display);
 
-    if (focus_client == self) {
-        /* ignore enter events from the unmap so it doesnt mess with the focus
-         */
-        event_ignore_queued_enters();
-    }
+    /* ignore enter events from the unmap so it doesnt mess with the focus */
+    event_ignore_queued_enters();
 
     mouse_grab_for_client(self, FALSE);
 
@@ -496,9 +493,24 @@ void client_unmanage(ObClient *self)
 
     /* update the focus lists */
     focus_order_remove(self);
-    /* don't leave an invalid focus_client */
-    if (self == focus_client)
-        focus_client = NULL;
+    if (client_focused(self)) {
+        /* we have to fall back here because we might not get a focus out.
+           1. we need to xselectinput off the window before we unmap it because
+           otherwise we end up getting unmapnotifies we don't want and they
+           can mess up mapping it again quickly
+           2. this means that if we unmanage from a synthetic unmapnotify, we
+              are the ones unmapped it, and causing the focusout. so we won't
+              get the focusout event.
+           3. we can't handle focusin events on the root window because they
+              come from all screens, so the focus change gets lost
+
+           if this ever gets removed in the future MAKE SURE to replace it
+           with:
+           /- don't leave an invalid focus_client -/
+           focus_client = NULL;
+        */
+        focus_fallback(FALSE);
+    }
 
     client_list = g_list_remove(client_list, self);
     stacking_remove(self);
@@ -739,61 +751,98 @@ void client_move_onscreen(ObClient *self, gboolean rude)
 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
                               gboolean rude)
 {
-    Rect *a;
+    Rect *mon_a, *all_a;
     gint ox = *x, oy = *y;
+    gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
+    gint fw, fh;
+
+    all_a = screen_area(self->desktop);
+    mon_a = screen_area_monitor(self->desktop, client_monitor(self));
+
+    /* get where the frame would be */
+    frame_client_gravity(self->frame, x, y, w, h);
 
-    frame_client_gravity(self->frame, x, y); /* get where the frame
-                                                would be */
+    /* get the requested size of the window with decorations */
+    fw = self->frame->size.left + w + self->frame->size.right;
+    fh = self->frame->size.top + h + self->frame->size.bottom;
 
-    /* XXX watch for xinerama dead areas */
     /* This makes sure windows aren't entirely outside of the screen so you
        can't see them at all.
        It makes sure 10% of the window is on the screen at least. At don't let
        it move itself off the top of the screen, which would hide the titlebar
        on you. (The user can still do this if they want too, it's only limiting
        the application.
+
+       XXX watch for xinerama dead areas...
     */
     if (client_normal(self)) {
-        a = screen_area(self->desktop);
-        if (!self->strut.right &&
-            *x + self->frame->area.width/10 >= a->x + a->width - 1)
-            *x = a->x + a->width - self->frame->area.width/10;
-        if (!self->strut.bottom &&
-            *y + self->frame->area.height/10 >= a->y + a->height - 1)
-            *y = a->y + a->height - self->frame->area.height/10;
-        if (!self->strut.left && *x + self->frame->area.width*9/10 - 1 < a->x)
-            *x = a->x - self->frame->area.width*9/10;
-        if (!self->strut.top && *y + self->frame->area.height*9/10 - 1 < a->y)
-            *y = a->y - self->frame->area.width*9/10;
-    }
-
-    /* This here doesn't let windows even a pixel outside the screen,
-     * when called from client_manage, programs placing themselves are
+        if (!self->strut.right && *x + fw/10 >= all_a->x + all_a->width - 1)
+            *x = all_a->x + all_a->width - fw/10;
+        if (!self->strut.bottom && *y + fh/10 >= all_a->y + all_a->height - 1)
+            *y = all_a->y + all_a->height - fh/10;
+        if (!self->strut.left && *x + fw*9/10 - 1 < all_a->x)
+            *x = all_a->x - fw*9/10;
+        if (!self->strut.top && *y + fh*9/10 - 1 < all_a->y)
+            *y = all_a->y - fw*9/10;
+    }
+
+    /* If rudeness wasn't requested, then figure out of the client is currently
+       entirely on the screen. If it is, and the position isn't changing by
+       request, and it is enlarging, then be rude even though it wasn't
+       requested */
+    if (!rude) {
+        Point oldtl, oldtr, oldbl, oldbr;
+        Point newtl, newtr, newbl, newbr;
+        gboolean stationary;
+
+        POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
+        POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
+                  self->frame->area.y + self->frame->area.height - 1);
+        POINT_SET(oldtr, oldbr.x, oldtl.y);
+        POINT_SET(oldbl, oldtl.x, oldbr.y);
+
+        POINT_SET(newtl, *x, *y);
+        POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
+        POINT_SET(newtr, newbr.x, newtl.y);
+        POINT_SET(newbl, newtl.x, newbr.y);
+
+        /* is it moving or just resizing from some corner? */
+        stationary = (POINT_EQUAL(oldtl, newtl) || POINT_EQUAL(oldtr, newtr) ||
+                      POINT_EQUAL(oldbl, newbl) || POINT_EQUAL(oldbr, newbr));
+
+        /* if left edge is growing */
+        if (stationary && newtl.x < oldtl.x)
+            rudel = TRUE;
+        /* if right edge is growing */
+        if (stationary && newtr.x > oldtr.x)
+            ruder = TRUE;
+        /* if top edge is growing */
+        if (stationary && newtl.y < oldtl.y)
+            rudet = TRUE;
+        /* if bottom edge is growing */
+        if (stationary && newbl.y > oldbl.y)
+            rudeb = TRUE;
+    }
+
+    /* This here doesn't let windows even a pixel outside the struts/screen.
+     * When called from client_manage, programs placing themselves are
      * forced completely onscreen, while things like
      * 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 (rude) {
-        /* avoid the xinerama monitor divide while we're at it,
-         * remember to fix the placement stuff to avoid it also and
-         * then remove this XXX */
-        a = screen_area_monitor(self->desktop, client_monitor(self));
-        /* dont let windows map into the strut unless they
-           are bigger than the available area */
-        if (w <= a->width) {
-            if (!self->strut.left && *x < a->x) *x = a->x;
-            if (!self->strut.right && *x + w > a->x + a->width)
-                *x = a->x + a->width - w;
-        }
-        if (h <= a->height) {
-            if (!self->strut.top && *y < a->y) *y = a->y;
-            if (!self->strut.bottom && *y + h > a->y + a->height)
-                *y = a->y + a->height - h;
-        }
+    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;
     }
 
-    frame_frame_gravity(self->frame, x, y); /* get where the client
-                                               should be */
+    /* get where the client should be */
+    frame_frame_gravity(self->frame, x, y, w, h);
 
     return ox != *x || oy != *y;
 }
@@ -1137,7 +1186,8 @@ void client_update_transient_for(ObClient *self)
             /* remove from old parents */
             for (it = self->group->members; it; it = g_slist_next(it)) {
                 ObClient *c = it->data;
-                if (c != self && !c->transient_for)
+                if (c != self && (!c->transient_for ||
+                                  c->transient_for != OB_TRAN_GROUP))
                     c->transients = g_slist_remove(c->transients, self);
             }
         } else if (self->transient_for != NULL) { /* transient of window */
@@ -1152,7 +1202,8 @@ void client_update_transient_for(ObClient *self)
             /* add to new parents */
             for (it = self->group->members; it; it = g_slist_next(it)) {
                 ObClient *c = it->data;
-                if (c != self && !c->transient_for)
+                if (c != self && (!c->transient_for ||
+                                  c->transient_for != OB_TRAN_GROUP))
                     c->transients = g_slist_append(c->transients, self);
             }
 
@@ -1337,9 +1388,9 @@ void client_update_normal_hints(ObClient *self)
             if (self->frame && self->gravity != oldgravity) {
                 /* move our idea of the client's position based on its new
                    gravity */
-                self->area.x = self->frame->area.x;
-                self->area.y = self->frame->area.y;
-                frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
+                client_convert_gravity(self, oldgravity,
+                                       &self->area.x, &self->area.y,
+                                       self->area.width, self->area.height);
             }
         }
 
@@ -1558,7 +1609,7 @@ 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, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
+    client_configure(self, self->area.x, self->area.y,
                      self->area.width, self->area.height, FALSE, TRUE);
 }
 
@@ -2211,8 +2262,21 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y)
     */
 }
 
-void client_try_configure(ObClient *self, ObCorner anchor,
-                          gint *x, gint *y, gint *w, gint *h,
+void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y,
+                            gint w, gint h)
+{
+    gint oldg = self->gravity;
+
+    /* get the frame's position from the requested stuff */
+    self->gravity = gravity;
+    frame_client_gravity(self->frame, x, y, w, h);
+    self->gravity = oldg;
+
+    /* get the client's position in its true gravity from that */
+    frame_frame_gravity(self->frame, x, y, w, h);
+}
+
+void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
                           gint *logicalw, gint *logicalh,
                           gboolean user)
 {
@@ -2307,7 +2371,7 @@ void client_try_configure(ObClient *self, ObCorner anchor,
     }
 
     /* gets the frame's position */
-    frame_client_gravity(self->frame, x, y);
+    frame_client_gravity(self->frame, x, y, *w, *h);
 
     /* these positions are frame positions, not client positions */
 
@@ -2348,7 +2412,7 @@ void client_try_configure(ObClient *self, ObCorner anchor,
     }
 
     /* gets the client's position */
-    frame_frame_gravity(self->frame, x, y);
+    frame_frame_gravity(self->frame, x, y, *w, *h);
 
     /* these override the above states! if you cant move you can't move! */
     if (user) {
@@ -2364,26 +2428,10 @@ void client_try_configure(ObClient *self, ObCorner anchor,
 
     g_assert(*w > 0);
     g_assert(*h > 0);
-
-    switch (anchor) {
-    case OB_CORNER_TOPLEFT:
-        break;
-    case OB_CORNER_TOPRIGHT:
-        *x -= *w - self->area.width;
-        break;
-    case OB_CORNER_BOTTOMLEFT:
-        *y -= *h - self->area.height;
-        break;
-    case OB_CORNER_BOTTOMRIGHT:
-        *x -= *w - self->area.width;
-        *y -= *h - self->area.height;
-        break;
-    }
 }
 
 
-void client_configure_full(ObClient *self, ObCorner anchor,
-                           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,
                            gboolean force_reply)
 {
@@ -2395,8 +2443,7 @@ void client_configure_full(ObClient *self, ObCorner anchor,
     gint logicalw, logicalh;
 
     /* find the new x, y, width, and height (and logical size) */
-    client_try_configure(self, anchor, &x, &y, &w, &h,
-                         &logicalw, &logicalh, user);
+    client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
 
     /* set the logical size if things changed */
     if (!(w == self->area.width && h == self->area.height))
@@ -2420,6 +2467,7 @@ void client_configure_full(ObClient *self, ObCorner anchor,
     /* 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 */
         frame_adjust_client_area(self->frame);
     }
 
@@ -2468,7 +2516,9 @@ void client_configure_full(ObClient *self, ObCorner anchor,
 
     /* 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 */
         frame_adjust_client_area(self->frame);
+
         XResizeWindow(ob_display, self->window, w, h);
     }
 
@@ -3244,7 +3294,7 @@ void client_set_undecorated(ObClient *self, gboolean undecorated)
          * since 125 of these are sent per second when moving the window (with
          * user = FALSE) i doubt it matters much.
          */
-        client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
+        client_configure(self, self->area.x, self->area.y,
                          self->area.width, self->area.height, TRUE, TRUE);
         client_change_state(self); /* reflect this in the state hints */
     }
This page took 0.027448 seconds and 4 git commands to generate.