Fix window width/height going less than 1 on some platforms (bug #5246)
[mikachu/openbox.git] / openbox / client.c
index 35afb31..f3b4bda 100644 (file)
@@ -110,7 +110,10 @@ static void client_ping_event(ObClient *self, gboolean dead);
 static void client_prompt_kill(ObClient *self);
 static gboolean client_can_steal_focus(ObClient *self,
                                        gboolean allow_other_desktop,
+                                       gboolean request_from_user,
                                        Time steal_time, Time launch_time);
+static void client_setup_default_decor_and_functions(ObClient *self);
+static void client_setup_decor_undecorated(ObClient *self);
 
 void client_startup(gboolean reconfig)
 {
@@ -237,6 +240,18 @@ void client_manage(Window window, ObPrompt *prompt)
        that needs to be freed with g_free(). */
     settings = client_get_settings_state(self);
 
+    /* the session should get the last say though */
+    client_restore_session_state(self);
+
+    /* the per-app settings/session may have changed the decorations for
+       the window, so we setup decorations for that here.  this is a special
+       case because we want to place the window according to these decoration
+       changes.
+       we do this before setting up the frame so that it will reflect the
+       decorations of the window as it will be placed on screen.
+    */
+    client_setup_decor_undecorated(self);
+
     /* specify that if we exit, the window should not be destroyed and
        should be reparented back to root automatically, unless we are managing
        an internal ObPrompt window  */
@@ -252,8 +267,18 @@ void client_manage(Window window, ObPrompt *prompt)
        time now */
     grab_server(FALSE);
 
-    /* the session should get the last say though */
-    client_restore_session_state(self);
+    /* this needs to occur once we have a frame, since it sets a property on
+       the frame */
+    client_update_opacity(self);
+
+    /* don't put helper/modal windows on a different desktop if they are
+       related to the focused window.  */
+    if (!screen_compare_desktops(self->desktop, screen_desktop) &&
+        focus_client && client_search_transient(focus_client, self)  &&
+        (client_helper(self) || self->modal))
+    {
+        self->desktop = screen_desktop;
+    }
 
     /* tell startup notification that this app started */
     launch_time = sn_app_started(self->startup_id, self->class, self->name);
@@ -302,8 +327,10 @@ void client_manage(Window window, ObPrompt *prompt)
     ob_debug("Going to try activate new window? %s",
              try_activate ? "yes" : "no");
     if (try_activate)
-        do_activate = client_can_steal_focus(self, settings->focus,
-                                             event_time(), launch_time);
+        do_activate = client_can_steal_focus(
+            self, settings->focus == 1,
+            (!!launch_time || settings->focus == 1),
+            event_time(), launch_time);
     else
         do_activate = FALSE;
 
@@ -434,6 +461,10 @@ void client_manage(Window window, ObPrompt *prompt)
     client_apply_startup_state(self, place.x, place.y,
                                place.width, place.height);
 
+    /* set the initial value of the desktop hint, when one wasn't requested
+       on map. */
+    OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
     /* grab mouse bindings before showing the window */
     mouse_grab_for_client(self, TRUE);
 
@@ -694,6 +725,7 @@ void client_fake_unmanage(ObClient *self)
 
 static gboolean client_can_steal_focus(ObClient *self,
                                        gboolean allow_other_desktop,
+                                       gboolean request_from_user,
                                        Time steal_time,
                                        Time launch_time)
 {
@@ -708,9 +740,14 @@ static gboolean client_can_steal_focus(ObClient *self,
 
     /* This is focus stealing prevention */
     ob_debug("Want to focus window 0x%x at time %u "
-             "launched at %u (last user interaction time %u)",
+             "launched at %u (last user interaction time %u) "
+             "request from %s, allow other desktop: %s, "
+             "desktop switch time %u",
              self->window, steal_time, launch_time,
-             event_last_user_time);
+             event_last_user_time,
+             (request_from_user ? "user" : "other"),
+             (allow_other_desktop ? "yes" : "no"),
+             screen_desktop_user_time);
 
     /*
       if no launch time is provided for an application, make one up.
@@ -739,37 +776,51 @@ static gboolean client_can_steal_focus(ObClient *self,
             if (event_last_user_time && client_search_focus_group_full(self)) {
                 /* our relative is focused */
                 launch_time = event_last_user_time;
-                ob_debug("Unknown launch time, using %u window in active "
+                ob_debug("Unknown launch time, using %u window in active "
                          "group", launch_time);
             }
-            else {
+            else if (!request_from_user) {
                 /* has relatives which are not being used. suspicious */
                 launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
-                ob_debug("Unknown launch time, using %u window in inactive "
+                ob_debug("Unknown launch time, using %u window in inactive "
                          "group", launch_time);
             }
+            else {
+                /* has relatives which are not being used, but the user seems
+                   to want to go there! */
+            launch_time = event_last_user_time;
+            ob_debug("Unknown launch time, using %u - user request",
+                     launch_time);
+            }
         }
         else {
             /* the window is on its own, probably the user knows it is going
                to appear */
             launch_time = event_last_user_time;
-            ob_debug("Unknown launch time, using %u for solo window",
+            ob_debug("Unknown launch time, using %u - independent window",
                      launch_time);
         }
     }
 
     /* if it's on another desktop
-       then if allow_other_desktop is false, we don't want to let it steal
-       focus, unless it was launched after we changed desktops
+       and if allow_other_desktop is true, we generally let it steal focus.
+       but if it didn't come from the user, don't let it steal unless it was
+       launched before the user switched desktops.
+       focus, unless it was launched after we changed desktops and the request
+       came from the user
      */
-    if (!(self->desktop == screen_desktop ||
-          self->desktop == DESKTOP_ALL) &&
-        (!allow_other_desktop ||
-         (screen_desktop_user_time &&
-          !event_time_after(launch_time, screen_desktop_user_time))))
-    {
-        steal = FALSE;
-        ob_debug("Not focusing the window because its on another desktop\n");
+    if (!screen_compare_desktops(screen_desktop, self->desktop)) {
+        /* must be allowed */
+        if (!allow_other_desktop) {
+            steal = FALSE;
+            ob_debug("Not focusing the window because its on another desktop");
+        }
+        /* if we don't know when the desktop changed, but request is from an
+           application, don't let it change desktop on you */
+        else if (!request_from_user) {
+            steal = FALSE;
+            ob_debug("Not focusing the window because non-user request");
+        }
     }
     /* If something is focused... */
     else if (focus_client) {
@@ -787,23 +838,6 @@ static gboolean client_can_steal_focus(ObClient *self,
             ob_debug("Not focusing the window because the user is "
                      "working in another window that is not its relative");
         }
-        /* If the new window is a transient (and its relatives aren't
-           focused) */
-        else if (client_has_parent(self) && !relative_focused) {
-            steal = FALSE;
-            ob_debug("Not focusing the window because it is a "
-                     "transient, and its relatives aren't focused");
-        }
-        /* Don't steal focus from globally active clients.
-           I stole this idea from KWin. It seems nice.
-        */
-        else if (!(focus_client->can_focus ||
-                   focus_client->focus_notify))
-        {
-            steal = FALSE;
-            ob_debug("Not focusing the window because a globally "
-                     "active client has focus");
-        }
         /* Don't move focus if it's not going to go to this window
            anyway */
         else if (client_focus_target(self) != self) {
@@ -811,15 +845,34 @@ static gboolean client_can_steal_focus(ObClient *self,
             ob_debug("Not focusing the window because another window "
                      "would get the focus anyway");
         }
-        /* Don't move focus if the window is not visible on the current
-           desktop and none of its relatives are focused */
-        else if (!(self->desktop == screen_desktop ||
-                   self->desktop == DESKTOP_ALL) &&
-                 !relative_focused)
-        {
-            steal = FALSE;
-            ob_debug("Not focusing the window because it is on "
-                     "another desktop and no relatives are focused ");
+        /* For requests that don't come from the user */
+        else if (!request_from_user) {
+            /* If the new window is a transient (and its relatives aren't
+               focused) */
+            if (client_has_parent(self) && !relative_focused) {
+                steal = FALSE;
+                ob_debug("Not focusing the window because it is a "
+                         "transient, and its relatives aren't focused");
+            }
+            /* Don't steal focus from globally active clients.
+               I stole this idea from KWin. It seems nice.
+            */
+            else if (!(focus_client->can_focus || focus_client->focus_notify))
+            {
+                steal = FALSE;
+                ob_debug("Not focusing the window because a globally "
+                         "active client has focus");
+            }
+            /* Don't move focus if the window is not visible on the current
+               desktop and none of its relatives are focused */
+            else if (!allow_other_desktop &&
+                     !screen_compare_desktops(self->desktop, screen_desktop) &&
+                     !relative_focused)
+            {
+                steal = FALSE;
+                ob_debug("Not focusing the window because it is on "
+                         "another desktop and no relatives are focused ");
+            }
         }
     }
 
@@ -827,6 +880,10 @@ static gboolean client_can_steal_focus(ObClient *self,
         ob_debug("Focus stealing prevention activated for %s at "
                  "time %u (last user interaction time %u)",
                  self->title, steal_time, event_last_user_time);
+    else
+        ob_debug("Allowing focus stealing for %s at time %u (last user "
+                 "interaction time %u)",
+                 self->title, steal_time, event_last_user_time);
     return steal;
 }
 
@@ -1131,11 +1188,8 @@ static void client_get_all(ObClient *self, gboolean real)
     client_get_type_and_transientness(self);
     client_update_normal_hints(self);
 
-    /* set up the decor/functions before getting the state.  the states may
-       affect which functions are available, but we want to know the maximum
-       decor/functions are available to this window, so we can then apply them
-       in client_apply_startup_state() */
-    client_setup_decor_and_functions(self, FALSE);
+    /* set up the maximum possible decor/functions */
+    client_setup_default_decor_and_functions(self);
 
     client_get_state(self);
 
@@ -1623,6 +1677,16 @@ void client_update_colormap(ObClient *self, Colormap colormap)
         self->colormap = colormap;
 }
 
+void client_update_opacity(ObClient *self)
+{
+    guint32 o;
+
+    if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o))
+        OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o);
+    else
+        OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
+}
+
 void client_update_normal_hints(ObClient *self)
 {
     XSizeHints size;
@@ -1679,7 +1743,7 @@ void client_update_normal_hints(ObClient *self)
         ob_debug("Normal hints: not set");
 }
 
-void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
+static void client_setup_default_decor_and_functions(ObClient *self)
 {
     /* start with everything (cept fullscreen) */
     self->decorations =
@@ -1812,6 +1876,23 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
     }
+}
+
+/*! Set up decor for a client based on its undecorated state. */
+static void client_setup_decor_undecorated(ObClient *self)
+{
+    /* If the user requested no decorations, then remove all the decorations,
+       except the border.  But don't add a border if there wasn't one. */
+    if (self->undecorated)
+        self->decorations &= (config_theme_keepborder ?
+                              OB_FRAME_DECOR_BORDER : 0);
+}
+
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
+{
+    client_setup_default_decor_and_functions(self);
+
+    client_setup_decor_undecorated(self);
 
     if (self->max_horz && self->max_vert) {
         /* once upon a time you couldn't resize maximized windows, that is not
@@ -1821,12 +1902,6 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
         self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
     }
 
-    /* finally, the user can have requested no decorations, which overrides
-       everything (but doesnt give it a border if it doesnt have one) */
-    if (self->undecorated)
-        self->decorations &= (config_theme_keepborder ?
-                              OB_FRAME_DECOR_BORDER : 0);
-
     /* if we don't have a titlebar, then we cannot shade! */
     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
         self->functions &= ~OB_CLIENT_FUNC_SHADE;
@@ -2758,6 +2833,9 @@ static void client_apply_startup_state(ObClient *self,
     if (fullscreen)
         client_fullscreen(self, TRUE);
 
+    /* make sure client_setup_decor_and_functions() is called at least once */
+    client_setup_decor_and_functions(self, FALSE);
+
     /* if the window hasn't been configured yet, then do so now, in fact the
        x,y,w,h may _not_ be the same as the area rect, which can end up
        meaning that the client isn't properly moved/resized by the fullscreen
@@ -2771,9 +2849,6 @@ static void client_apply_startup_state(ObClient *self,
     self->area = oldarea;
     client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
 
-    /* set the desktop hint, to make sure that it always exists */
-    OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
-
     /* nothing to do for the other states:
        skip_taskbar
        skip_pager
@@ -2853,6 +2928,14 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
        the updated frame dimensions. */
     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
 
+    /* cap any X windows at the size of an unsigned short */
+    *w = MIN(*w,
+             (gint)G_MAXUSHORT
+             - self->frame->size.left - self->frame->size.right);
+    *h = MIN(*h,
+             (gint)G_MAXUSHORT
+             - self->frame->size.top - self->frame->size.bottom);
+
     /* gets the frame's position */
     frame_client_gravity(self->frame, x, y);
 
@@ -3891,6 +3974,9 @@ gboolean client_focus(ObClient *self)
         return FALSE;
     }
 
+    /* if we have helper windows they should be there with the window */
+    client_bring_helper_windows(self);
+
     ob_debug_type(OB_DEBUG_FOCUS,
                   "Focusing client \"%s\" (0x%x) at time %u",
                   self->title, self->window, event_time());
@@ -3961,13 +4047,10 @@ void client_activate(ObClient *self, gboolean desktop,
                      gboolean here, gboolean raise,
                      gboolean unshade, gboolean user)
 {
-    if ((user && (desktop ||
-                  self->desktop == DESKTOP_ALL ||
-                  self->desktop == screen_desktop)) ||
-        client_can_steal_focus(self, desktop, event_time(), CurrentTime))
-    {
+    self = client_focus_target(self);
+
+    if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
         client_present(self, here, raise, unshade);
-    }
     else
         client_hilite(self, TRUE);
 }
@@ -3986,7 +4069,7 @@ static void client_bring_windows_recursive(ObClient *self,
 
     if (((helpers && client_helper(self)) ||
          (modals && self->modal)) &&
-        ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
+        (!screen_compare_desktops(self->desktop, desktop) ||
          (iconic && self->iconic)))
     {
         if (iconic && self->iconic)
@@ -4300,32 +4383,26 @@ void client_find_edge_directional(ObClient *self, ObDirection dir,
     }
 
     /* search for edges of clients */
-    if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) &&
-         !self->max_vert) ||
-        ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) &&
-         !self->max_horz))
-    {
-        for (it = client_list; it; it = g_list_next(it)) {
-            ObClient *cur = it->data;
+    for (it = client_list; it; it = g_list_next(it)) {
+        ObClient *cur = it->data;
 
-            /* skip windows to not bump into */
-            if (cur == self)
-                continue;
-            if (cur->iconic)
-                continue;
-            if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
-                cur->desktop != screen_desktop)
-                continue;
+        /* skip windows to not bump into */
+        if (cur == self)
+            continue;
+        if (cur->iconic)
+            continue;
+        if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+            cur->desktop != screen_desktop)
+            continue;
 
-            ob_debug("trying window %s", cur->title);
+        ob_debug("trying window %s", cur->title);
 
-            detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
-                        my_edge_size, dest, near_edge);
-        }
-        dock_get_area(&dock_area);
-        detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+        detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
                     my_edge_size, dest, near_edge);
     }
+    dock_get_area(&dock_area);
+    detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+                my_edge_size, dest, near_edge);
 
     g_slice_free(Rect, a);
 }