]> Dogcows Code - chaz/openbox/blobdiff - openbox/client.c
add the _NET_WM_USER_TIME property support. When focus_new is enabled, don't focus...
[chaz/openbox] / openbox / client.c
index bf5c8bec06f1aed275910664657e82b6c443390a..bfb9f790169f6e39eccd05e634f92d3de0b8544b 100644 (file)
@@ -57,8 +57,10 @@ typedef struct
     gpointer data;
 } Destructor;
 
-GList      *client_list        = NULL;
-GSList     *client_destructors = NULL;
+GList         *client_list           = NULL;
+
+static GSList *client_destructors    = NULL;
+static Time    client_last_user_time = CurrentTime;
 
 static void client_get_all(ObClient *self);
 static void client_toggle_border(ObClient *self, gboolean show);
@@ -75,7 +77,6 @@ static void client_change_state(ObClient *self);
 static void client_apply_startup_state(ObClient *self);
 static void client_restore_session_state(ObClient *self);
 static void client_restore_session_stacking(ObClient *self);
-static void client_urgent_notify(ObClient *self);
 
 void client_startup(gboolean reconfig)
 {
@@ -299,7 +300,7 @@ void client_manage(Window window)
     client_get_all(self);
     client_restore_session_state(self);
 
-    sn_app_started(self->class);
+    self->user_time = sn_app_started(self->startup_id, self->class);
 
     /* update the focus lists, do this before the call to change_state or
        it can end up in the list twice! */
@@ -460,15 +461,26 @@ void client_manage(Window window)
        clicking a window to activate is. so keep the new window out of the way
        but do focus it. */
     if (activate) {
-        /* if using focus_delay, stop the timer now so that focus doesn't go
-           moving on us */
-        event_halt_focus_delay();
-
-        client_focus(self);
-        /* since focus can change the stacking orders, if we focus the window
-           then the standard raise it gets is not enough, we need to queue one
-           for after the focus change takes place */
-        client_raise(self);
+        /* This is focus stealing prevention, if a user_time has been set */
+        if (self->user_time == CurrentTime ||
+            self->user_time > client_last_user_time)
+        {
+            /* if using focus_delay, stop the timer now so that focus doesn't
+               go moving on us */
+            event_halt_focus_delay();
+
+            client_focus(self);
+            /* since focus can change the stacking orders, if we focus the
+               window then the standard raise it gets is not enough, we need
+               to queue one for after the focus change takes place */
+            client_raise(self);
+        } else {
+            ob_debug("Focus stealing prevention activated for %s\n",
+                     self->title);
+            /* if the client isn't focused, then hilite it so the user
+               knows it is there */
+            client_hilite(self, TRUE);
+        }
     }
 
     /* client_activate does this but we aret using it so we have to do it
@@ -619,14 +631,6 @@ void client_unmanage(ObClient *self)
         grab_pointer(FALSE, OB_CURSOR_NONE);
 }
 
-static void client_urgent_notify(ObClient *self)
-{
-    if (self->urgent)
-        frame_flash_start(self->frame);
-    else
-        frame_flash_stop(self->frame);
-}
-
 static void client_restore_session_state(ObClient *self)
 {
     GList *it;
@@ -735,7 +739,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
         /* 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(self->desktop);
+        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) {
@@ -866,6 +870,7 @@ static void client_get_all(ObClient *self)
     client_update_sm_client_id(self);
     client_update_strut(self);
     client_update_icons(self);
+    client_update_user_time(self, FALSE);
 }
 
 static void client_get_startup_id(ObClient *self)
@@ -961,6 +966,8 @@ static void client_get_state(ObClient *self)
                 self->above = TRUE;
             else if (state[i] == prop_atoms.net_wm_state_below)
                 self->below = TRUE;
+            else if (state[i] == prop_atoms.net_wm_state_demands_attention)
+                self->demands_attention = TRUE;
             else if (state[i] == prop_atoms.ob_wm_state_undecorated)
                 self->undecorated = TRUE;
         }
@@ -1036,7 +1043,13 @@ void client_update_transient_for(ObClient *self)
                    a dockapp, for example */
                 target = NULL;
             }
-            
+
+#if 0 
+/* we used to do this, but it violates the ICCCM and causes problems because
+   toolkits seem to set transient_for = root rather arbitrarily (eg kicker's
+   config dialogs), so it is being removed. the ewmh provides other ways to
+   make things transient for their group. -dana
+*/
             if (!target && self->group) {
                 /* not transient to a client, see if it is transient for a
                    group */
@@ -1048,6 +1061,8 @@ void client_update_transient_for(ObClient *self)
                     target = OB_TRAN_GROUP;
                 }
             }
+#endif
+
         }
     } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
         self->transient = TRUE;
@@ -1459,7 +1474,6 @@ void client_reconfigure(ObClient *self)
 void client_update_wmhints(ObClient *self)
 {
     XWMHints *hints;
-    gboolean ur = FALSE;
     GSList *it;
 
     /* assume a window takes input if it doesnt specify */
@@ -1475,9 +1489,6 @@ void client_update_wmhints(ObClient *self)
             if (hints->flags & StateHint)
                 self->iconic = hints->initial_state == IconicState;
 
-        if (hints->flags & XUrgencyHint)
-            ur = TRUE;
-
         if (!(hints->flags & WindowGroupHint))
             hints->window_group = None;
 
@@ -1538,14 +1549,6 @@ void client_update_wmhints(ObClient *self)
 
         XFree(hints);
     }
-
-    if (ur != self->urgent) {
-        self->urgent = ur;
-        /* fire the urgent callback if we're mapped, otherwise, wait until
-           after we're mapped */
-        if (self->frame)
-            client_urgent_notify(self);
-    }
 }
 
 void client_update_title(ObClient *self)
@@ -1786,23 +1789,6 @@ void client_update_icons(ObClient *self)
             g_assert(i <= num);
         }
 
-        g_free(data);
-    } else if (PROP_GETA32(self->window, kwm_win_icon,
-                           kwm_win_icon, &data, &num)) {
-        if (num == 2) {
-            self->nicons++;
-            self->icons = g_new(ObClientIcon, self->nicons);
-            xerror_set_ignore(TRUE);
-            if (!RrPixmapToRGBA(ob_rr_inst,
-                                data[0], data[1],
-                                &self->icons[self->nicons-1].width,
-                                &self->icons[self->nicons-1].height,
-                                &self->icons[self->nicons-1].data)) {
-                g_free(&self->icons[self->nicons-1]);
-                self->nicons--;
-            }
-            xerror_set_ignore(FALSE);
-        }
         g_free(data);
     } else {
         XWMHints *hints;
@@ -1832,6 +1818,26 @@ void client_update_icons(ObClient *self)
         frame_adjust_icon(self->frame);
 }
 
+void client_update_user_time(ObClient *self, gboolean new_event)
+{
+    guint32 time;
+
+    if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) {
+        self->user_time = time;
+        /* we set this every time, not just when it grows, because in practice
+           sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
+           backward we don't want all windows to stop focusing. we'll just
+           assume noone is setting times older than the last one, cuz that
+           would be pretty stupid anyways
+           However! This is called when a window is mapped to get its user time
+           but it's an old number, it's not changing it from new user
+           interaction, so in that case, don't change the last user time.
+        */
+        if (new_event)
+            client_last_user_time = time;
+    }
+}
+
 static void client_change_state(ObClient *self)
 {
     gulong state[2];
@@ -1863,6 +1869,8 @@ static void client_change_state(ObClient *self)
         netstate[num++] = prop_atoms.net_wm_state_above;
     if (self->below)
         netstate[num++] = prop_atoms.net_wm_state_below;
+    if (self->demands_attention)
+        netstate[num++] = prop_atoms.net_wm_state_demands_attention;
     if (self->undecorated)
         netstate[num++] = prop_atoms.ob_wm_state_undecorated;
     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
@@ -2033,8 +2041,10 @@ static void client_apply_startup_state(ObClient *self)
         self->shaded = FALSE;
         client_shade(self, TRUE);
     }
-    if (self->urgent)
-        client_urgent_notify(self);
+    if (self->demands_attention) {
+        self->demands_attention = FALSE;
+        client_hilite(self, TRUE);
+    }
   
     if (self->max_vert && self->max_horz) {
         self->max_vert = self->max_horz = FALSE;
@@ -2540,6 +2550,17 @@ void client_kill(ObClient *self)
     XKillClient(ob_display, self->window);
 }
 
+void client_hilite(ObClient *self, gboolean hilite)
+{
+    /* don't allow focused windows to hilite */
+    self->demands_attention = hilite && !client_focused(self);
+    if (self->demands_attention)
+        frame_flash_start(self->frame);
+    else
+        frame_flash_stop(self->frame);
+    client_change_state(self);
+}
+
 void client_set_desktop_recursive(ObClient *self,
                                   guint target, gboolean donthide)
 {
@@ -2639,6 +2660,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
     gboolean max_vert = self->max_vert;
     gboolean modal = self->modal;
     gboolean iconic = self->iconic;
+    gboolean demands_attention = self->demands_attention;
     gint i;
 
     if (!(action == prop_atoms.net_wm_state_add ||
@@ -2688,6 +2710,10 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
             else if (state == prop_atoms.net_wm_state_below)
                 action = self->below ? prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
+            else if (state == prop_atoms.net_wm_state_demands_attention)
+                action = self->demands_attention ?
+                    prop_atoms.net_wm_state_remove :
+                    prop_atoms.net_wm_state_add;
             else if (state == prop_atoms.ob_wm_state_undecorated)
                 action = undecorated ? prop_atoms.net_wm_state_remove :
                     prop_atoms.net_wm_state_add;
@@ -2716,6 +2742,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
             } else if (state == prop_atoms.net_wm_state_below) {
                 self->above = FALSE;
                 self->below = TRUE;
+            } else if (state == prop_atoms.net_wm_state_demands_attention) {
+                demands_attention = TRUE;
             } else if (state == prop_atoms.ob_wm_state_undecorated) {
                 undecorated = TRUE;
             }
@@ -2741,6 +2769,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
                 self->above = FALSE;
             } else if (state == prop_atoms.net_wm_state_below) {
                 self->below = FALSE;
+            } else if (state == prop_atoms.net_wm_state_demands_attention) {
+                demands_attention = FALSE;
             } else if (state == prop_atoms.ob_wm_state_undecorated) {
                 undecorated = FALSE;
             }
@@ -2780,6 +2810,9 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
     if (iconic != self->iconic)
         client_iconify(self, iconic, FALSE);
 
+    if (demands_attention != self->demands_attention)
+        client_hilite(self, demands_attention);
+
     client_calc_layer(self);
     client_change_state(self); /* change the hint to reflect these changes */
 }
This page took 0.027092 seconds and 4 git commands to generate.