]> 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 08b09c93f68d4eddf26603187a6d2bba79894ec6..bfb9f790169f6e39eccd05e634f92d3de0b8544b 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
    
    client.c for the Openbox window manager
-   Copyright (c) 2004        Mikael Magnusson
+   Copyright (c) 2006        Mikael Magnusson
    Copyright (c) 2003        Ben Jansens
 
    This program is free software; you can redistribute it and/or modify
@@ -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)
 {
@@ -218,10 +219,11 @@ static ObAppSettings *get_settings(ObClient *client)
                 && !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)))
+            /* 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;
         }
 
@@ -245,7 +247,8 @@ void client_manage(Window window)
     /* 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)) {
+        XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
+    {
         XPutBackEvent(ob_display, &e);
 
         grab_server(FALSE);
@@ -254,7 +257,8 @@ void client_manage(Window window)
 
     /* make sure it isn't an override-redirect window */
     if (!XGetWindowAttributes(ob_display, window, &attrib) ||
-        attrib.override_redirect) {
+        attrib.override_redirect)
+    {
         grab_server(FALSE);
         return; /* don't manage it */
     }
@@ -262,7 +266,8 @@ void client_manage(Window window)
     /* is the window a docking app */
     if ((wmhint = XGetWMHints(ob_display, window))) {
         if ((wmhint->flags & StateHint) &&
-            wmhint->initial_state == WithdrawnState) {
+            wmhint->initial_state == WithdrawnState)
+        {
             dock_add(window, wmhint);
             grab_server(FALSE);
             XFree(wmhint);
@@ -295,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! */
@@ -311,7 +316,7 @@ void client_manage(Window window)
     XChangeSaveSet(ob_display, window, SetModeInsert);
 
     /* create the decoration frame for the client window */
-    self->frame = frame_new();
+    self->frame = frame_new(self);
 
     frame_grab_client(self->frame, self);
 
@@ -329,11 +334,11 @@ void client_manage(Window window)
         /* 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);
+            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);
+            client_iconify(self, !!settings->iconic, FALSE);
         if (settings->skip_pager != -1) {
             self->skip_pager = !!settings->skip_pager;
             client_change_state(self);
@@ -361,7 +366,7 @@ void client_manage(Window window)
 
         if (settings->desktop < screen_num_desktops
             || settings->desktop == DESKTOP_ALL)
-            client_set_desktop(self, settings->desktop, FALSE);
+            client_set_desktop(self, settings->desktop, TRUE);
 
         if (settings->layer > -2 && settings->layer < 2)
             client_set_layer(self, settings->layer);
@@ -370,14 +375,17 @@ void client_manage(Window window)
 
     /* focus the new window? */
     if (ob_state() != OB_STATE_STARTING &&
-        (config_focus_new || client_search_focus_parent(self)) ||
-        (settings && settings->focus == TRUE) &&
+        /* this means focus=true for window is same as config_focus_new=true */
+        ((config_focus_new || (settings && settings->focus == 1)) ||
+         client_search_focus_parent(self)) &&
+        /* this checks for focus=false for the window */
+        (!settings || settings->focus != 0) &&
         /* note the check against Type_Normal/Dialog, not client_normal(self),
            which would also include other types. in this case we want more
            strict rules for focus */
         (self->type == OB_CLIENT_TYPE_NORMAL ||
          self->type == OB_CLIENT_TYPE_DIALOG))
-    {        
+    {
         activate = TRUE;
 #if 0
         if (self->desktop != screen_desktop) {
@@ -415,8 +423,9 @@ void client_manage(Window window)
     if (ob_state() == OB_STATE_RUNNING) {
         gint x = self->area.x, ox = x;
         gint y = self->area.y, oy = y;
+        gboolean transient;
 
-        place_client(self, &x, &y, settings);
+        transient = place_client(self, &x, &y, settings);
 
         /* make sure the window is visible. */
         client_find_onscreen(self, &x, &y,
@@ -432,10 +441,11 @@ void client_manage(Window window)
                                 off-screen and on xinerama divides (ie,
                                 it is up to the placement routines to avoid
                                 the xinerama divides) */
-                             ((self->positioned & PPosition) &&
-                              !(self->positioned & USPosition)) &&
-                             client_normal(self) &&
-                             !self->session);
+                             transient ||
+                             (((self->positioned & PPosition) &&
+                               !(self->positioned & USPosition)) &&
+                              client_normal(self) &&
+                              !self->session));
         if (x != ox || y != oy)         
             client_move(self, x, y);
     }
@@ -451,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
@@ -472,7 +493,8 @@ void client_manage(Window window)
     g_hash_table_insert(window_map, &self->window, self);
 
     /* this has to happen after we're in the client_list */
-    screen_update_areas();
+    if (STRUT_EXISTS(self->strut))
+        screen_update_areas();
 
     /* update the list hints */
     client_set_list();
@@ -519,7 +541,8 @@ void client_unmanage(ObClient *self)
 
     /* once the client is out of the list, update the struts to remove it's
        influence */
-    screen_update_areas();
+    if (STRUT_EXISTS(self->strut))
+        screen_update_areas();
 
     for (it = client_destructors; it; it = g_slist_next(it)) {
         Destructor *d = it->data;
@@ -608,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;
@@ -694,17 +709,24 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
 
     /* 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 */
+       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.
+    */
     if (client_normal(self)) {
         a = screen_area(self->desktop);
-        if (!self->strut.right && *x >= a->x + a->width - 1)
-            *x = a->x + a->width - self->frame->area.width;
-        if (!self->strut.bottom && *y >= a->y + a->height - 1)
-            *y = a->y + a->height - self->frame->area.height;
-        if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
-            *x = a->x;
-        if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
-            *y = a->y;
+        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,
@@ -717,8 +739,8 @@ 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_physical_area_monitor(client_monitor(self));
-        /* dont let windows map/move into the strut unless they
+        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;
@@ -807,20 +829,22 @@ static void client_toggle_border(ObClient *self, gboolean show)
 static void client_get_all(ObClient *self)
 {
     client_get_area(self);
-    client_update_transient_for(self);
-    client_update_wmhints(self);
-    client_get_startup_id(self);
-    client_get_desktop(self);
-    client_get_shaped(self);
-
     client_get_mwm_hints(self);
-    client_get_type(self);/* this can change the mwmhints for special cases */
 
     /* The transient hint is used to pick a type, but the type can also affect
-       transiency (dialogs are always made transients). This is Havoc's idea,
-       but it is needed to make some apps work right (eg tsclient). */
+       transiency (dialogs are always made transients of their group if they
+       have one). This is Havoc's idea, but it is needed to make some apps
+       work right (eg tsclient). */
+    client_update_transient_for(self);
+    client_get_type(self);/* this can change the mwmhints for special cases */
     client_update_transient_for(self);
 
+    client_update_wmhints(self);
+    client_get_startup_id(self);
+    client_get_desktop(self);/* uses transient data/group/startup id if a
+                                desktop is not specified */
+    client_get_shaped(self);
+
     client_get_state(self);
 
     {
@@ -846,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)
@@ -941,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;
         }
@@ -1016,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 */
@@ -1028,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;
@@ -1300,9 +1335,15 @@ void client_setup_decor_and_functions(ObClient *self)
         if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
             if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
                    (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
+            {
                 /* if the mwm hints request no handle or title, then all
-                   decorations are disabled */
-                self->decorations = config_theme_keepborder ? OB_FRAME_DECOR_BORDER : 0;
+                   decorations are disabled, but keep the border if that's
+                   specified */
+                if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
+                    self->decorations = OB_FRAME_DECOR_BORDER;
+                else
+                    self->decorations = 0;
+            }
         }
     }
 
@@ -1433,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 */
@@ -1449,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;
 
@@ -1512,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)
@@ -1558,8 +1587,21 @@ void client_update_title(ObClient *self)
         for (it = client_list; it; it = g_list_next(it))
             if (it->data != self) {
                 ObClient *c = it->data;
-                if (0 == strncmp(c->title, data, strlen(data)))
-                    nums |= 1 << c->title_count;
+
+                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)
@@ -1605,12 +1647,10 @@ no_number:
      * 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 *vdata, *ndata;
-        ndata = g_strdup_printf(" - [%u]", self->title_count);
-        vdata = g_strconcat(data, ndata, NULL);
-        g_free(ndata);
+        gchar *newdata;
+        newdata = g_strdup_printf("%s - [%u]", data, self->title_count);
         g_free(data);
-        data = vdata;
+        data = newdata;
     }
 
     PROP_SETS(self->window, net_wm_visible_icon_name, data);
@@ -1749,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;
@@ -1795,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];
@@ -1826,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);
@@ -1996,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;
@@ -2342,7 +2389,8 @@ static void client_iconify_recursive(ObClient *self,
     if (changed) {
         client_change_state(self);
         client_showhide(self);
-        screen_update_areas();
+        if (STRUT_EXISTS(self->strut))
+            screen_update_areas();
     }
 
     /* iconify all transients */
@@ -2490,7 +2538,7 @@ void client_close(ObClient *self)
     ce.xclient.window = self->window;
     ce.xclient.format = 32;
     ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
-    ce.xclient.data.l[1] = event_lasttime;
+    ce.xclient.data.l[1] = event_curtime;
     ce.xclient.data.l[2] = 0l;
     ce.xclient.data.l[3] = 0l;
     ce.xclient.data.l[4] = 0l;
@@ -2502,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)
 {
@@ -2528,7 +2587,8 @@ void client_set_desktop_recursive(ObClient *self,
         /* raise if it was not already on the desktop */
         if (old != DESKTOP_ALL)
             client_raise(self);
-        screen_update_areas();
+        if (STRUT_EXISTS(self->strut))
+            screen_update_areas();
 
         /* add to the new desktop(s) */
         if (config_focus_new)
@@ -2600,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 ||
@@ -2649,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;
@@ -2677,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;
             }
@@ -2702,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;
             }
