]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
watch for reverttoparent reverting to the root window, which will create a DetailInfe...
[chaz/openbox] / openbox / client.c
index 8358293dcfd5f8f178be90bd10fb1acbaf2d1a40..a51ecfd7c7ed54ce2ba0e094d394b9969cb6f885 100644 (file)
@@ -2,7 +2,7 @@
    
    client.c for the Openbox window manager
    Copyright (c) 2006        Mikael Magnusson
-   Copyright (c) 2003        Ben Jansens
+   Copyright (c) 2003-2007   Dana Jansens
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -45,8 +45,7 @@
 #include <X11/Xutil.h>
 
 /*! The event mask to grab on client windows */
-#define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
-                          StructureNotifyMask)
+#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
 
 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
                                 ButtonMotionMask)
@@ -72,12 +71,14 @@ static void client_get_layer(ObClient *self);
 static void client_get_shaped(ObClient *self);
 static void client_get_mwm_hints(ObClient *self);
 static void client_get_gravity(ObClient *self);
-static void client_showhide(ObClient *self);
 static void client_change_allowed_actions(ObClient *self);
 static void client_change_state(ObClient *self);
-static void client_apply_startup_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_restore_session_state(ObClient *self);
 static void client_restore_session_stacking(ObClient *self);
+static ObAppSettings *client_get_settings_state(ObClient *self);
+static void client_unfocus(ObClient *self);
 
 void client_startup(gboolean reconfig)
 {
@@ -206,33 +207,6 @@ void client_manage_all()
     XFree(children);
 }
 
