X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=a51ecfd7c7ed54ce2ba0e094d394b9969cb6f885;hb=eb2a0feb0154e86a1c6c117fd0f6d1a18959b694;hp=732f031f205fd7b5a357d28faa69c0767cf5554c;hpb=4fa19b598554d8cd02307745d2fe7809cc26a064;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 732f031f..a51ecfd7 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -2,7 +2,7 @@ client.c for the Openbox window manager Copyright (c) 2006 Mikael Magnusson - Copyright (c) 2003 Ben Jansens + Copyright (c) 2003-2007 Dana Jansens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -45,8 +45,7 @@ #include /*! The event mask to grab on client windows */ -#define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \ - StructureNotifyMask) +#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask) #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \ ButtonMotionMask) @@ -57,8 +56,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,16 +67,18 @@ 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); -static void client_showhide(ObClient *self); static void client_change_allowed_actions(ObClient *self); static void client_change_state(ObClient *self); -static void client_apply_startup_state(ObClient *self); +static void client_change_wm_state(ObClient *self); +static void client_apply_startup_state(ObClient *self, gint x, gint y); static void client_restore_session_state(ObClient *self); static void client_restore_session_stacking(ObClient *self); -static void client_urgent_notify(ObClient *self); +static ObAppSettings *client_get_settings_state(ObClient *self); +static void client_unfocus(ObClient *self); void client_startup(gboolean reconfig) { @@ -204,33 +207,6 @@ void client_manage_all() XFree(children); } -static ObAppSettings *get_settings(ObClient *client) -{ - GSList *a = config_per_app_settings; - - while (a) { - ObAppSettings *app = (ObAppSettings *) a->data; - - if ( - (app->name && !app->class && !strcmp(app->name, client->name)) - || (app->class && !app->name && !strcmp(app->class, client->class)) - || (app->class && app->name && !strcmp(app->class, client->class) - && !strcmp(app->name, client->name)) - ) { - ob_debug("Window matching: %s\n", app->name); - /* Match if no role was specified in the per app setting, or if the - * string matches the beginning of the role, since apps like to set - * the role to things like browser-window-23c4b2f */ - if (!app->role - || !strncmp(app->role, client->role, strlen(app->role))) - return app; - } - - a = a->next; - } - return NULL; -} - void client_manage(Window window) { ObClient *self; @@ -240,16 +216,18 @@ void client_manage(Window window) XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; + gint newx, newy; grab_server(TRUE); - /* check if it has already been unmapped by the time we started mapping + /* check if it has already been unmapped by the time we started mapping. the grab does a sync so we don't have to here */ if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) || XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) { XPutBackEvent(ob_display, &e); + ob_debug("Trying to manage unmapped window. Aborting that.\n"); grab_server(FALSE); return; /* don't manage it */ } @@ -291,22 +269,28 @@ void client_manage(Window window) self->window = window; /* non-zero defaults */ - self->title_count = 1; - self->wmstate = NormalState; + self->wmstate = WithdrawnState; /* make sure it gets updated first time */ self->layer = -1; self->desktop = screen_num_desktops; /* always an invalid value */ + self->user_time = ~0; /* maximum value, always newer than the real time */ client_get_all(self); client_restore_session_state(self); + /* per-app settings override stuff, and return the settings for other + uses too */ + settings = client_get_settings_state(self); + + client_calc_layer(self); - sn_app_started(self->class); + { + 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! */ focus_order_add_new(self); - client_change_state(self); - /* remove the client's border (and adjust re gravity) */ client_toggle_border(self, FALSE); @@ -319,59 +303,15 @@ void client_manage(Window window) frame_grab_client(self->frame, self); - grab_server(FALSE); - - client_apply_startup_state(self); + /* do this after we have a frame.. it uses the frame to help determine the + WM_STATE to apply. */ + client_change_state(self); - /* get and set application level settings */ - settings = get_settings(self); + grab_server(FALSE); - stacking_add(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); - if (settings->decor != -1) - client_set_undecorated(self, !settings->decor); - if (settings->iconic != -1) - client_iconify(self, !!settings->iconic, FALSE); - if (settings->skip_pager != -1) { - self->skip_pager = !!settings->skip_pager; - client_change_state(self); - } - if (settings->skip_taskbar != -1) { - self->skip_taskbar = !!settings->skip_taskbar; - client_change_state(self); - } - - /* 1 && -1 shouldn't be possible by the code in config.c */ - if (settings->max_vert == 1 && settings->max_horz == 1) - client_maximize(self, TRUE, 0, TRUE); - else if (settings->max_vert == 0 && settings->max_horz == 0) - client_maximize(self, FALSE, 0, TRUE); - else if (settings->max_vert == 1 && settings->max_horz == 0) { - client_maximize(self, TRUE, 2, TRUE); - client_maximize(self, FALSE, 1, TRUE); - } else if (settings->max_vert == 0 && settings->max_horz == 1) { - client_maximize(self, TRUE, 1, TRUE); - client_maximize(self, FALSE, 2, TRUE); - } - - if (settings->fullscreen != -1) - client_fullscreen(self, !!settings->fullscreen, TRUE); - - if (settings->desktop < screen_num_desktops - || settings->desktop == DESKTOP_ALL) - client_set_desktop(self, settings->desktop, TRUE); - - if (settings->layer > -2 && settings->layer < 2) - client_set_layer(self, settings->layer); - - } - /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && /* this means focus=true for window is same as config_focus_new=true */ @@ -419,15 +359,18 @@ void client_manage(Window window) #endif } + /* get the current position */ + newx = self->area.x; + newy = self->area.y; + + /* figure out placement for the window */ if (ob_state() == OB_STATE_RUNNING) { - gint x = self->area.x, ox = x; - gint y = self->area.y, oy = y; gboolean transient; - transient = place_client(self, &x, &y, settings); + transient = place_client(self, &newx, &newy, settings); /* make sure the window is visible. */ - client_find_onscreen(self, &x, &y, + client_find_onscreen(self, &newx, &newy, self->frame->area.width, self->frame->area.height, /* non-normal clients has less rules, and @@ -445,30 +388,82 @@ void client_manage(Window window) !(self->positioned & USPosition)) && client_normal(self) && !self->session)); - if (x != ox || y != oy) - client_move(self, x, y); } + /* do this after the window is placed, so the premax/prefullscreen numbers + won't be all wacko!! + also, this moves the window to the position where it has been placed + */ + ob_debug("placing window 0x%x at %d, %d with size %d x %d\n", + self->window, newx, newy, self->area.width, self->area.height); + client_apply_startup_state(self, newx, newy); + keyboard_grab_for_client(self, TRUE); mouse_grab_for_client(self, TRUE); + 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 @@ -501,33 +496,49 @@ void client_unmanage(ObClient *self) guint j; GSList *it; - ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class); + ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class, + self->title ? self->title : ""); g_assert(self != NULL); - keyboard_grab_for_client(self, FALSE); - mouse_grab_for_client(self, FALSE); + /* update the focus lists */ + focus_order_remove(self); + + if (focus_client == self) { + XEvent e; + + /* focus the last focused window on the desktop, and ignore enter + events from the unmap so it doesnt mess with the focus */ + while (XCheckTypedEvent(ob_display, EnterNotify, &e)); + /* remove these flags so we don't end up getting focused in the + fallback! */ + self->can_focus = FALSE; + self->focus_notify = FALSE; + self->modal = FALSE; + client_unfocus(self); + } /* potentially fix focusLast */ if (config_focus_last) grab_pointer(TRUE, OB_CURSOR_NONE); + frame_hide(self->frame); + XFlush(ob_display); + + keyboard_grab_for_client(self, FALSE); + mouse_grab_for_client(self, FALSE); + /* remove the window from our save set */ XChangeSaveSet(ob_display, self->window, SetModeDelete); /* we dont want events no more */ XSelectInput(ob_display, self->window, NoEventMask); - frame_hide(self->frame); - client_list = g_list_remove(client_list, self); stacking_remove(self); g_hash_table_remove(window_map, &self->window); - /* update the focus lists */ - focus_order_remove(self); - - /* once the client is out of the list, update the struts to remove it's + /* once the client is out of the list, update the struts to remove its influence */ if (STRUT_EXISTS(self->strut)) screen_update_areas(); @@ -536,20 +547,6 @@ void client_unmanage(ObClient *self) Destructor *d = it->data; d->func(self, d->data); } - - if (focus_client == self) { - XEvent e; - - /* focus the last focused window on the desktop, and ignore enter - events from the unmap so it doesnt mess with the focus */ - while (XCheckTypedEvent(ob_display, EnterNotify, &e)); - /* remove these flags so we don't end up getting focused in the - fallback! */ - self->can_focus = FALSE; - self->focus_notify = FALSE; - self->modal = FALSE; - client_unfocus(self); - } /* tell our parent(s) that we're gone */ if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */ @@ -582,6 +579,27 @@ void client_unmanage(ObClient *self) /* reparent the window out of the frame, and free the frame */ frame_release_client(self->frame, self); self->frame = NULL; + + /* restore the window's original geometry so it is not lost */ + if (self->fullscreen) + XMoveResizeWindow(ob_display, self->window, + self->pre_fullscreen_area.x, + self->pre_fullscreen_area.y, + self->pre_fullscreen_area.width, + self->pre_fullscreen_area.height); + else if (self->max_horz || self->max_vert) { + Rect a = self->area; + if (self->max_horz) { + a.x = self->pre_max_area.x; + a.width = self->pre_max_area.width; + } + if (self->max_vert) { + a.y = self->pre_max_area.y; + a.height = self->pre_max_area.height; + } + XMoveResizeWindow(ob_display, self->window, + a.x, a.y, a.width, a.height); + } if (ob_state() != OB_STATE_EXITING) { /* these values should not be persisted across a window @@ -619,12 +637,71 @@ void client_unmanage(ObClient *self) grab_pointer(FALSE, OB_CURSOR_NONE); } -static void client_urgent_notify(ObClient *self) +static ObAppSettings *client_get_settings_state(ObClient *self) { - if (self->urgent) - frame_flash_start(self->frame); - else - frame_flash_stop(self->frame); + ObAppSettings *settings = NULL; + GSList *it; + + for (it = config_per_app_settings; it; it = g_slist_next(it)) { + ObAppSettings *app = it->data; + + if ((app->name && !app->class && !strcmp(app->name, self->name)) + || (app->class && !app->name && !strcmp(app->class, self->class)) + || (app->class && app->name && !strcmp(app->class, self->class) + && !strcmp(app->name, self->name))) + { + ob_debug("Window matching: %s\n", app->name); + /* Match if no role was specified in the per app setting, or if the + * string matches the beginning of the role, since apps like to set + * the role to things like browser-window-23c4b2f */ + if (!app->role + || !strncmp(app->role, self->role, strlen(app->role))) + { + /* use this one */ + settings = app; + break; + } + } + } + + if (settings) { + if (settings->shade != -1) + self->shaded = !!settings->shade; + if (settings->decor != -1) + self->undecorated = !settings->decor; + if (settings->iconic != -1) + self->iconic = !!settings->iconic; + if (settings->skip_pager != -1) + self->skip_pager = !!settings->skip_pager; + if (settings->skip_taskbar != -1) + self->skip_taskbar = !!settings->skip_taskbar; + + if (settings->max_vert != -1) + self->max_vert = !!settings->max_vert; + if (settings->max_horz != -1) + self->max_vert = !!settings->max_horz; + + if (settings->fullscreen != -1) + self->fullscreen = !!settings->fullscreen; + + if (settings->desktop < screen_num_desktops + || settings->desktop == DESKTOP_ALL) + client_set_desktop(self, settings->desktop, TRUE); + + if (settings->layer == -1) { + self->below = TRUE; + self->above = FALSE; + } + else if (settings->layer == 0) { + self->below = FALSE; + self->above = FALSE; + } + else if (settings->layer == 1) { + self->below = FALSE; + self->above = TRUE; + } + } + return settings; } static void client_restore_session_state(ObClient *self) @@ -833,6 +910,7 @@ static void client_get_all(ObClient *self) 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); @@ -841,7 +919,8 @@ static void client_get_all(ObClient *self) desktop is not specified */ client_get_shaped(self); - client_get_state(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 */ @@ -866,6 +945,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) @@ -886,6 +966,9 @@ static void client_get_area(ObClient *self) RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height); self->border_width = wattrib.border_width; + + ob_debug("client area: %d %d %d %d\n", wattrib.x, wattrib.y, + wattrib.width, wattrib.height); } static void client_get_desktop(ObClient *self) @@ -933,6 +1016,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; @@ -961,44 +1079,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) @@ -1036,22 +1124,42 @@ 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; } } } - } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) { - self->transient = TRUE; - target = OB_TRAN_GROUP; + } else if (self->group) { + if (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_TOOLBAR || + self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_UTILITY) + { + self->transient = TRUE; + target = OB_TRAN_GROUP; + } } else self->transient = FALSE; @@ -1437,12 +1545,12 @@ static void client_change_allowed_actions(ObClient *self) else self->iconic = FALSE; } if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) { - if (self->frame) client_fullscreen(self, FALSE, TRUE); + if (self->frame) client_fullscreen(self, FALSE); else self->fullscreen = FALSE; } if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz || self->max_vert)) { - if (self->frame) client_maximize(self, FALSE, 0, TRUE); + if (self->frame) client_maximize(self, FALSE, 0); else self->max_vert = self->max_horz = FALSE; } } @@ -1459,7 +1567,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 +1582,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,120 +1642,48 @@ 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) { - GList *it; - guint32 nums; - guint i; gchar *data = NULL; - gboolean read_title; - gchar *old_title; - old_title = self->title; + g_free(self->title); /* try netwm */ if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) { /* try old x stuff */ if (!(PROP_GETS(self->window, wm_name, locale, &data) || PROP_GETS(self->window, wm_name, utf8, &data))) { - // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html if (self->transient) { + /* + GNOME alert windows are not given titles: + http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html + */ data = g_strdup(""); - goto no_number; } else data = g_strdup("Unnamed Window"); } } - if (config_title_number) { - - /* did the title change? then reset the title_count */ - if (old_title && 0 != strncmp(old_title, data, strlen(data))) - self->title_count = 1; - - /* look for duplicates and append a number */ - nums = 0; - for (it = client_list; it; it = g_list_next(it)) - if (it->data != self) { - ObClient *c = it->data; - - if (c->title_count == 1) { - if (!strcmp(c->title, data)) - nums |= 1 << c->title_count; - } else { - size_t len; - gchar *end; - - /* find the beginning of our " - [%u]", this relies on - that syntax being used */ - end = strrchr(c->title, '-') - 1; - len = end - c->title; - if (!strncmp(c->title, data, len)) - nums |= 1 << c->title_count; - } - } - /* find first free number */ - for (i = 1; i <= 32; ++i) - if (!(nums & (1 << i))) { - if (self->title_count == 1 || i == 1) - self->title_count = i; - break; - } - /* dont display the number for the first window */ - if (self->title_count > 1) { - gchar *ndata; - ndata = g_strdup_printf("%s - [%u]", data, self->title_count); - g_free(data); - data = ndata; - } - } else - self->title_count = 1; - -no_number: PROP_SETS(self->window, net_wm_visible_name, data); self->title = data; if (self->frame) frame_adjust_title(self->frame); - g_free(old_title); - /* update the icon title */ data = NULL; g_free(self->icon_title); - read_title = TRUE; /* try netwm */ if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data)) /* try old x stuff */ - if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) - || PROP_GETS(self->window, wm_icon_name, utf8, &data))) { + if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) || + PROP_GETS(self->window, wm_icon_name, utf8, &data))) data = g_strdup(self->title); - read_title = FALSE; - } - - /* append the title count, dont display the number for the first window. - * We don't need to check for config_title_number here since title_count - * is not set above 1 then. */ - if (read_title && self->title_count > 1) { - gchar *newdata; - newdata = g_strdup_printf("%s - [%u]", data, self->title_count); - g_free(data); - data = newdata; - } PROP_SETS(self->window, net_wm_visible_icon_name, data); - self->icon_title = data; } @@ -1786,23 +1818,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,16 +1847,58 @@ void client_update_icons(ObClient *self) frame_adjust_icon(self->frame); } -static void client_change_state(ObClient *self) +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 %s user time %u\n", self->title, time); + ob_debug("last user time %u\n", client_last_user_time); + */ + } +} + +static void client_change_wm_state(ObClient *self) { gulong state[2]; + glong old; + + old = self->wmstate; + + if (self->shaded || self->iconic || !self->frame->visible) + self->wmstate = IconicState; + else + self->wmstate = NormalState; + + if (old != self->wmstate) { + PROP_MSG(self->window, kde_wm_change_state, + self->wmstate, 1, 0, 0); + + state[0] = self->wmstate; + state[1] = None; + PROP_SETA32(self->window, wm_state, wm_state, state, 2); + } +} + +static void client_change_state(ObClient *self) +{ gulong netstate[11]; guint num; - state[0] = self->wmstate; - state[1] = None; - PROP_SETA32(self->window, wm_state, wm_state, state, 2); - num = 0; if (self->modal) netstate[num++] = prop_atoms.net_wm_state_modal; @@ -1863,12 +1920,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); } @@ -1934,20 +1991,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)); @@ -1956,17 +2014,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_all_top_parents(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,13 +2055,25 @@ gboolean client_should_show(ObClient *self) return FALSE; } -static void client_showhide(ObClient *self) +void client_showhide(ObClient *self) { - if (client_should_show(self)) + if (client_should_show(self)) { frame_show(self->frame); - else + } + else { frame_hide(self->frame); + + /* Fall back focus since we're disappearing */ + if (focus_client == self) + client_unfocus(self); + } + + /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it + needs to be in IconicState. This includes when it is on another + desktop! + */ + client_change_wm_state(self); } gboolean client_normal(ObClient *self) { @@ -2013,8 +2082,17 @@ gboolean client_normal(ObClient *self) { self->type == OB_CLIENT_TYPE_SPLASH); } -static void client_apply_startup_state(ObClient *self) +static void client_apply_startup_state(ObClient *self, gint x, gint y) { + gboolean pos = FALSE; /* has the window's position been configured? */ + gint ox, oy; + + /* save the position, and set self->area for these to use */ + ox = self->area.x; + oy = self->area.y; + self->area.x = x; + self->area.y = y; + /* these are in a carefully crafted order.. */ if (self->iconic) { @@ -2023,7 +2101,8 @@ static void client_apply_startup_state(ObClient *self) } if (self->fullscreen) { self->fullscreen = FALSE; - client_fullscreen(self, TRUE, FALSE); + client_fullscreen(self, TRUE); + pos = TRUE; } if (self->undecorated) { self->undecorated = FALSE; @@ -2033,18 +2112,31 @@ 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; - client_maximize(self, TRUE, 0, FALSE); + client_maximize(self, TRUE, 0); + pos = TRUE; } else if (self->max_vert) { self->max_vert = FALSE; - client_maximize(self, TRUE, 2, FALSE); + client_maximize(self, TRUE, 2); + pos = TRUE; } else if (self->max_horz) { self->max_horz = FALSE; - client_maximize(self, TRUE, 1, FALSE); + client_maximize(self, TRUE, 1); + pos = TRUE; + } + + /* if the client didn't get positioned yet, then do so now */ + if (!pos && (ox != x || oy != y)) { + /* use the saved position */ + self->area.x = ox; + self->area.y = oy; + client_move(self, x, y); } /* nothing to do for the other states: @@ -2056,16 +2148,12 @@ static void client_apply_startup_state(ObClient *self) */ } -void client_configure_full(ObClient *self, ObCorner anchor, - gint x, gint y, gint w, gint h, - gboolean user, gboolean final, - gboolean force_reply) +void client_try_configure(ObClient *self, ObCorner anchor, + gint *x, gint *y, gint *w, gint *h, + gint *logicalw, gint *logicalh, + gboolean user) { - gint oldw, oldh; - gboolean send_resize_client; - gboolean moved = FALSE, resized = FALSE; - guint fdecor = self->frame->decorations; - gboolean fhorz = self->frame->max_horz; + Rect desired_area = {*x, *y, *w, *h}; /* make the frame recalculate its dimentions n shit without changing anything visible for real, this way the constraints below can work with @@ -2073,7 +2161,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, frame_adjust_area(self->frame, TRUE, TRUE, TRUE); /* gets the frame's position */ - frame_client_gravity(self->frame, &x, &y); + frame_client_gravity(self->frame, x, y); /* these positions are frame positions, not client positions */ @@ -2082,47 +2170,49 @@ 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; - y = a->y; - w = a->width; - h = a->height; + *x = a->x; + *y = a->y; + *w = a->width; + *h = a->height; user = FALSE; /* ignore that increment etc shit when in fullscreen */ } else { 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) { - x = a->x; - w = a->width - self->frame->size.left - self->frame->size.right; + *x = a->x; + *w = a->width - self->frame->size.left - self->frame->size.right; } if (self->max_vert) { - y = a->y; - h = a->height - self->frame->size.top - self->frame->size.bottom; + *y = a->y; + *h = a->height - self->frame->size.top - self->frame->size.bottom; } } /* gets the client's position */ - frame_frame_gravity(self->frame, &x, &y); + frame_frame_gravity(self->frame, x, y); /* these override the above states! if you cant move you can't move! */ if (user) { if (!(self->functions & OB_CLIENT_FUNC_MOVE)) { - x = self->area.x; - y = self->area.y; + *x = self->area.x; + *y = self->area.y; } if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) { - w = self->area.width; - h = self->area.height; + *w = self->area.width; + *h = self->area.height; } } - if (!(w == self->area.width && h == self->area.height)) { + if (!(*w == self->area.width && *h == self->area.height)) { gint basew, baseh, minw, minh; /* base size is substituted with min size if not specified */ @@ -2146,83 +2236,105 @@ void client_configure_full(ObClient *self, ObCorner anchor, sizes */ /* smaller than min size or bigger than max size? */ - if (w > self->max_size.width) w = self->max_size.width; - if (w < minw) w = minw; - if (h > self->max_size.height) h = self->max_size.height; - if (h < minh) h = minh; + if (*w > self->max_size.width) *w = self->max_size.width; + if (*w < minw) *w = minw; + if (*h > self->max_size.height) *h = self->max_size.height; + if (*h < minh) *h = minh; - w -= basew; - h -= baseh; + *w -= basew; + *h -= baseh; /* keep to the increments */ - w /= self->size_inc.width; - h /= self->size_inc.height; + *w /= self->size_inc.width; + *h /= self->size_inc.height; /* you cannot resize to nothing */ - if (basew + w < 1) w = 1 - basew; - if (baseh + h < 1) h = 1 - baseh; + if (basew + *w < 1) *w = 1 - basew; + if (baseh + *h < 1) *h = 1 - baseh; - /* store the logical size */ - SIZE_SET(self->logical_size, - self->size_inc.width > 1 ? w : w + basew, - self->size_inc.height > 1 ? h : h + baseh); + /* save the logical size */ + *logicalw = self->size_inc.width > 1 ? *w : *w + basew; + *logicalh = self->size_inc.height > 1 ? *h : *h + baseh; - w *= self->size_inc.width; - h *= self->size_inc.height; + *w *= self->size_inc.width; + *h *= self->size_inc.height; - w += basew; - h += baseh; + *w += basew; + *h += baseh; /* adjust the height to match the width for the aspect ratios. for this, min size is not substituted for base size ever. */ - w -= self->base_size.width; - h -= self->base_size.height; + *w -= self->base_size.width; + *h -= self->base_size.height; if (!self->fullscreen) { if (self->min_ratio) - if (h * self->min_ratio > w) { - h = (gint)(w / self->min_ratio); + if (*h * self->min_ratio > *w) { + *h = (gint)(*w / self->min_ratio); /* you cannot resize to nothing */ - if (h < 1) { - h = 1; - w = (gint)(h * self->min_ratio); + if (*h < 1) { + *h = 1; + *w = (gint)(*h * self->min_ratio); } } if (self->max_ratio) - if (h * self->max_ratio < w) { - h = (gint)(w / self->max_ratio); + if (*h * self->max_ratio < *w) { + *h = (gint)(*w / self->max_ratio); /* you cannot resize to nothing */ - if (h < 1) { - h = 1; - w = (gint)(h * self->min_ratio); + if (*h < 1) { + *h = 1; + *w = (gint)(*h * self->min_ratio); } } } - w += self->base_size.width; - h += self->base_size.height; + *w += self->base_size.width; + *h += self->base_size.height; } - g_assert(w > 0); - g_assert(h > 0); + g_assert(*w > 0); + g_assert(*h > 0); switch (anchor) { case OB_CORNER_TOPLEFT: break; case OB_CORNER_TOPRIGHT: - x -= w - self->area.width; + *x -= *w - self->area.width; break; case OB_CORNER_BOTTOMLEFT: - y -= h - self->area.height; + *y -= *h - self->area.height; break; case OB_CORNER_BOTTOMRIGHT: - x -= w - self->area.width; - y -= h - self->area.height; + *x -= *w - self->area.width; + *y -= *h - self->area.height; break; } +} + +void client_configure_full(ObClient *self, ObCorner anchor, + gint x, gint y, gint w, gint h, + gboolean user, gboolean final, + gboolean force_reply) +{ + gint oldw, oldh; + gboolean send_resize_client; + gboolean moved = FALSE, resized = FALSE; + guint fdecor = self->frame->decorations; + gboolean fhorz = self->frame->max_horz; + gint logicalw, logicalh; + + /* find the new x, y, width, and height (and logical size) */ + client_try_configure(self, anchor, &x, &y, &w, &h, + &logicalw, &logicalh, user); + + /* set the logical size if things changed */ + if (!(w == self->area.width && h == self->area.height)) + SIZE_SET(self->logical_size, logicalw, logicalh); + + /* figure out if we moved or resized or what */ moved = x != self->area.x || y != self->area.y; resized = w != self->area.width || h != self->area.height; @@ -2279,7 +2391,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, XFlush(ob_display); } -void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea) +void client_fullscreen(ObClient *self, gboolean fs) { gint x, y, w, h; @@ -2287,12 +2399,21 @@ 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) - self->pre_fullscreen_area = self->area; + self->pre_fullscreen_area = self->area; + /* if the window is maximized, its area isn't all that meaningful. + save it's premax area instead. */ + if (self->max_horz) { + self->pre_fullscreen_area.x = self->pre_max_area.x; + self->pre_fullscreen_area.width = self->pre_max_area.width; + } + if (self->max_vert) { + self->pre_fullscreen_area.y = self->pre_max_area.y; + self->pre_fullscreen_area.height = self->pre_max_area.height; + } /* these are not actually used cuz client_configure will set them as appropriate when the window is fullscreened */ @@ -2337,17 +2458,9 @@ static void client_iconify_recursive(ObClient *self, ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"), self->window); - self->iconic = iconic; - if (iconic) { if (self->functions & OB_CLIENT_FUNC_ICONIFY) { - glong old; - - old = self->wmstate; - self->wmstate = IconicState; - if (old != self->wmstate) - PROP_MSG(self->window, kde_wm_change_state, - self->wmstate, 1, 0, 0); + self->iconic = iconic; /* update the focus lists.. iconic windows go to the bottom of the list, put the new iconic window at the 'top of the @@ -2357,17 +2470,11 @@ static void client_iconify_recursive(ObClient *self, changed = TRUE; } } else { - glong old; + self->iconic = iconic; if (curdesk) client_set_desktop(self, screen_desktop, FALSE); - old = self->wmstate; - self->wmstate = self->shaded ? IconicState : NormalState; - if (old != self->wmstate) - PROP_MSG(self->window, kde_wm_change_state, - self->wmstate, 1, 0, 0); - /* this puts it after the current focused window */ focus_order_remove(self); focus_order_add_new(self); @@ -2383,22 +2490,21 @@ static void client_iconify_recursive(ObClient *self, screen_update_areas(); } - /* iconify all transients */ + /* iconify all direct transients */ for (it = self->transients; it; it = g_slist_next(it)) - if (it->data != self) client_iconify_recursive(it->data, - iconic, curdesk); + if (it->data != self) + if (client_is_direct_child(self, it->data)) + client_iconify_recursive(it->data, iconic, curdesk); } void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) { /* move up the transient chain as far as possible first */ - self = client_search_top_transient(self); - - client_iconify_recursive(client_search_top_transient(self), - iconic, curdesk); + self = client_search_top_parent(self); + client_iconify_recursive(self, iconic, curdesk); } -void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea) +void client_maximize(ObClient *self, gboolean max, gint dir) { gint x, y, w, h; @@ -2424,17 +2530,15 @@ void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea) h = self->area.height; if (max) { - if (savearea) { - if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */ - RECT_SET(self->pre_max_area, - self->area.x, self->pre_max_area.y, - self->area.width, self->pre_max_area.height); - } - if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */ - RECT_SET(self->pre_max_area, - self->pre_max_area.x, self->area.y, - self->pre_max_area.width, self->area.height); - } + if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */ + RECT_SET(self->pre_max_area, + self->area.x, self->pre_max_area.y, + self->area.width, self->pre_max_area.height); + } + if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */ + RECT_SET(self->pre_max_area, + self->pre_max_area.x, self->area.y, + self->pre_max_area.width, self->area.height); } } else { Rect *a; @@ -2486,19 +2590,9 @@ void client_shade(ObClient *self, gboolean shade) shade) || /* can't shade */ self->shaded == shade) return; /* already done */ - /* when we're iconic, don't change the wmstate */ - if (!self->iconic) { - glong old; - - old = self->wmstate; - self->wmstate = shade ? IconicState : NormalState; - if (old != self->wmstate) - PROP_MSG(self->window, kde_wm_change_state, - self->wmstate, 1, 0, 0); - } - self->shaded = shade; client_change_state(self); + client_change_wm_state(self); /* the window is being hidden/shown */ /* resize the frame to just the titlebar */ frame_adjust_area(self->frame, FALSE, FALSE, FALSE); } @@ -2540,6 +2634,20 @@ void client_kill(ObClient *self) XKillClient(ob_display, self->window); } +void client_hilite(ObClient *self, gboolean hilite) +{ + if (self->demands_attention == hilite) + return; /* no change */ + + /* don't allow focused windows to hilite */ + self->demands_attention = hilite && !client_focused(self); + if (self->demands_attention) + 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) { @@ -2578,14 +2686,23 @@ void client_set_desktop_recursive(ObClient *self, /* move all transients */ for (it = self->transients; it; it = g_slist_next(it)) - if (it->data != self) client_set_desktop_recursive(it->data, - target, donthide); + if (it->data != self) + if (client_is_direct_child(self, it->data)) + client_set_desktop_recursive(it->data, target, donthide); } void client_set_desktop(ObClient *self, guint target, gboolean donthide) { - client_set_desktop_recursive(client_search_top_transient(self), - target, donthide); + self = client_search_top_parent(self); + client_set_desktop_recursive(self, target, donthide); +} + +gboolean client_is_direct_child(ObClient *parent, ObClient *child) +{ + while (child != parent && + child->transient_for && child->transient_for != OB_TRAN_GROUP) + child = child->transient_for; + return child == parent; } ObClient *client_search_modal_child(ObClient *self) @@ -2639,6 +2756,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 +2806,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 +2838,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 +2865,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; } @@ -2750,23 +2876,23 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) if (max_horz != self->max_horz && max_vert != self->max_vert) { /* toggling both */ if (max_horz == max_vert) { /* both going the same way */ - client_maximize(self, max_horz, 0, TRUE); + client_maximize(self, max_horz, 0); } else { - client_maximize(self, max_horz, 1, TRUE); - client_maximize(self, max_vert, 2, TRUE); + client_maximize(self, max_horz, 1); + client_maximize(self, max_vert, 2); } } else { /* toggling one */ if (max_horz != self->max_horz) - client_maximize(self, max_horz, 1, TRUE); + client_maximize(self, max_horz, 1); else - client_maximize(self, max_vert, 2, TRUE); + client_maximize(self, max_vert, 2); } } /* change fullscreen state before shading, as it will affect if the window can shade or not */ if (fullscreen != self->fullscreen) - client_fullscreen(self, fullscreen, TRUE); + client_fullscreen(self, fullscreen); if (shaded != self->shaded) client_shade(self, shaded); if (undecorated != self->undecorated) @@ -2780,16 +2906,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; } @@ -2833,6 +2960,11 @@ gboolean client_focus(ObClient *self) /* choose the correct target */ self = client_focus_target(self); +#if 0 + if (!client_validate(self)) + return FALSE; +#endif + if (!client_can_focus(self)) { if (!self->frame->visible) { /* update the focus lists */ @@ -2841,15 +2973,10 @@ gboolean client_focus(ObClient *self) return FALSE; } + ob_debug("Focusing client \"%s\" at time %u\n", self->title, event_curtime); + if (self->can_focus) { - /* RevertToPointerRoot causes much more headache than RevertToNone, so - I choose to use it always, hopefully to find errors quicker, if any - are left. (I hate X. I hate focus events.) - - Update: Changing this to RevertToNone fixed a bug with mozilla (bug - #799. So now it is RevertToNone again. - */ - XSetInputFocus(ob_display, self->window, RevertToNone, + XSetInputFocus(ob_display, self->window, RevertToPointerRoot, event_curtime); } @@ -2881,58 +3008,68 @@ gboolean client_focus(ObClient *self) return TRUE; } -/* Used when the current client is closed, focus_last will then prevent - * focus from going to the mouse pointer */ -void client_unfocus(ObClient *self) +/* Used when the current client is closed or otherwise hidden, focus_last will + then prevent focus from going to the mouse pointer +*/ +static void client_unfocus(ObClient *self) { if (focus_client == self) { #ifdef DEBUG_FOCUS ob_debug("client_unfocus for %lx\n", self->window); #endif - focus_fallback(OB_FOCUS_FALLBACK_CLOSED); + focus_fallback(FALSE); } } void client_activate(ObClient *self, gboolean here, gboolean user) { /* 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) - 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); + 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, event_curtime, client_last_user_time, + (user ? "user" : "application")); + if (!user && event_curtime && event_curtime < 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) @@ -3129,40 +3266,29 @@ 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; + return screen_find_monitor(&self->frame->area); +} - if (v > mostv) { - mostv = v; - most = i; - } - } - } - return most; +ObClient *client_search_top_parent(ObClient *self) +{ + while (self->transient_for && self->transient_for != OB_TRAN_GROUP) + self = self->transient_for; + return self; } -ObClient *client_search_top_transient(ObClient *self) +GSList *client_search_all_top_parents(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); @@ -3170,16 +3296,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)