@@ -2741,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 */
 }
@@ -2811,7 +2883,7 @@ gboolean client_focus(ObClient *self)
            #799. So now it is RevertToNone again.
         */
         XSetInputFocus(ob_display, self->window, RevertToNone,
-                       event_lasttime);
+                       event_curtime);
     }
 
     if (self->focus_notify) {
@@ -2822,7 +2894,7 @@ gboolean client_focus(ObClient *self)
         ce.xclient.window = self->window;
         ce.xclient.format = 32;
         ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
-        ce.xclient.data.l[1] = event_lasttime;
+        ce.xclient.data.l[1] = event_curtime;
         ce.xclient.data.l[2] = 0l;
         ce.xclient.data.l[3] = 0l;
         ce.xclient.data.l[4] = 0l;
@@ -2832,7 +2904,7 @@ gboolean client_focus(ObClient *self)
 #ifdef DEBUG_FOCUS
     ob_debug("%sively focusing %lx at %d\n",
              (self->can_focus ? "act" : "pass"),
-             self->window, (gint) event_lasttime);
+             self->window, (gint) event_curtime);
 #endif
 
     /* Cause the FocusIn to come back to us. Important for desktop switches,
@@ -2854,11 +2926,11 @@ void client_unfocus(ObClient *self)
     }
 }
 
-void client_activate(ObClient *self, gboolean here)
+void client_activate(ObClient *self, gboolean here, gboolean user)
 {
-    /* This check is for the client_list_menu trying to activate
-     * a closed client. */
-    if (!g_list_find(client_list, self)) return;
+    /* 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 active) */
+
     if (client_normal(self) && screen_showing_desktop)
         screen_show_desktop(FALSE);
     if (self->iconic)
