X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=c59baf96905d7d04036ed6b632b7b98b91ba0b7f;hb=bf247215bb015dbb4dfa39c38bf020aa815cd306;hp=7efc0e9418505b94a4cc231105bb9764a459e003;hpb=c234ed9ecd9d5228744aae018768ff0c04f2d217;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 7efc0e94..c59baf96 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,8 +1,8 @@ /* -*- 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) 2003 Ben Jansens + Copyright (c) 2006 Mikael Magnusson + 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 @@ -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); @@ -66,6 +68,7 @@ static void client_get_startup_id(ObClient *self); static void client_get_area(ObClient *self); static void client_get_desktop(ObClient *self); static void client_get_state(ObClient *self); +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); @@ -75,7 +78,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 +220,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 +248,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 +258,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 +267,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); @@ -291,11 +297,17 @@ void client_manage(Window window) self->wmstate = NormalState; 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); - sn_app_started(self->class); + client_calc_layer(self); + + { + Time t = sn_app_started(self->startup_id, self->class); + if (t) self->user_time = t; + } /* update the focus lists, do this before the call to change_state or it can end up in the list twice! */ @@ -311,7 +323,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); @@ -322,18 +334,18 @@ void client_manage(Window window) /* get and set application level settings */ settings = get_settings(self); - stacking_add(CLIENT_AS_WINDOW(self)); + 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); + 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); @@ -370,14 +382,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) { @@ -445,23 +460,69 @@ void client_manage(Window window) keyboard_grab_for_client(self, TRUE); mouse_grab_for_client(self, TRUE); + if (activate) { + /* This is focus stealing prevention */ + ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n", + self->window, self->user_time, client_last_user_time); + + /* If a nothing at all, or a parent was focused, then focus this + always + */ + if (!focus_client || client_search_focus_parent(self) != NULL) + activate = TRUE; + else + { + /* If time stamp is old, don't steal focus */ + if (self->user_time && self->user_time < client_last_user_time) + activate = FALSE; + /* 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) + activate = FALSE; + } + + if (activate) + { + /* 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 with time %u " + "(last time %u)\n", + self->title, self->user_time, client_last_user_time); + /* if the client isn't focused, then hilite it so the user + knows it is there */ + client_hilite(self, TRUE); + } + } + else { + /* This may look rather odd. Well it's because new windows are added + to the stacking order non-intrusively. If we're not going to focus + the new window or hilite it, then we raise it to the top. This will + take affect for things that don't get focused like splash screens. + Also if you don't have focus_new enabled, then it's going to get + raised to the top. Legacy begets legacy I guess? + */ + client_raise(self); + } + + /* this has to happen before we try focus the window, but we want it to + happen after the client's stacking has been determined or it looks bad + */ client_showhide(self); /* use client_focus instead of client_activate cuz client_activate does stuff like switch desktops etc and I'm not interested in all that when a window maps since its not based on an action from the user like - clicking a window to activate is. so keep the new window out of the way + clicking a window to activate it. 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 */ + /* 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); } /* client_activate does this but we aret using it so we have to do it @@ -474,7 +535,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(); @@ -521,7 +583,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; @@ -610,14 +673,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; @@ -696,17 +751,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, @@ -719,8 +781,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; @@ -809,21 +871,25 @@ 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_get_state(self); + 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_layer(self); /* if layer hasn't been specified, get it from + other sources if possible */ { /* a couple type-based defaults for new windows */ @@ -848,6 +914,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) @@ -915,6 +982,41 @@ static void client_get_desktop(ObClient *self) } } +static void client_get_layer(ObClient *self) +{ + if (!(self->above || self->below)) { + if (self->group) { + /* apply stuff from the group */ + GSList *it; + gint layer = -2; + + for (it = self->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if (c != self && !client_search_transient(self, c) && + client_normal(self) && client_normal(c)) + { + layer = MAX(layer, + (c->above ? 1 : (c->below ? -1 : 0))); + } + } + switch (layer) { + case -1: + self->below = TRUE; + break; + case -2: + case 0: + break; + case 1: + self->above = TRUE; + break; + default: + g_assert_not_reached(); + break; + } + } + } +} + static void client_get_state(ObClient *self) { guint32 *state; @@ -943,44 +1045,14 @@ 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; } g_free(state); } - - if (!(self->above || self->below)) { - if (self->group) { - /* apply stuff from the group */ - GSList *it; - gint layer = -2; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && !client_search_transient(self, c) && - client_normal(self) && client_normal(c)) - { - layer = MAX(layer, - (c->above ? 1 : (c->below ? -1 : 0))); - } - } - switch (layer) { - case -1: - self->below = TRUE; - break; - case -2: - case 0: - break; - case 1: - self->above = TRUE; - break; - default: - g_assert_not_reached(); - break; - } - } - } } static void client_get_shaped(ObClient *self) @@ -1018,14 +1090,28 @@ void client_update_transient_for(ObClient *self) a dockapp, for example */ target = NULL; } - + + /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat.. + + Setting the transient_for to Root is actually illegal, however + applications from time have done this to specify transient for + their group. + + Now you can do that by being a TYPE_DIALOG and not setting + the transient_for hint at all on your window. But people still + use Root, and Kwin is very strange in this regard. + + KWin 3.0 will not consider windows with transient_for set to + Root as transient for their group *UNLESS* they are also modal. + In that case, it will make them transient for the group. This + leads to all sorts of weird behavior from KDE apps which are + only tested in KWin. I'd like to follow their behavior just to + make this work right with KDE stuff, but that seems wrong. + */ if (!target && self->group) { /* not transient to a client, see if it is transient for a group */ - if (t == self->group->leader || - t == None || - t == RootWindow(ob_display, ob_screen)) - { + if (t == RootWindow(ob_display, ob_screen)) { /* window is a transient for its group! */ target = OB_TRAN_GROUP; } @@ -1302,9 +1388,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; + } } } @@ -1435,7 +1527,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 */ @@ -1451,9 +1542,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; @@ -1514,14 +1602,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) @@ -1560,8 +1640,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) @@ -1607,12 +1700,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); @@ -1751,23 +1842,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; @@ -1797,6 +1871,28 @@ 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; + + /*ob_debug("window 0x%x user time %u\n", self->window, time);*/ + } +} + static void client_change_state(ObClient *self) { gulong state[2]; @@ -1828,12 +1924,12 @@ 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); - client_calc_layer(self); - if (self->frame) frame_adjust_state(self->frame); } @@ -1899,20 +1995,21 @@ static ObStackingLayer calc_layer(ObClient *self) } static void client_calc_layer_recursive(ObClient *self, ObClient *orig, - ObStackingLayer l, gboolean raised) + ObStackingLayer min, gboolean raised) { ObStackingLayer old, own; GSList *it; old = self->layer; own = calc_layer(self); - self->layer = l > own ? l : own; + self->layer = MAX(own, min); for (it = self->transients; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, - l, raised ? raised : l != old); + self->layer, + raised ? raised : self->layer != old); - if (!raised && l != old) + if (!raised && self->layer != old) if (orig->frame) { /* only restack if the original window is managed */ stacking_remove(CLIENT_AS_WINDOW(self)); stacking_add(CLIENT_AS_WINDOW(self)); @@ -1921,17 +2018,16 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig, void client_calc_layer(ObClient *self) { - ObStackingLayer l; ObClient *orig; + GSList *it; orig = self; /* transients take on the layer of their parents */ - self = client_search_top_transient(self); - - l = calc_layer(self); + it = client_search_top_transients(self); - client_calc_layer_recursive(self, orig, l, FALSE); + for (; it; it = g_slist_next(it)) + client_calc_layer_recursive(it->data, orig, 0, FALSE); } gboolean client_should_show(ObClient *self) @@ -1998,8 +2094,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; @@ -2031,6 +2129,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, gboolean moved = FALSE, resized = FALSE; guint fdecor = self->frame->decorations; gboolean fhorz = self->frame->max_horz; + 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 @@ -2047,7 +2146,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, Rect *a; guint i; - i = client_monitor(self); + i = screen_find_monitor(&desired_area); a = screen_physical_area_monitor(i); x = a->x; @@ -2058,8 +2157,10 @@ void client_configure_full(ObClient *self, ObCorner anchor, user = FALSE; /* ignore that increment etc shit when in fullscreen */ } else { Rect *a; + guint i; - a = screen_area_monitor(self->desktop, client_monitor(self)); + i = screen_find_monitor(&desired_area); + a = screen_area_monitor(self->desktop, i); /* set the size and position if maximized */ if (self->max_horz) { @@ -2252,8 +2353,8 @@ void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea) self->fullscreen == fs) return; /* already done */ self->fullscreen = fs; - client_change_state(self); /* change the state hints on the client, - and adjust out layer/stacking */ + client_change_state(self); /* change the state hints on the client */ + client_calc_layer(self); /* and adjust out layer/stacking */ if (fs) { if (savearea) @@ -2344,7 +2445,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 */ @@ -2355,11 +2457,13 @@ static void client_iconify_recursive(ObClient *self, void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) { + GSList *it; + /* move up the transient chain as far as possible first */ - self = client_search_top_transient(self); + it = client_search_top_transients(self); - client_iconify_recursive(client_search_top_transient(self), - iconic, curdesk); + for (; it; it = g_slist_next(it)) + client_iconify_recursive(it->data, iconic, curdesk); } void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea) @@ -2492,7 +2596,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; @@ -2504,6 +2608,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) { @@ -2530,7 +2645,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) @@ -2547,8 +2663,12 @@ void client_set_desktop_recursive(ObClient *self, void client_set_desktop(ObClient *self, guint target, gboolean donthide) { - client_set_desktop_recursive(client_search_top_transient(self), - target, donthide); + GSList *it; + + it = client_search_top_transients(self); + + for(; it; it = g_slist_next(it)) + client_set_desktop_recursive(it->data, target, donthide); } ObClient *client_search_modal_child(ObClient *self) @@ -2602,6 +2722,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 || @@ -2651,6 +2772,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; @@ -2679,6 +2804,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; } @@ -2704,6 +2831,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; } @@ -2743,16 +2872,17 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) if (iconic != self->iconic) client_iconify(self, iconic, FALSE); - client_calc_layer(self); + if (demands_attention != self->demands_attention) + client_hilite(self, demands_attention); + client_change_state(self); /* change the hint to reflect these changes */ } ObClient *client_focus_target(ObClient *self) { - ObClient *child; - - /* if we have a modal child, then focus it, not us */ - child = client_search_modal_child(client_search_top_transient(self)); + ObClient *child = NULL; + + child = client_search_modal_child(self); if (child) return child; return self; } @@ -2813,7 +2943,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) { @@ -2824,7 +2954,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; @@ -2834,7 +2964,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, @@ -2856,43 +2986,55 @@ void client_unfocus(ObClient *self) } } -void client_activate(ObClient *self, gboolean here) +void client_activate(ObClient *self, gboolean here, gboolean user, Time time) { - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE); - if (self->iconic) - client_iconify(self, FALSE, here); - if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) { - if (here) - client_set_desktop(self, screen_desktop, FALSE); - else - screen_set_desktop(self->desktop); - } else if (!self->frame->visible) - /* if its not visible for other reasons, then don't mess - with it */ - return; - if (self->shaded) - client_shade(self, FALSE); + /* 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)? + */ + ob_debug("Want to activate window 0x%x with time %u (last time %u), " + "source=%s\n", + self->window, time, client_last_user_time, + (user ? "user" : "application")); + if (!user && time && time < client_last_user_time) + client_hilite(self, TRUE); + else { + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE); + if (self->iconic) + client_iconify(self, FALSE, here); + if (self->desktop != DESKTOP_ALL && + self->desktop != screen_desktop) { + if (here) + client_set_desktop(self, screen_desktop, FALSE); + else + screen_set_desktop(self->desktop); + } else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded) + client_shade(self, FALSE); - client_focus(self); + client_focus(self); - /* we do this an action here. this is rather important. this is because - we want the results from the focus change to take place BEFORE we go - about raising the window. when a fullscreen window loses focus, we need - this or else the raise wont be able to raise above the to-lose-focus - fullscreen window. */ - client_raise(self); + /* we do this an action here. this is rather important. this is because + we want the results from the focus change to take place BEFORE we go + about raising the window. when a fullscreen window loses focus, we + need this or else the raise wont be able to raise above the + to-lose-focus fullscreen window. */ + client_raise(self); + } } void client_raise(ObClient *self) { - action_run_string("Raise", self); + action_run_string("Raise", self, CurrentTime); } void client_lower(ObClient *self) { - action_run_string("Lower", self); + action_run_string("Lower", self, CurrentTime); } gboolean client_focused(ObClient *self) @@ -2992,8 +3134,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 @@ -3089,40 +3231,22 @@ void client_set_undecorated(ObClient *self, gboolean undecorated) } } -/* Determines which physical monitor a client is on by calculating the - area of the part of the client on each monitor. The number of the - monitor containing the greatest area of the client is returned.*/ guint client_monitor(ObClient *self) { - guint i; - guint most = 0; - guint mostv = 0; - - for (i = 0; i < screen_num_monitors; ++i) { - Rect *area = screen_physical_area_monitor(i); - if (RECT_INTERSECTS_RECT(*area, self->frame->area)) { - Rect r; - guint v; - - RECT_SET_INTERSECTION(r, *area, self->frame->area); - v = r.width * r.height; - - if (v > mostv) { - mostv = v; - most = i; - } - } - } - return most; + return screen_find_monitor(&self->frame->area); } -ObClient *client_search_top_transient(ObClient *self) +GSList *client_search_top_transients(ObClient *self) { - /* move up the transient chain as far as possible */ - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - return client_search_top_transient(self->transient_for); - } else { + GSList *ret = NULL; + + /* move up the direct transient chain as far as possible */ + while (self->transient_for && self->transient_for != OB_TRAN_GROUP) + self = self->transient_for; + + if (!self->transient_for) + ret = g_slist_prepend(ret, self); + else { GSList *it; g_assert(self->group); @@ -3130,16 +3254,15 @@ ObClient *client_search_top_transient(ObClient *self) for (it = self->group->members; it; it = g_slist_next(it)) { ObClient *c = it->data; - /* checking transient_for prevents infinate loops! */ - if (c != self && !c->transient_for) - break; + if (!c->transient_for) + ret = g_slist_prepend(ret, c); } - if (it) - return it->data; - } + + if (ret == NULL) /* no group parents */ + ret = g_slist_prepend(ret, self); } - return self; + return ret; } ObClient *client_search_focus_parent(ObClient *self) @@ -3212,11 +3335,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; @@ -3233,11 +3375,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) @@ -3247,45 +3389,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) @@ -3295,20 +3423,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) @@ -3316,25 +3436,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) @@ -3344,46 +3457,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) @@ -3393,35 +3491,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: