X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=a51ecfd7c7ed54ce2ba0e094d394b9969cb6f885;hb=eb2a0feb0154e86a1c6c117fd0f6d1a18959b694;hp=b7ed57b5b086e06182320f048250c78a00d6e115;hpb=8ba619d5f15742d08850fe403e99e472ac5cf790;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index b7ed57b5..a51ecfd7 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,8 +1,8 @@ /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- client.c for the Openbox window manager - Copyright (c) 2004 Mikael Magnusson - Copyright (c) 2003 Ben Jansens + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2007 Dana Jansens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -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) { @@ -126,7 +129,7 @@ void client_set_list() windows = NULL; PROP_SETA32(RootWindow(ob_display, ob_screen), - net_client_list, window, (guint32*)windows, size); + net_client_list, window, (gulong*)windows, size); if (windows) g_free(windows); @@ -212,22 +215,27 @@ void client_manage(Window window) XSetWindowAttributes attrib_set; 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)) { + 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 */ } /* 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 */ } @@ -235,7 +243,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); @@ -260,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); - sn_app_started(self->class); + client_calc_layer(self); + + { + Time t = sn_app_started(self->startup_id, self->class); + if (t) self->user_time = t; + } /* update the focus lists, do this before the call to change_state or it can end up in the list twice! */ focus_order_add_new(self); - client_change_state(self); - /* remove the client's border (and adjust re gravity) */ client_toggle_border(self, FALSE); @@ -284,26 +299,32 @@ 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); - grab_server(FALSE); + /* do this after we have a frame.. it uses the frame to help determine the + WM_STATE to apply. */ + client_change_state(self); - client_apply_startup_state(self); + grab_server(FALSE); - stacking_add(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); client_restore_session_stacking(self); /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && - (config_focus_new || client_search_focus_parent(self)) && + /* 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) { @@ -338,43 +359,111 @@ 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; - place_client(self, &x, &y); + transient = place_client(self, &newx, &newy, settings); - /* make sure the window is visible */ - client_find_onscreen(self, &x, &y, + /* make sure the window is visible. */ + client_find_onscreen(self, &newx, &newy, self->frame->area.width, self->frame->area.height, /* non-normal clients has less rules, and - windows that are being restored from a session - do also. we can assume you want it back where - you saved it */ - client_normal(self) && !self->session); + windows that are being restored from a + session do also. we can assume you want + it back where you saved it. Clients saying + they placed themselves are subjected to + harder rules, ones that are placed by + place.c or by the user are allowed partially + off-screen and on xinerama divides (ie, + it is up to the placement routines to avoid + the xinerama divides) */ + transient || + (((self->positioned & PPosition) && + !(self->positioned & USPosition)) && + client_normal(self) && + !self->session)); + } + + /* 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 (x != ox || y != oy) - client_move(self, x, y); + 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 @@ -387,14 +476,12 @@ 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(); - keyboard_grab_for_client(self, TRUE); - mouse_grab_for_client(self, TRUE); - ob_debug("Managed window 0x%lx (%s)\n", window, self->class); } @@ -409,59 +496,60 @@ 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 */ - 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; 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 */ - GSList *it; - for (it = self->group->members; it; it = g_slist_next(it)) if (it->data != self) ((ObClient*)it->data)->transients = @@ -491,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 @@ -528,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) @@ -546,7 +714,7 @@ static void client_restore_session_state(ObClient *self) self->session = it->data; RECT_SET_POINT(self->area, self->session->x, self->session->y); - self->positioned = TRUE; + self->positioned = PPosition; if (self->session->w > 0) self->area.width = self->session->w; if (self->session->h > 0) @@ -613,24 +781,39 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, would be */ /* XXX watch for xinerama dead areas */ - - a = screen_area(self->desktop); + /* This makes sure windows aren't entirely outside of the screen so you + 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)) { - 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; - } - + a = screen_area(self->desktop); + 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, + * when called from client_manage, programs placing themselves are + * forced completely onscreen, while things like + * xterm -geometry resolution-width/2 will work fine. Trying to + * place it completely offscreen will be handled in the above code. + * Sorry for this confused comment, i am tired. */ if (rude) { - /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good. - Java can suck it too. */ - - /* dont let windows map/move into the strut unless they + /* avoid the xinerama monitor divide while we're at it, + * remember to fix the placement stuff to avoid it also and + * then remove this XXX */ + a = screen_area_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; @@ -719,21 +902,25 @@ static void client_toggle_border(ObClient *self, gboolean show) static void client_get_all(ObClient *self) { client_get_area(self); - client_update_transient_for(self); - client_update_wmhints(self); - client_get_startup_id(self); - client_get_desktop(self); - client_get_shaped(self); - client_get_mwm_hints(self); - client_get_type(self);/* this can change the mwmhints for special cases */ /* The transient hint is used to pick a type, but the type can also affect - transiency (dialogs are always made transients). This is Havoc's idea, - but it is needed to make some apps work right (eg tsclient). */ + transiency (dialogs are always made transients of their group if they + have one). This is Havoc's idea, but it is needed to make some apps + work right (eg tsclient). */ client_update_transient_for(self); - + client_get_type(self);/* this can change the mwmhints for special cases */ client_get_state(self); + client_update_transient_for(self); + + client_update_wmhints(self); + client_get_startup_id(self); + client_get_desktop(self);/* uses transient data/group/startup id if a + desktop is not specified */ + client_get_shaped(self); + + client_get_layer(self); /* if layer hasn't been specified, get it from + other sources if possible */ { /* a couple type-based defaults for new windows */ @@ -758,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) @@ -778,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) @@ -825,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; @@ -853,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) @@ -928,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; @@ -1113,7 +1329,7 @@ void client_update_normal_hints(ObClient *self) /* normal windows can't request placement! har har if (!client_normal(self)) */ - self->positioned = !!(size.flags & (PPosition|USPosition)); + self->positioned = (size.flags & (PPosition|USPosition)); if (size.flags & PWinGravity) { self->gravity = size.win_gravity; @@ -1147,7 +1363,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); } } @@ -1157,7 +1373,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 | @@ -1212,9 +1428,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 = 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; + } } } @@ -1286,7 +1508,7 @@ void client_setup_decor_and_functions(ObClient *self) static void client_change_allowed_actions(ObClient *self) { - guint32 actions[9]; + gulong actions[9]; gint num = 0; /* desktop windows are kept on all desktops */ @@ -1323,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; } } @@ -1345,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 */ @@ -1361,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; @@ -1424,99 +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)) + if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) { /* try old x stuff */ - if (!PROP_GETS(self->window, wm_name, locale, &data)) - // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html + if (!(PROP_GETS(self->window, wm_name, locale, &data) + || PROP_GETS(self->window, wm_name, utf8, &data))) { 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"); - - /* 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 (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; } - /* 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; } PROP_SETS(self->window, net_wm_visible_name, data); -no_number: 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)) { + 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 */ - 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); - g_free(data); - data = vdata; - } PROP_SETS(self->window, net_wm_visible_icon_name, data); - self->icon_title = data; } @@ -1651,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; @@ -1697,16 +1847,58 @@ void client_update_icons(ObClient *self) frame_adjust_icon(self->frame); } +void client_update_user_time(ObClient *self, gboolean new_event) +{ + guint32 time; + + if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) { + self->user_time = time; + /* we set this every time, not just when it grows, because in practice + sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes + backward we don't want all windows to stop focusing. we'll just + assume noone is setting times older than the last one, cuz that + would be pretty stupid anyways + However! This is called when a window is mapped to get its user time + but it's an old number, it's not changing it from new user + interaction, so in that case, don't change the last user time. + */ + if (new_event) + client_last_user_time = time; + + /* + ob_debug("window %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) { - guint32 state[2]; - guint32 netstate[11]; + 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; @@ -1728,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); } @@ -1788,9 +1980,8 @@ static ObStackingLayer calc_layer(ObClient *self) else if (self->type == OB_CLIENT_TYPE_DESKTOP) l = OB_STACKING_LAYER_DESKTOP; else if (self->type == OB_CLIENT_TYPE_DOCK) { - if (self->above) l = OB_STACKING_LAYER_DOCK_ABOVE; - else if (self->below) l = OB_STACKING_LAYER_DOCK_BELOW; - else l = OB_STACKING_LAYER_DOCK_NORMAL; + if (self->below) l = OB_STACKING_LAYER_NORMAL; + else l = OB_STACKING_LAYER_ABOVE; } else if (self->above) l = OB_STACKING_LAYER_ABOVE; else if (self->below) l = OB_STACKING_LAYER_BELOW; @@ -1800,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)); @@ -1822,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) @@ -1864,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) { @@ -1879,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) { @@ -1889,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; @@ -1899,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: @@ -1922,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 @@ -1939,75 +2161,58 @@ 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 */ /* 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); + i = screen_find_monitor(&desired_area); 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 { 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 */ @@ -2031,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; @@ -2164,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; @@ -2172,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 */ @@ -2222,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 @@ -2242,27 +2470,15 @@ 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); - /* 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; } } @@ -2270,25 +2486,25 @@ 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 */ + /* 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; @@ -2314,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; @@ -2376,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); } @@ -2418,7 +2622,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; @@ -2430,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) { @@ -2456,7 +2674,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) @@ -2467,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) @@ -2527,6 +2755,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) gboolean max_horz = self->max_horz; 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 || @@ -2562,6 +2792,10 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action = self->skip_pager ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; + else if (state == prop_atoms.net_wm_state_hidden) + action = self->iconic ? + prop_atoms.net_wm_state_remove : + prop_atoms.net_wm_state_add; else if (state == prop_atoms.net_wm_state_fullscreen) action = fullscreen ? prop_atoms.net_wm_state_remove : @@ -2572,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; @@ -2590,6 +2828,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) self->skip_taskbar = TRUE; } else if (state == prop_atoms.net_wm_state_skip_pager) { self->skip_pager = TRUE; + } else if (state == prop_atoms.net_wm_state_hidden) { + iconic = TRUE; } else if (state == prop_atoms.net_wm_state_fullscreen) { fullscreen = TRUE; } else if (state == prop_atoms.net_wm_state_above) { @@ -2598,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; } @@ -2615,12 +2857,16 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) self->skip_taskbar = FALSE; } else if (state == prop_atoms.net_wm_state_skip_pager) { self->skip_pager = FALSE; + } else if (state == prop_atoms.net_wm_state_hidden) { + iconic = FALSE; } else if (state == prop_atoms.net_wm_state_fullscreen) { fullscreen = FALSE; } else if (state == prop_atoms.net_wm_state_above) { 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; } @@ -2630,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) @@ -2657,16 +2903,20 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) transients needs to change */ client_raise(self); } - client_calc_layer(self); + if (iconic != self->iconic) + client_iconify(self, iconic, FALSE); + + 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; } @@ -2710,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 */ @@ -2718,16 +2973,11 @@ 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, - event_lasttime); + XSetInputFocus(ob_display, self->window, RevertToPointerRoot, + event_curtime); } if (self->focus_notify) { @@ -2738,7 +2988,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; @@ -2748,7 +2998,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, @@ -2758,53 +3008,68 @@ gboolean client_focus(ObClient *self) return TRUE; } -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_UNFOCUSING); + focus_fallback(FALSE); } } -void client_activate(ObClient *self, gboolean here) +void client_activate(ObClient *self, gboolean here, gboolean user) { - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE); - if (self->iconic) - client_iconify(self, FALSE, here); - if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) { - if (here) - client_set_desktop(self, screen_desktop, FALSE); - else - screen_set_desktop(self->desktop); - } else if (!self->frame->visible) - /* if its not visible for other reasons, then don't mess - with it */ - return; - if (self->shaded) - client_shade(self, FALSE); + /* XXX do some stuff here if user is false to determine if we really want + to activate it or not (a parent or group member is currently + active)? + */ + ob_debug("Want to activate window 0x%x with time %u (last time %u), " + "source=%s\n", + self->window, 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) @@ -2904,8 +3169,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 @@ -2990,41 +3255,40 @@ void client_set_undecorated(ObClient *self, gboolean undecorated) if (self->undecorated != undecorated) { self->undecorated = undecorated; client_setup_decor_and_functions(self); + /* Make sure the client knows it might have moved. Maybe there is a + * better way of doing this so only one client_configure is sent, but + * since 125 of these are sent per second when moving the window (with + * user = FALSE) i doubt it matters much. + */ + client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y, + self->area.width, self->area.height, TRUE, TRUE); client_change_state(self); /* reflect this in the state hints */ } } 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); @@ -3032,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) @@ -3114,88 +3377,100 @@ 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; + gint dest, monitor_dest; gint my_edge_start, my_edge_end, my_offset; GList *it; - Rect *a; + Rect *a, *monitor; if(!client_list) return -1; a = screen_area(c->desktop); + monitor = screen_area_monitor(c->desktop, client_monitor(c)); switch(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; - - for(it = client_list; it; it = g_list_next(it)) { + 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) + dest = monitor_dest; + + for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { 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; + 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; - - for(it = client_list; it; it = g_list_next(it)) { + 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) + dest = monitor_dest; + + for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { 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; + 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) @@ -3203,98 +3478,75 @@ 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; - - for(it = client_list; it; it = g_list_next(it)) { + 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) + dest = monitor_dest; + + for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { 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; + 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; - - for(it = client_list; it; it = g_list_next(it)) { + 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) + dest = monitor_dest; + + for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { 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; + 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: @@ -3304,6 +3556,7 @@ gint client_directional_edge_search(ObClient *c, ObDirection dir) /* not implemented */ default: g_assert_not_reached(); + dest = 0; /* suppress warning */ } return dest; }