@@ -2993,8 +3065,8 @@ ObClient *client_find_directional(ObClient *c, ObDirection dir)
             continue;
         if(cur->iconic)
             continue;
-        if(client_focus_target(cur) == cur &&
-           !(cur->can_focus || cur->focus_notify))
+        if(!(client_focus_target(cur) == cur &&
+             client_can_focus(cur)))
             continue;
 
         /* find the centre coords of this window, from the
@@ -3213,11 +3285,30 @@ void client_update_sm_client_id(ObClient *self)
                   &self->sm_client_id);
 }
 
+#define WANT_EDGE(cur, c) \
+            if(cur == c)                                                      \
+                continue;                                                     \
+            if(!client_normal(cur))                                   \
+                continue;                                                     \
+            if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
+                continue;                                                     \
+            if(cur->iconic)                                                   \
+                continue;                                                     \
+            if(cur->layer < c->layer && !config_resist_layers_below)          \
+                continue;
+
+#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
+            if ((his_edge_start >= my_edge_start && \
+                 his_edge_start <= my_edge_end) ||  \
+                (my_edge_start >= his_edge_start && \
+                 my_edge_start <= his_edge_end))    \
+                dest = his_offset;
+
 /* finds the nearest edge in the given direction from the current client
  * note to self: the edge is the -frame- edge (the actual one), not the
  * client edge.
  */
-gint client_directional_edge_search(ObClient *c, ObDirection dir)
+gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
 {
     gint dest, monitor_dest;
     gint my_edge_start, my_edge_end, my_offset;
@@ -3234,11 +3325,11 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir)
     case OB_DIRECTION_NORTH:
         my_edge_start = c->frame->area.x;
         my_edge_end = c->frame->area.x + c->frame->area.width;
-        my_offset = c->frame->area.y;
+        my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
         
         /* default: top of screen */
-        dest = a->y;
-        monitor_dest = monitor->y;
+        dest = a->y + (hang ? c->frame->area.height : 0);
+        monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
         /* if the monitor edge comes before the screen edge, */
         /* use that as the destination instead. (For xinerama) */
         if (monitor_dest != dest && my_offset > monitor_dest)
@@ -3248,45 +3339,31 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir)
             gint his_edge_start, his_edge_end, his_offset;
             ObClient *cur = it->data;
 
-            if(cur == c)
-                continue;
-            if(!client_normal(cur))
-                continue;
-            if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
-                continue;
-            if(cur->iconic)
-                continue;
-            if(cur->layer < c->layer && !config_resist_layers_below)
-                continue;
+            WANT_EDGE(cur, c)
 
             his_edge_start = cur->frame->area.x;
             his_edge_end = cur->frame->area.x + cur->frame->area.width;
-            his_offset = cur->frame->area.y + cur->frame->area.height;
+            his_offset = cur->frame->area.y + 
+                         (hang ? 0 : cur->frame->area.height);
 
             if(his_offset + 1 > my_offset)
                 continue;
 
             if(his_offset < dest)
                 continue;
-            
-            if(his_edge_start >= my_edge_start &&
-               his_edge_start <= my_edge_end)
-                dest = his_offset;
-
-            if(my_edge_start >= his_edge_start &&
-               my_edge_start <= his_edge_end)
-                dest = his_offset;
 
+            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
         }
         break;
     case OB_DIRECTION_SOUTH:
         my_edge_start = c->frame->area.x;
         my_edge_end = c->frame->area.x + c->frame->area.width;
-        my_offset = c->frame->area.y + c->frame->area.height;
+        my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
 
         /* default: bottom of screen */
-        dest = a->y + a->height;
-        monitor_dest = monitor->y + monitor->height;
+        dest = a->y + a->height - (hang ? c->frame->area.height : 0);
+        monitor_dest = monitor->y + monitor->height -
+                       (hang ? c->frame->area.height : 0);
         /* if the monitor edge comes before the screen edge, */
         /* use that as the destination instead. (For xinerama) */
         if (monitor_dest != dest && my_offset < monitor_dest)
@@ -3296,20 +3373,12 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir)
             gint his_edge_start, his_edge_end, his_offset;
             ObClient *cur = it->data;
 
-            if(cur == c)
-                continue;
-            if(!client_normal(cur))
-                continue;
-            if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
-                continue;
-            if(cur->iconic)
-                continue;
-            if(cur->layer < c->layer && !config_resist_layers_below)
-                continue;
+            WANT_EDGE(cur, c)
 
             his_edge_start = cur->frame->area.x;
             his_edge_end = cur->frame->area.x + cur->frame->area.width;
-            his_offset = cur->frame->area.y;
+            his_offset = cur->frame->area.y +
+                         (hang ? cur->frame->area.height : 0);
 
 
             if(his_offset - 1 < my_offset)
@@ -3317,25 +3386,18 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir)
             
             if(his_offset > dest)
                 continue;
-            
-            if(his_edge_start >= my_edge_start &&
-               his_edge_start <= my_edge_end)
-                dest = his_offset;
-
-            if(my_edge_start >= his_edge_start &&
-               my_edge_start <= his_edge_end)
-                dest = his_offset;
 
+            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
         }
         break;
     case OB_DIRECTION_WEST:
         my_edge_start = c->frame->area.y;
         my_edge_end = c->frame->area.y + c->frame->area.height;
-        my_offset = c->frame->area.x;
+        my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
 
         /* default: leftmost egde of screen */
-        dest = a->x;
-        monitor_dest = monitor->x;
+        dest = a->x + (hang ? c->frame->area.width : 0);
+        monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
         /* if the monitor edge comes before the screen edge, */
         /* use that as the destination instead. (For xinerama) */
         if (monitor_dest != dest && my_offset > monitor_dest)
@@ -3345,46 +3407,31 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir)
             gint his_edge_start, his_edge_end, his_offset;
             ObClient *cur = it->data;
 
-            if(cur == c)
-                continue;
-            if(!client_normal(cur))
-                continue;
-            if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
-                continue;
-            if(cur->iconic)
-                continue;
-            if(cur->layer < c->layer && !config_resist_layers_below)
-                continue;
+            WANT_EDGE(cur, c)
 
             his_edge_start = cur->frame->area.y;
             his_edge_end = cur->frame->area.y + cur->frame->area.height;
-            his_offset = cur->frame->area.x + cur->frame->area.width;
+            his_offset = cur->frame->area.x +
+                         (hang ? 0 : cur->frame->area.width);
 
             if(his_offset + 1 > my_offset)
                 continue;
-            
+
             if(his_offset < dest)
                 continue;
-            
-            if(his_edge_start >= my_edge_start &&
-               his_edge_start <= my_edge_end)
-                dest = his_offset;
-
-            if(my_edge_start >= his_edge_start &&
-               my_edge_start <= his_edge_end)
-                dest = his_offset;
-                
 
+            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
         }
-        break;
+       break;
     case OB_DIRECTION_EAST:
         my_edge_start = c->frame->area.y;
         my_edge_end = c->frame->area.y + c->frame->area.height;
-        my_offset = c->frame->area.x + c->frame->area.width;
+        my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
         
         /* default: rightmost edge of screen */
-        dest = a->x + a->width;
-        monitor_dest = monitor->x + monitor->width;
+        dest = a->x + a->width - (hang ? c->frame->area.width : 0);
+        monitor_dest = monitor->x + monitor->width -
+                       (hang ? c->frame->area.width : 0);
         /* if the monitor edge comes before the screen edge, */
         /* use that as the destination instead. (For xinerama) */
         if (monitor_dest != dest && my_offset < monitor_dest)
@@ -3394,35 +3441,20 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir)
             gint his_edge_start, his_edge_end, his_offset;
             ObClient *cur = it->data;
 
-            if(cur == c)
-                continue;
-            if(!client_normal(cur))
-                continue;
-            if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
-                continue;
-            if(cur->iconic)
-                continue;
-            if(cur->layer < c->layer && !config_resist_layers_below)
-                continue;
+            WANT_EDGE(cur, c)
 
             his_edge_start = cur->frame->area.y;
             his_edge_end = cur->frame->area.y + cur->frame->area.height;
-            his_offset = cur->frame->area.x;
+            his_offset = cur->frame->area.x +
+                         (hang ? cur->frame->area.width : 0);
 
             if(his_offset - 1 < my_offset)
                 continue;
             
             if(his_offset > dest)
                 continue;
-            
-            if(his_edge_start >= my_edge_start &&
-               his_edge_start <= my_edge_end)
-                dest = his_offset;
-
-            if(my_edge_start >= his_edge_start &&
-               my_edge_start <= his_edge_end)
-                dest = his_offset;
 
+            HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
         }
         break;
     case OB_DIRECTION_NORTHEAST:
This page took 0.049258 seconds and 4 git commands to generate.