-static ObAppSettings *get_settings(ObClient *client)
-{
-    GSList *a = config_per_app_settings;
-
-    while (a) {
-        ObAppSettings *app = (ObAppSettings *) a->data;
-        
-        if (
-            (app->name && !app->class && !strcmp(app->name, client->name))
-            || (app->class && !app->name && !strcmp(app->class, client->class))
-            || (app->class && app->name && !strcmp(app->class, client->class)
-                && !strcmp(app->name, client->name))
-            ) {
-            ob_debug("Window matching: %s\n", app->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, client->role, strlen(app->role)))
-                return app;
-        }
-
-        a = a->next;
-    }
-    return NULL;
-}
-
 void client_manage(Window window)
 {
     ObClient *self;
@@ -242,16 +216,18 @@ void client_manage(Window window)
     XWMHints *wmhint;
     gboolean activate = FALSE;
     ObAppSettings *settings;
+    gint newx, newy;
 
     grab_server(TRUE);
 
-    /* check if it has already been unmapped by the time we started mapping
+    /* check if it has already been unmapped by the time we started mapping.
        the grab does a sync so we don't have to here */
     if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
         XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
     {
         XPutBackEvent(ob_display, &e);
 
+        ob_debug("Trying to manage unmapped window. Aborting that.\n");
         grab_server(FALSE);
         return; /* don't manage it */
     }
@@ -293,14 +269,16 @@ void client_manage(Window window)
     self->window = window;
 
     /* non-zero defaults */
-    self->title_count = 1;
-    self->wmstate = NormalState;
+    self->wmstate = WithdrawnState; /* make sure it gets updated first time */
     self->layer = -1;
     self->desktop = screen_num_desktops; /* always an invalid value */
     self->user_time = ~0; /* maximum value, always newer than the real time */
 
     client_get_all(self);
     client_restore_session_state(self);
+    /* per-app settings override stuff, and return the settings for other
+       uses too */
+    settings = client_get_settings_state(self);
 
     client_calc_layer(self);
 
@@ -313,8 +291,6 @@ void client_manage(Window window)
        it can end up in the list twice! */
     focus_order_add_new(self);
 
-    client_change_state(self);
-
     /* remove the client's border (and adjust re gravity) */
     client_toggle_border(self, FALSE);
      
@@ -327,59 +303,15 @@ void client_manage(Window window)
 
     frame_grab_client(self->frame, self);
 
-    grab_server(FALSE);
-
-    client_apply_startup_state(self);
+    /* do this after we have a frame.. it uses the frame to help determine the
+       WM_STATE to apply. */
+    client_change_state(self);
 
-    /* get and set application level settings */
-    settings = get_settings(self);
+    grab_server(FALSE);
 
     stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
     client_restore_session_stacking(self);
 
-    if (settings) {
-        /* Don't worry, we won't actually both shade and undecorate the
-         * window when push comes to shove. */
-        if (settings->shade != -1)
-            client_shade(self, !!settings->shade);
-        if (settings->decor != -1)
-            client_set_undecorated(self, !settings->decor);
-        if (settings->iconic != -1)
-            client_iconify(self, !!settings->iconic, FALSE);
-        if (settings->skip_pager != -1) {
-            self->skip_pager = !!settings->skip_pager;
-            client_change_state(self);
-        }
-        if (settings->skip_taskbar != -1) {
-            self->skip_taskbar = !!settings->skip_taskbar;
-            client_change_state(self);
-        }
-
-        /* 1 && -1 shouldn't be possible by the code in config.c */
-        if (settings->max_vert == 1 && settings->max_horz == 1)
-            client_maximize(self, TRUE, 0, TRUE);
-        else if (settings->max_vert == 0 && settings->max_horz == 0)
-            client_maximize(self, FALSE, 0, TRUE);
-        else if (settings->max_vert == 1 && settings->max_horz == 0) {
-            client_maximize(self, TRUE, 2, TRUE);
-            client_maximize(self, FALSE, 1, TRUE);
-        } else if (settings->max_vert == 0 && settings->max_horz == 1) {
-            client_maximize(self, TRUE, 1, TRUE);
-            client_maximize(self, FALSE, 2, TRUE);
-        }
-
-        if (settings->fullscreen != -1)
-            client_fullscreen(self, !!settings->fullscreen, TRUE);
-
-        if (settings->desktop < screen_num_desktops
-            || settings->desktop == DESKTOP_ALL)
-            client_set_desktop(self, settings->desktop, TRUE);
-
-        if (settings->layer > -2 && settings->layer < 2)
-            client_set_layer(self, settings->layer);
-
-    }
-
     /* focus the new window? */
     if (ob_state() != OB_STATE_STARTING &&
         /* this means focus=true for window is same as config_focus_new=true */
@@ -427,15 +359,18 @@ void client_manage(Window window)
 #endif
     }
 
+    /* get the current position */
+    newx = self->area.x;
+    newy = self->area.y;
+
+    /* figure out placement for the window */
     if (ob_state() == OB_STATE_RUNNING) {
-        gint x = self->area.x, ox = x;
-        gint y = self->area.y, oy = y;
         gboolean transient;
 
-        transient = place_client(self, &x, &y, settings);
+        transient = place_client(self, &newx, &newy, settings);
 
         /* make sure the window is visible. */
-        client_find_onscreen(self, &x, &y,
+        client_find_onscreen(self, &newx, &newy,
                              self->frame->area.width,
                              self->frame->area.height,
                              /* non-normal clients has less rules, and
@@ -453,10 +388,16 @@ void client_manage(Window window)
                                !(self->positioned & USPosition)) &&
                               client_normal(self) &&
                               !self->session));
-        if (x != ox || y != oy)         
-            client_move(self, x, y);
     }
 
+    /* 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);
+    client_apply_startup_state(self, newx, newy);
+
     keyboard_grab_for_client(self, TRUE);
     mouse_grab_for_client(self, TRUE);
 
@@ -469,9 +410,7 @@ void client_manage(Window window)
            always
         */
         if (!focus_client || client_search_focus_parent(self) != NULL)
-        {
             activate = TRUE;
-        }
         else
         {
             /* If time stamp is old, don't steal focus */
@@ -480,10 +419,8 @@ void client_manage(Window window)
             /* 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)
-            {
+            if (!(focus_client->can_focus || focus_client->focus_notify))
                 activate = FALSE;
-            }
         }
 
         if (activate)
@@ -559,33 +496,49 @@ void client_unmanage(ObClient *self)
     guint j;
     GSList *it;
 
-    ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
+    ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class,
+             self->title ? self->title : "");
 
     g_assert(self != NULL);
 
-    keyboard_grab_for_client(self, FALSE);
-    mouse_grab_for_client(self, FALSE);
+    /* update the focus lists */
+    focus_order_remove(self);
+
+    if (focus_client == self) {
+        XEvent e;
+
+        /* focus the last focused window on the desktop, and ignore enter
+           events from the unmap so it doesnt mess with the focus */
+        while (XCheckTypedEvent(ob_display, EnterNotify, &e));
+        /* remove these flags so we don't end up getting focused in the
+           fallback! */
+        self->can_focus = FALSE;
+        self->focus_notify = FALSE;
+        self->modal = FALSE;
+        client_unfocus(self);
+    }
 
     /* potentially fix focusLast */
     if (config_focus_last)
         grab_pointer(TRUE, OB_CURSOR_NONE);
 
+    frame_hide(self->frame);
+    XFlush(ob_display);
+
+    keyboard_grab_for_client(self, FALSE);
+    mouse_grab_for_client(self, FALSE);
+
     /* remove the window from our save set */
     XChangeSaveSet(ob_display, self->window, SetModeDelete);
 
     /* we dont want events no more */
     XSelectInput(ob_display, self->window, NoEventMask);
 
-    frame_hide(self->frame);
-
     client_list = g_list_remove(client_list, self);
     stacking_remove(self);
     g_hash_table_remove(window_map, &self->window);
 
-    /* update the focus lists */
-    focus_order_remove(self);
-
-    /* once the client is out of the list, update the struts to remove it's
+    /* once the client is out of the list, update the struts to remove its
        influence */
     if (STRUT_EXISTS(self->strut))
         screen_update_areas();
@@ -594,20 +547,6 @@ void client_unmanage(ObClient *self)
         Destructor *d = it->data;
         d->func(self, d->data);
     }
-        
-    if (focus_client == self) {
-        XEvent e;
-
-        /* focus the last focused window on the desktop, and ignore enter
-           events from the unmap so it doesnt mess with the focus */
-        while (XCheckTypedEvent(ob_display, EnterNotify, &e));
-        /* remove these flags so we don't end up getting focused in the
-           fallback! */
-        self->can_focus = FALSE;
-        self->focus_notify = FALSE;
-        self->modal = FALSE;
-        client_unfocus(self);
-    }
 
     /* tell our parent(s) that we're gone */
     if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
@@ -640,6 +579,27 @@ void client_unmanage(ObClient *self)
     /* reparent the window out of the frame, and free the frame */
     frame_release_client(self->frame, self);
     self->frame = NULL;
+
+    /* restore the window's original geometry so it is not lost */
+    if (self->fullscreen)
+        XMoveResizeWindow(ob_display, self->window,
+                          self->pre_fullscreen_area.x,
+                          self->pre_fullscreen_area.y,
+                          self->pre_fullscreen_area.width,
+                          self->pre_fullscreen_area.height);
+    else if (self->max_horz || self->max_vert) {
+        Rect a = self->area;
+        if (self->max_horz) {
+            a.x = self->pre_max_area.x;
+            a.width = self->pre_max_area.width;
+        }
+        if (self->max_vert) {
+            a.y = self->pre_max_area.y;
+            a.height = self->pre_max_area.height;
+        }
+        XMoveResizeWindow(ob_display, self->window,
+                          a.x, a.y, a.width, a.height);
+    }
      
     if (ob_state() != OB_STATE_EXITING) {
         /* these values should not be persisted across a window
@@ -677,6 +637,73 @@ void client_unmanage(ObClient *self)
         grab_pointer(FALSE, OB_CURSOR_NONE);
 }
 
+static ObAppSettings *client_get_settings_state(ObClient *self)
+{
+    ObAppSettings *settings = NULL;
+    GSList *it;
+
+    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)))
+        {
+            ob_debug("Window matching: %s\n", app->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)))
+            {
+                /* use this one */
+                settings = app;
+                break;
+            }
+        }
+    }
+
+    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_vert = !!settings->max_horz;
+
+        if (settings->fullscreen != -1)
+            self->fullscreen = !!settings->fullscreen;
+
+        if (settings->desktop < screen_num_desktops
+            || settings->desktop == DESKTOP_ALL)
+            client_set_desktop(self, settings->desktop, TRUE);
+
+        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;
+        }
+    }
+    return settings;
+}
+
 static void client_restore_session_state(ObClient *self)
 {
     GList *it;
@@ -939,6 +966,9 @@ static void client_get_area(ObClient *self)
 
     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
     self->border_width = wattrib.border_width;
+
+    ob_debug("client area: %d %d  %d %d\n", wattrib.x, wattrib.y,
+             wattrib.width, wattrib.height);
 }
 
 static void client_get_desktop(ObClient *self)
@@ -1121,9 +1151,15 @@ void client_update_transient_for(ObClient *self)
                 }
             }
         }
-    } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
-        self->transient = TRUE;
-        target = OB_TRAN_GROUP;
+    } else if (self->group) {
+        if (self->type == OB_CLIENT_TYPE_DIALOG ||
+            self->type == OB_CLIENT_TYPE_TOOLBAR ||
+            self->type == OB_CLIENT_TYPE_MENU ||
+            self->type == OB_CLIENT_TYPE_UTILITY)
+        {
+            self->transient = TRUE;
+            target = OB_TRAN_GROUP;
+        }
     } else
         self->transient = FALSE;
 
@@ -1509,12 +1545,12 @@ static void client_change_allowed_actions(ObClient *self)
         else self->iconic = FALSE;
     }
     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
-        if (self->frame) client_fullscreen(self, FALSE, TRUE);
+        if (self->frame) client_fullscreen(self, FALSE);
         else self->fullscreen = FALSE;
     }
     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
                                                          self->max_vert)) {
-        if (self->frame) client_maximize(self, FALSE, 0, TRUE);
+        if (self->frame) client_maximize(self, FALSE, 0);
         else self->max_vert = self->max_horz = FALSE;
     }
 }
@@ -1610,108 +1646,44 @@ void client_update_wmhints(ObClient *self)
 
 void client_update_title(ObClient *self)
 {
-    GList *it;
-    guint32 nums;
-    guint i;
     gchar *data = NULL;
-    gboolean read_title;
-    gchar *old_title;
 
-    old_title = self->title;
+    g_free(self->title);
      
     /* try netwm */
     if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
         /* try old x stuff */
         if (!(PROP_GETS(self->window, wm_name, locale, &data)
               || PROP_GETS(self->window, wm_name, utf8, &data))) {
-            // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
             if (self->transient) {
+                /*
+                  GNOME alert windows are not given titles:
+                  http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+                */
                 data = g_strdup("");
-                goto no_number;
             } else
                 data = g_strdup("Unnamed Window");
         }
     }
 
-    if (config_title_number) {
-
-        /* did the title change? then reset the title_count */
-        if (old_title && 0 != strncmp(old_title, data, strlen(data)))
-            self->title_count = 1;
-
-        /* look for duplicates and append a number */
-        nums = 0;
-        for (it = client_list; it; it = g_list_next(it))
-            if (it->data != self) {
-                ObClient *c = it->data;
-
-                if (c->title_count == 1) {
-                    if (!strcmp(c->title, data))
-                        nums |= 1 << c->title_count;
-                } else {
-                    size_t len;
-                    gchar *end;
-
-                    /* find the beginning of our " - [%u]", this relies on
-                     that syntax being used */
-                    end = strrchr(c->title, '-') - 1; 
-                    len = end - c->title;
-                    if (!strncmp(c->title, data, len))
-                        nums |= 1 << c->title_count;
-                }
-            }
-        /* find first free number */
-        for (i = 1; i <= 32; ++i)
-            if (!(nums & (1 << i))) {
-                if (self->title_count == 1 || i == 1)
-                    self->title_count = i;
-                break;
-            }
-        /* dont display the number for the first window */
-        if (self->title_count > 1) {
-            gchar *ndata;
-            ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
-            g_free(data);
-            data = ndata;
-        }
-    } else
-        self->title_count = 1;
-
-no_number:
     PROP_SETS(self->window, net_wm_visible_name, data);
     self->title = data;
 
     if (self->frame)
         frame_adjust_title(self->frame);
 
-    g_free(old_title);
-
     /* update the icon title */
     data = NULL;
     g_free(self->icon_title);
 
-    read_title = TRUE;
     /* try netwm */
     if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
         /* try old x stuff */
-        if (!(PROP_GETS(self->window, wm_icon_name, locale, &data)
-              || PROP_GETS(self->window, wm_icon_name, utf8, &data))) {
+        if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
+              PROP_GETS(self->window, wm_icon_name, utf8, &data)))
             data = g_strdup(self->title);
-            read_title = FALSE;
-        }
-
-    /* append the title count, dont display the number for the first window.
-     * We don't need to check for config_title_number here since title_count
-     * is not set above 1 then. */
-    if (read_title && self->title_count > 1) {
-        gchar *newdata;
-        newdata = g_strdup_printf("%s - [%u]", data, self->title_count);
-        g_free(data);
-        data = newdata;
-    }
 
     PROP_SETS(self->window, net_wm_visible_icon_name, data);
-
     self->icon_title = data;
 }
 
@@ -1893,20 +1865,40 @@ void client_update_user_time(ObClient *self, gboolean new_event)
         if (new_event)
             client_last_user_time = time;
 
-        /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
+        /*
+        ob_debug("window %s user time %u\n", self->title, time);
+        ob_debug("last user time %u\n", client_last_user_time);
+        */
     }
 }
 
-static void client_change_state(ObClient *self)
+static void client_change_wm_state(ObClient *self)
 {
     gulong state[2];
+    glong old;
+
+    old = self->wmstate;
+
+    if (self->shaded || self->iconic || !self->frame->visible)
+        self->wmstate = IconicState;
+    else
+        self->wmstate = NormalState;
+
+    if (old != self->wmstate) {
+        PROP_MSG(self->window, kde_wm_change_state,
+                 self->wmstate, 1, 0, 0);
+
+        state[0] = self->wmstate;
+        state[1] = None;
+        PROP_SETA32(self->window, wm_state, wm_state, state, 2);
+    }
+}
+
+static void client_change_state(ObClient *self)
+{
     gulong netstate[11];
     guint num;
 
-    state[0] = self->wmstate;
-    state[1] = None;
-    PROP_SETA32(self->window, wm_state, wm_state, state, 2);
-
     num = 0;
     if (self->modal)
         netstate[num++] = prop_atoms.net_wm_state_modal;
@@ -2008,8 +2000,6 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
     own = calc_layer(self);
     self->layer = MAX(own, min);
 
-    ob_debug("layer for %s: %d\n", self->title, self->layer);
-
     for (it = self->transients; it; it = g_slist_next(it))
         client_calc_layer_recursive(it->data, orig,
                                     self->layer,
@@ -2030,7 +2020,7 @@ void client_calc_layer(ObClient *self)
     orig = self;
 
     /* transients take on the layer of their parents */
-    it = client_search_top_transients(self);
+    it = client_search_all_top_parents(self);
 
     for (; it; it = g_slist_next(it))
         client_calc_layer_recursive(it->data, orig, 0, FALSE);
@@ -2065,13 +2055,25 @@ gboolean client_should_show(ObClient *self)
     return FALSE;
 }
 
-static void client_showhide(ObClient *self)
+void client_showhide(ObClient *self)
 {
 
-    if (client_should_show(self))
+    if (client_should_show(self)) {
         frame_show(self->frame);
-    else
+    }
+    else {
         frame_hide(self->frame);
+
+        /* Fall back focus since we're disappearing */
+        if (focus_client == self)
+            client_unfocus(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);
 }
 
 gboolean client_normal(ObClient *self) {
@@ -2080,8 +2082,17 @@ gboolean client_normal(ObClient *self) {
               self->type == OB_CLIENT_TYPE_SPLASH);
 }
 
-static void client_apply_startup_state(ObClient *self)
+static void client_apply_startup_state(ObClient *self, gint x, gint y)
 {
+    gboolean pos = FALSE; /* has the window's position been configured? */
+    gint ox, oy;
+
+    /* 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;
+
     /* these are in a carefully crafted order.. */
 
     if (self->iconic) {
@@ -2090,7 +2101,8 @@ static void client_apply_startup_state(ObClient *self)
     }
     if (self->fullscreen) {
         self->fullscreen = FALSE;
-        client_fullscreen(self, TRUE, FALSE);
+        client_fullscreen(self, TRUE);
+        pos = TRUE;
     }
     if (self->undecorated) {
         self->undecorated = FALSE;
@@ -2107,13 +2119,24 @@ static void client_apply_startup_state(ObClient *self)
   
     if (self->max_vert && self->max_horz) {
         self->max_vert = self->max_horz = FALSE;
-        client_maximize(self, TRUE, 0, FALSE);
+        client_maximize(self, TRUE, 0);
+        pos = TRUE;
     } else if (self->max_vert) {
         self->max_vert = FALSE;
-        client_maximize(self, TRUE, 2, FALSE);
+        client_maximize(self, TRUE, 2);
+        pos = TRUE;
     } else if (self->max_horz) {
         self->max_horz = FALSE;
-        client_maximize(self, TRUE, 1, FALSE);
+        client_maximize(self, TRUE, 1);
+        pos = TRUE;
+    }
+
+    /* if the client didn't get positioned yet, then do so now */
+    if (!pos && (ox != x || oy != y)) {
+        /* use the saved position */
+        self->area.x = ox;
+        self->area.y = oy;
+        client_move(self, x, y);
     }
 
     /* nothing to do for the other states:
@@ -2125,17 +2148,12 @@ static void client_apply_startup_state(ObClient *self)
     */
 }
 
-void client_configure_full(ObClient *self, ObCorner anchor,
-                           gint x, gint y, gint w, gint h,
-                           gboolean user, gboolean final,
-                           gboolean force_reply)
+void client_try_configure(ObClient *self, ObCorner anchor,
+                          gint *x, gint *y, gint *w, gint *h,
+                          gint *logicalw, gint *logicalh,
+                          gboolean user)
 {
-    gint oldw, oldh;
-    gboolean send_resize_client;
-    gboolean moved = FALSE, resized = FALSE;
-    guint fdecor = self->frame->decorations;
-    gboolean fhorz = self->frame->max_horz;
-    Rect desired_area = {x, y, w, h};
+    Rect desired_area = {*x, *y, *w, *h};
 
     /* make the frame recalculate its dimentions n shit without changing
        anything visible for real, this way the constraints below can work with
@@ -2143,7 +2161,7 @@ void client_configure_full(ObClient *self, ObCorner anchor,
     frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
 
     /* gets the frame's position */
-    frame_client_gravity(self->frame, &x, &y);
+    frame_client_gravity(self->frame, x, y);
 
     /* these positions are frame positions, not client positions */
 
@@ -2155,10 +2173,10 @@ void client_configure_full(ObClient *self, ObCorner anchor,
         i = screen_find_monitor(&desired_area);
         a = screen_physical_area_monitor(i);
 
-        x = a->x;
-        y = a->y;
-        w = a->width;
-        h = a->height;
+        *x = a->x;
+        *y = a->y;
+        *w = a->width;
+        *h = a->height;
 
         user = FALSE; /* ignore that increment etc shit when in fullscreen */
     } else {
@@ -2170,31 +2188,31 @@ void client_configure_full(ObClient *self, ObCorner anchor,
 
         /* set the size and position if maximized */
         if (self->max_horz) {
-            x = a->x;
-            w = a->width - self->frame->size.left - self->frame->size.right;
+            *x = a->x;
+            *w = a->width - self->frame->size.left - self->frame->size.right;
         }
         if (self->max_vert) {
-            y = a->y;
-            h = a->height - self->frame->size.top - self->frame->size.bottom;
+            *y = a->y;
+            *h = a->height - self->frame->size.top - self->frame->size.bottom;
         }
     }
 
     /* gets the client's position */
-    frame_frame_gravity(self->frame, &x, &y);
+    frame_frame_gravity(self->frame, x, y);
 
     /* these override the above states! if you cant move you can't move! */
     if (user) {
         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
-            x = self->area.x;
-            y = self->area.y;
+            *x = self->area.x;
+            *y = self->area.y;
         }
         if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
-            w = self->area.width;
-            h = self->area.height;
+            *w = self->area.width;
+            *h = self->area.height;
         }
     }
 
-    if (!(w == self->area.width && h == self->area.height)) {
+    if (!(*w == self->area.width && *h == self->area.height)) {
         gint basew, baseh, minw, minh;
 
         /* base size is substituted with min size if not specified */
@@ -2218,83 +2236,105 @@ void client_configure_full(ObClient *self, ObCorner anchor,
            sizes */
 
         /* smaller than min size or bigger than max size? */
-        if (w > self->max_size.width) w = self->max_size.width;
-        if (w < minw) w = minw;
-        if (h > self->max_size.height) h = self->max_size.height;
-        if (h < minh) h = minh;
+        if (*w > self->max_size.width) *w = self->max_size.width;
+        if (*w < minw) *w = minw;
+        if (*h > self->max_size.height) *h = self->max_size.height;
+        if (*h < minh) *h = minh;
 
-        w -= basew;
-        h -= baseh;
+        *w -= basew;
+        *h -= baseh;
 
         /* keep to the increments */
-        w /= self->size_inc.width;
-        h /= self->size_inc.height;
+        *w /= self->size_inc.width;
+        *h /= self->size_inc.height;
 
         /* you cannot resize to nothing */
-        if (basew + w < 1) w = 1 - basew;
-        if (baseh + h < 1) h = 1 - baseh;
+        if (basew + *w < 1) *w = 1 - basew;
+        if (baseh + *h < 1) *h = 1 - baseh;
   
-        /* store the logical size */
-        SIZE_SET(self->logical_size,
-                 self->size_inc.width > 1 ? w : w + basew,
-                 self->size_inc.height > 1 ? h : h + baseh);
+        /* save the logical size */
+        *logicalw = self->size_inc.width > 1 ? *w : *w + basew;
+        *logicalh = self->size_inc.height > 1 ? *h : *h + baseh;
 
-        w *= self->size_inc.width;
-        h *= self->size_inc.height;
+        *w *= self->size_inc.width;
+        *h *= self->size_inc.height;
 
-        w += basew;
-        h += baseh;
+        *w += basew;
+        *h += baseh;
 
         /* adjust the height to match the width for the aspect ratios.
            for this, min size is not substituted for base size ever. */
-        w -= self->base_size.width;
-        h -= self->base_size.height;
+        *w -= self->base_size.width;
+        *h -= self->base_size.height;
 
         if (!self->fullscreen) {
             if (self->min_ratio)
-                if (h * self->min_ratio > w) {
-                    h = (gint)(w / self->min_ratio);
+                if (*h * self->min_ratio > *w) {
+                    *h = (gint)(*w / self->min_ratio);
 
                     /* you cannot resize to nothing */
-                    if (h < 1) {
-                        h = 1;
-                        w = (gint)(h * self->min_ratio);
+                    if (*h < 1) {
+                        *h = 1;
+                        *w = (gint)(*h * self->min_ratio);
                     }
                 }
             if (self->max_ratio)
-                if (h * self->max_ratio < w) {
-                    h = (gint)(w / self->max_ratio);
+                if (*h * self->max_ratio < *w) {
+                    *h = (gint)(*w / self->max_ratio);
 
                     /* you cannot resize to nothing */
-                    if (h < 1) {
-                        h = 1;
-                        w = (gint)(h * self->min_ratio);
+                    if (*h < 1) {
+                        *h = 1;
+                        *w = (gint)(*h * self->min_ratio);
                     }
                 }
         }
 
-        w += self->base_size.width;
-        h += self->base_size.height;
+        *w += self->base_size.width;
+        *h += self->base_size.height;
     }
 
-    g_assert(w > 0);
-    g_assert(h > 0);
+    g_assert(*w > 0);
+    g_assert(*h > 0);
 
     switch (anchor) {
     case OB_CORNER_TOPLEFT:
         break;
     case OB_CORNER_TOPRIGHT:
-        x -= w - self->area.width;
+        *x -= *w - self->area.width;
         break;
     case OB_CORNER_BOTTOMLEFT:
-        y -= h - self->area.height;
+        *y -= *h - self->area.height;
         break;
     case OB_CORNER_BOTTOMRIGHT:
-        x -= w - self->area.width;
-        y -= h - self->area.height;
+        *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,
+                           gboolean user, gboolean final,
+                           gboolean force_reply)
+{
+    gint oldw, oldh;
+    gboolean send_resize_client;
+    gboolean moved = FALSE, resized = FALSE;
+    guint fdecor = self->frame->decorations;
+    gboolean fhorz = self->frame->max_horz;
+    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);
+
+    /* set the logical size if things changed */
+    if (!(w == self->area.width && h == self->area.height))
+        SIZE_SET(self->logical_size, logicalw, logicalh);
+
+    /* 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;
 
@@ -2351,7 +2391,7 @@ void client_configure_full(ObClient *self, ObCorner anchor,
     XFlush(ob_display);
 }
 
-void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
+void client_fullscreen(ObClient *self, gboolean fs)
 {
     gint x, y, w, h;
 
@@ -2363,8 +2403,17 @@ void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
     client_calc_layer(self);   /* and adjust out layer/stacking */
 
     if (fs) {
-        if (savearea)
-            self->pre_fullscreen_area = self->area;
+        self->pre_fullscreen_area = self->area;
+        /* if the window is maximized, its area isn't all that meaningful.
+           save it's premax area instead. */
+        if (self->max_horz) {
+            self->pre_fullscreen_area.x = self->pre_max_area.x;
+            self->pre_fullscreen_area.width = self->pre_max_area.width;
+        }
+        if (self->max_vert) {
+            self->pre_fullscreen_area.y = self->pre_max_area.y;
+            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 */
@@ -2409,17 +2458,9 @@ static void client_iconify_recursive(ObClient *self,
         ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
                  self->window);
 
-        self->iconic = iconic;
-
         if (iconic) {
             if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
-                glong old;
-
-                old = self->wmstate;
-                self->wmstate = IconicState;
-                if (old != self->wmstate)
-                    PROP_MSG(self->window, kde_wm_change_state,
-                             self->wmstate, 1, 0, 0);
+                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
@@ -2429,17 +2470,11 @@ static void client_iconify_recursive(ObClient *self,
                 changed = TRUE;
             }
         } else {
-            glong old;
+            self->iconic = iconic;
 
             if (curdesk)
                 client_set_desktop(self, screen_desktop, FALSE);
 
-            old = self->wmstate;
-            self->wmstate = self->shaded ? IconicState : NormalState;
-            if (old != self->wmstate)
-                PROP_MSG(self->window, kde_wm_change_state,
-                         self->wmstate, 1, 0, 0);
-
             /* this puts it after the current focused window */
             focus_order_remove(self);
             focus_order_add_new(self);
@@ -2455,24 +2490,21 @@ static void client_iconify_recursive(ObClient *self,
             screen_update_areas();
     }
 
-    /* iconify all transients */
+    /* iconify all direct transients */
     for (it = self->transients; it; it = g_slist_next(it))
-        if (it->data != self) client_iconify_recursive(it->data,
-                                                       iconic, curdesk);
+        if (it->data != self)
+            if (client_is_direct_child(self, it->data))
+                client_iconify_recursive(it->data, iconic, curdesk);
 }
 
 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
 {
-    GSList *it;
-
     /* move up the transient chain as far as possible first */
-    it = client_search_top_transients(self);
-
-    for (; it; it = g_slist_next(it))
-    client_iconify_recursive(it->data, iconic, curdesk);
+    self = client_search_top_parent(self);
+    client_iconify_recursive(self, iconic, curdesk);
 }
 
-void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
+void client_maximize(ObClient *self, gboolean max, gint dir)
 {
     gint x, y, w, h;
      
@@ -2498,17 +2530,15 @@ void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
     h = self->area.height;
 
     if (max) {
-        if (savearea) {
-            if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
-                RECT_SET(self->pre_max_area,
-                         self->area.x, self->pre_max_area.y,
-                         self->area.width, self->pre_max_area.height);
-            }
-            if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
-                RECT_SET(self->pre_max_area,
-                         self->pre_max_area.x, self->area.y,
-                         self->pre_max_area.width, self->area.height);
-            }
+        if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
+            RECT_SET(self->pre_max_area,
+                     self->area.x, self->pre_max_area.y,
+                     self->area.width, self->pre_max_area.height);
+        }
+        if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
+            RECT_SET(self->pre_max_area,
+                     self->pre_max_area.x, self->area.y,
+                     self->pre_max_area.width, self->area.height);
         }
     } else {
         Rect *a;
@@ -2560,19 +2590,9 @@ void client_shade(ObClient *self, gboolean shade)
          shade) ||                         /* can't shade */
         self->shaded == shade) return;     /* already done */
 
-    /* when we're iconic, don't change the wmstate */
-    if (!self->iconic) {
-        glong old;
-
-        old = self->wmstate;
-        self->wmstate = shade ? IconicState : NormalState;
-        if (old != self->wmstate)
-            PROP_MSG(self->window, kde_wm_change_state,
-                     self->wmstate, 1, 0, 0);
-    }
-
     self->shaded = shade;
     client_change_state(self);
+    client_change_wm_state(self); /* the window is being hidden/shown */
     /* resize the frame to just the titlebar */
     frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
 }
@@ -2616,6 +2636,9 @@ void client_kill(ObClient *self)
 
 void client_hilite(ObClient *self, gboolean hilite)
 {
+    if (self->demands_attention == hilite)
+        return; /* no change */
+
     /* don't allow focused windows to hilite */
     self->demands_attention = hilite && !client_focused(self);
     if (self->demands_attention)
@@ -2663,18 +2686,23 @@ void client_set_desktop_recursive(ObClient *self,
 
     /* move all transients */
     for (it = self->transients; it; it = g_slist_next(it))
-        if (it->data != self) client_set_desktop_recursive(it->data,
-                                                           target, donthide);
+        if (it->data != self)
+            if (client_is_direct_child(self, it->data))
+                client_set_desktop_recursive(it->data, target, donthide);
 }
 
 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
 {
-    GSList *it;
-
-    it = client_search_top_transients(self);
+    self = client_search_top_parent(self);
+    client_set_desktop_recursive(self, target, donthide);
+}
 
-    for(; it; it = g_slist_next(it))
-        client_set_desktop_recursive(it->data, target, donthide);
+gboolean client_is_direct_child(ObClient *parent, ObClient *child)
+{
+    while (child != parent &&
+           child->transient_for && child->transient_for != OB_TRAN_GROUP)
+        child = child->transient_for;
+    return child == parent;
 }
 
 ObClient *client_search_modal_child(ObClient *self)
@@ -2848,23 +2876,23 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
         if (max_horz != self->max_horz && max_vert != self->max_vert) {
             /* toggling both */
             if (max_horz == max_vert) { /* both going the same way */
-                client_maximize(self, max_horz, 0, TRUE);
+                client_maximize(self, max_horz, 0);
             } else {
-                client_maximize(self, max_horz, 1, TRUE);
-                client_maximize(self, max_vert, 2, TRUE);
+                client_maximize(self, max_horz, 1);
+                client_maximize(self, max_vert, 2);
             }
         } else {
             /* toggling one */
             if (max_horz != self->max_horz)
-                client_maximize(self, max_horz, 1, TRUE);
+                client_maximize(self, max_horz, 1);
             else
-                client_maximize(self, max_vert, 2, TRUE);
+                client_maximize(self, max_vert, 2);
         }
     }
     /* change fullscreen state before shading, as it will affect if the window
        can shade or not */
     if (fullscreen != self->fullscreen)
-        client_fullscreen(self, fullscreen, TRUE);
+        client_fullscreen(self, fullscreen);
     if (shaded != self->shaded)
         client_shade(self, shaded);
     if (undecorated != self->undecorated)
@@ -2932,6 +2960,11 @@ gboolean client_focus(ObClient *self)
     /* choose the correct target */
     self = client_focus_target(self);
 
+#if 0
+    if (!client_validate(self))
+        return FALSE;
+#endif
+
     if (!client_can_focus(self)) {
         if (!self->frame->visible) {
             /* update the focus lists */
@@ -2940,15 +2973,10 @@ gboolean client_focus(ObClient *self)
         return FALSE;
     }
 
+    ob_debug("Focusing client \"%s\" at time %u\n", self->title, event_curtime);
+
     if (self->can_focus) {
-        /* RevertToPointerRoot causes much more headache than RevertToNone, so
-           I choose to use it always, hopefully to find errors quicker, if any
-           are left. (I hate X. I hate focus events.)
-           
-           Update: Changing this to RevertToNone fixed a bug with mozilla (bug
-           #799. So now it is RevertToNone again.
-        */
-        XSetInputFocus(ob_display, self->window, RevertToNone,
+        XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
                        event_curtime);
     }
 
@@ -2980,19 +3008,20 @@ gboolean client_focus(ObClient *self)
     return TRUE;
 }
 
-/* Used when the current client is closed, focus_last will then prevent
- * focus from going to the mouse pointer */
-void client_unfocus(ObClient *self)
+/* Used when the current client is closed or otherwise hidden, focus_last will
+   then prevent focus from going to the mouse pointer
+*/
+static void client_unfocus(ObClient *self)
 {
     if (focus_client == self) {
 #ifdef DEBUG_FOCUS
         ob_debug("client_unfocus for %lx\n", self->window);
 #endif
-        focus_fallback(OB_FOCUS_FALLBACK_CLOSED);
+        focus_fallback(FALSE);
     }
 }
 
-void client_activate(ObClient *self, gboolean here, gboolean user, Time time)
+void client_activate(ObClient *self, gboolean here, gboolean user)
 {
     /* XXX do some stuff here if user is false to determine if we really want
        to activate it or not (a parent or group member is currently
@@ -3000,9 +3029,9 @@ void client_activate(ObClient *self, gboolean here, gboolean user, Time time)
     */
     ob_debug("Want to activate window 0x%x with time %u (last time %u), "
              "source=%s\n",
-             self->window, time, client_last_user_time,
+             self->window, event_curtime, client_last_user_time,
              (user ? "user" : "application"));
-    if (!user && time && time < client_last_user_time)
+    if (!user && event_curtime && event_curtime < client_last_user_time)
         client_hilite(self, TRUE);
     else {
         if (client_normal(self) && screen_showing_desktop)
@@ -3242,7 +3271,14 @@ guint client_monitor(ObClient *self)
     return screen_find_monitor(&self->frame->area);
 }
 
-GSList *client_search_top_transients(ObClient *self)
+ObClient *client_search_top_parent(ObClient *self)
+{
+    while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
+        self = self->transient_for;
+    return self;
+}
+
+GSList *client_search_all_top_parents(ObClient *self)
 {
     GSList *ret = NULL;
 
This page took 0.052431 seconds and 4 git commands to generate.