X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=cbef506ef0a9e05de8e01958f12b37b769dfb136;hb=19b480058e869a588ea20be5e29017ae2052e967;hp=ce1c417fd0f8f73303f4b243de8cb8c93123901b;hpb=de1559a09497ae4ead44a6e70b556123a5bab35c;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index ce1c417f..cbef506e 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,7 +1,7 @@ /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- client.c for the Openbox window manager - Copyright (c) 2004 Mikael Magnusson + Copyright (c) 2006 Mikael Magnusson Copyright (c) 2003 Ben Jansens This program is free software; you can redistribute it and/or modify @@ -57,8 +57,10 @@ typedef struct gpointer data; } Destructor; -GList *client_list = NULL; -GSList *client_destructors = NULL; +GList *client_list = NULL; + +static GSList *client_destructors = NULL; +static Time client_last_user_time = CurrentTime; static void client_get_all(ObClient *self); static void client_toggle_border(ObClient *self, gboolean show); @@ -75,7 +77,6 @@ static void client_change_state(ObClient *self); static void client_apply_startup_state(ObClient *self); static void client_restore_session_state(ObClient *self); static void client_restore_session_stacking(ObClient *self); -static void client_urgent_notify(ObClient *self); void client_startup(gboolean reconfig) { @@ -204,19 +205,26 @@ void client_manage_all() XFree(children); } -/* This should possibly do something more interesting than just match - * against WM_CLASS literally. */ -static ObAppSetting *get_settings(ObClient *client) +static ObAppSettings *get_settings(ObClient *client) { GSList *a = config_per_app_settings; while (a) { - ObAppSetting *app = (ObAppSetting *) a->data; + ObAppSettings *app = (ObAppSettings *) a->data; - if (!strcmp(app->name, client->name)) { + 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); - - return (ObAppSetting *) a->data; + /* 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; @@ -232,14 +240,15 @@ void client_manage(Window window) XSetWindowAttributes attrib_set; XWMHints *wmhint; gboolean activate = FALSE; - ObAppSetting *settings; + ObAppSettings *settings; grab_server(TRUE); /* 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); @@ -248,7 +257,8 @@ void client_manage(Window window) /* make sure it isn't an override-redirect window */ if (!XGetWindowAttributes(ob_display, window, &attrib) || - attrib.override_redirect) { + attrib.override_redirect) + { grab_server(FALSE); return; /* don't manage it */ } @@ -256,7 +266,8 @@ void client_manage(Window window) /* is the window a docking app */ if ((wmhint = XGetWMHints(ob_display, window))) { if ((wmhint->flags & StateHint) && - wmhint->initial_state == WithdrawnState) { + wmhint->initial_state == WithdrawnState) + { dock_add(window, wmhint); grab_server(FALSE); XFree(wmhint); @@ -285,11 +296,15 @@ 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); + { + 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! */ @@ -305,7 +320,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); @@ -316,32 +331,65 @@ void client_manage(Window window) /* get and set application level settings */ settings = get_settings(self); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + client_restore_session_stacking(self); + if (settings) { - if (settings->shade && !settings->decor) - settings->decor = TRUE; - - client_shade(self, settings->shade); - client_set_undecorated(self, !settings->decor); - - if (settings->desktop != -1) - client_set_desktop(self, settings->desktop, FALSE); + /* 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); + } - client_set_layer(self, settings->layer); - } + /* 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); + } - stacking_add(CLIENT_AS_WINDOW(self)); - client_restore_session_stacking(self); + 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 && - (config_focus_new || client_search_focus_parent(self)) || - (settings && settings->focus) && + /* 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) { @@ -379,8 +427,9 @@ void client_manage(Window window) if (ob_state() == OB_STATE_RUNNING) { gint x = self->area.x, ox = x; gint y = self->area.y, oy = y; + gboolean transient; - place_client(self, &x, &y, settings); + transient = place_client(self, &x, &y, settings); /* make sure the window is visible. */ client_find_onscreen(self, &x, &y, @@ -396,10 +445,11 @@ void client_manage(Window window) off-screen and on xinerama divides (ie, it is up to the placement routines to avoid the xinerama divides) */ - ((self->positioned & PPosition) && - !(self->positioned & USPosition)) && - client_normal(self) && - !self->session); + transient || + (((self->positioned & PPosition) && + !(self->positioned & USPosition)) && + client_normal(self) && + !self->session)); if (x != ox || y != oy) client_move(self, x, y); } @@ -407,23 +457,54 @@ void client_manage(Window window) keyboard_grab_for_client(self, TRUE); mouse_grab_for_client(self, TRUE); + if (activate) { + /* This is focus stealing prevention, if a user_time has been set */ + 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 (!self->user_time || self->user_time >= client_last_user_time) + { + /* 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); + + /* don't focus it ! (focus stealing prevention) */ + activate = FALSE; + } + } + 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 @@ -436,7 +517,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(); @@ -483,7 +565,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; @@ -572,14 +655,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; @@ -658,17 +733,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, @@ -681,8 +763,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; @@ -771,20 +853,22 @@ static void client_toggle_border(ObClient *self, gboolean show) static void client_get_all(ObClient *self) { client_get_area(self); - client_update_transient_for(self); - client_update_wmhints(self); - client_get_startup_id(self); - client_get_desktop(self); - client_get_shaped(self); - client_get_mwm_hints(self); - client_get_type(self);/* this can change the mwmhints for special cases */ /* The transient hint is used to pick a type, but the type can also affect - transiency (dialogs are always made transients). This is Havoc's idea, - but it is needed to make some apps work right (eg tsclient). */ + transiency (dialogs are always made transients of their group if they + have one). This is Havoc's idea, but it is needed to make some apps + work right (eg tsclient). */ + client_update_transient_for(self); + client_get_type(self);/* this can change the mwmhints for special cases */ client_update_transient_for(self); + client_update_wmhints(self); + client_get_startup_id(self); + client_get_desktop(self);/* uses transient data/group/startup id if a + desktop is not specified */ + client_get_shaped(self); + client_get_state(self); { @@ -810,6 +894,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) @@ -905,6 +990,8 @@ static void client_get_state(ObClient *self) self->above = TRUE; else if (state[i] == prop_atoms.net_wm_state_below) self->below = TRUE; + else if (state[i] == prop_atoms.net_wm_state_demands_attention) + self->demands_attention = TRUE; else if (state[i] == prop_atoms.ob_wm_state_undecorated) self->undecorated = TRUE; } @@ -980,7 +1067,13 @@ void client_update_transient_for(ObClient *self) a dockapp, for example */ target = NULL; } - + +#if 0 +/* we used to do this, but it violates the ICCCM and causes problems because + toolkits seem to set transient_for = root rather arbitrarily (eg kicker's + config dialogs), so it is being removed. the ewmh provides other ways to + make things transient for their group. -dana +*/ if (!target && self->group) { /* not transient to a client, see if it is transient for a group */ @@ -992,6 +1085,8 @@ void client_update_transient_for(ObClient *self) target = OB_TRAN_GROUP; } } +#endif + } } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) { self->transient = TRUE; @@ -1199,7 +1294,7 @@ void client_update_normal_hints(ObClient *self) if (size.flags & PBaseSize) SIZE_SET(self->base_size, size.base_width, size.base_height); - if (size.flags & PResizeInc) + if (size.flags & PResizeInc && size.width_inc && size.height_inc) SIZE_SET(self->size_inc, size.width_inc, size.height_inc); } } @@ -1209,7 +1304,7 @@ void client_setup_decor_and_functions(ObClient *self) /* start with everything (cept fullscreen) */ self->decorations = (OB_FRAME_DECOR_TITLEBAR | - (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) | + OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_BORDER | OB_FRAME_DECOR_ICON | @@ -1264,9 +1359,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; + } } } @@ -1397,7 +1498,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 */ @@ -1413,9 +1513,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; @@ -1476,14 +1573,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) @@ -1500,7 +1589,8 @@ void client_update_title(ObClient *self) /* 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)) { + 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) { data = g_strdup(""); @@ -1510,35 +1600,52 @@ void client_update_title(ObClient *self) } } - /* did the title change? then reset the title_count */ - if (old_title && 0 != strncmp(old_title, data, strlen(data))) - self->title_count = 1; + if (config_title_number) { - /* 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 (0 == strncmp(c->title, data, strlen(data))) - 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; + /* 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; } - /* 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; - PROP_SETS(self->window, net_wm_visible_name, data); no_number: + PROP_SETS(self->window, net_wm_visible_name, data); self->title = data; if (self->frame) @@ -1554,19 +1661,20 @@ no_number: /* 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)) { + 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 */ + /* 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 *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); @@ -1705,23 +1813,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; @@ -1751,6 +1842,26 @@ void client_update_icons(ObClient *self) frame_adjust_icon(self->frame); } +void client_update_user_time(ObClient *self, gboolean new_event) +{ + guint32 time; + + if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) { + self->user_time = time; + /* we set this every time, not just when it grows, because in practice + sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes + backward we don't want all windows to stop focusing. we'll just + assume noone is setting times older than the last one, cuz that + would be pretty stupid anyways + However! This is called when a window is mapped to get its user time + but it's an old number, it's not changing it from new user + interaction, so in that case, don't change the last user time. + */ + if (new_event) + client_last_user_time = time; + } +} + static void client_change_state(ObClient *self) { gulong state[2]; @@ -1782,6 +1893,8 @@ static void client_change_state(ObClient *self) netstate[num++] = prop_atoms.net_wm_state_above; if (self->below) netstate[num++] = prop_atoms.net_wm_state_below; + if (self->demands_attention) + netstate[num++] = prop_atoms.net_wm_state_demands_attention; if (self->undecorated) netstate[num++] = prop_atoms.ob_wm_state_undecorated; PROP_SETA32(self->window, net_wm_state, atom, netstate, num); @@ -1952,8 +2065,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; @@ -1998,35 +2113,16 @@ void client_configure_full(ObClient *self, ObCorner anchor, /* set the size and position if fullscreen */ if (self->fullscreen) { -#ifdef VIDMODE - gint dot; - XF86VidModeModeLine mode; -#endif Rect *a; guint i; i = client_monitor(self); a = screen_physical_area_monitor(i); -#ifdef VIDMODE - if (i == 0 && /* primary head */ - extensions_vidmode && - XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) && - /* get the mode last so the mode.privsize isnt freed incorrectly */ - XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) { - x += a->x; - y += a->y; - w = mode.hdisplay; - h = mode.vdisplay; - if (mode.privsize) XFree(mode.private); - } else -#endif - { - 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 { @@ -2310,12 +2406,6 @@ static void client_iconify_recursive(ObClient *self, focus_order_remove(self); focus_order_add_new(self); - /* this is here cuz with the VIDMODE extension, the viewport can - change while a fullscreen window is iconic, and when it - uniconifies, it would be nice if it did so to the new position - of the viewport */ - client_reconfigure(self); - changed = TRUE; } } @@ -2323,7 +2413,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 */ @@ -2471,7 +2562,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; @@ -2483,6 +2574,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) { @@ -2509,7 +2611,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) @@ -2581,6 +2684,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 || @@ -2630,6 +2734,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; @@ -2658,6 +2766,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; } @@ -2683,6 +2793,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; } @@ -2722,6 +2834,9 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) if (iconic != self->iconic) client_iconify(self, iconic, FALSE); + if (demands_attention != self->demands_attention) + client_hilite(self, demands_attention); + client_calc_layer(self); client_change_state(self); /* change the hint to reflect these changes */ } @@ -2792,7 +2907,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) { @@ -2803,7 +2918,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; @@ -2813,7 +2928,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, @@ -2823,56 +2938,64 @@ 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) { if (focus_client == self) { #ifdef DEBUG_FOCUS ob_debug("client_unfocus for %lx\n", self->window); #endif - focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING); + focus_fallback(OB_FOCUS_FALLBACK_CLOSED); } } -void client_activate(ObClient *self, gboolean here) +void client_activate(ObClient *self, gboolean here, gboolean user, + Time timestamp) { - /* This check is for the client_list_menu trying to activate - * a closed client. */ - if (!g_list_find(client_list, self)) return; - 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)? + */ + if (!user) + 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) @@ -2972,8 +3095,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 @@ -3192,11 +3315,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; @@ -3213,11 +3355,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) @@ -3227,45 +3369,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) @@ -3275,20 +3403,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) @@ -3296,25 +3416,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) @@ -3324,46 +3437,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) @@ -3373,35 +3471,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: