X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=0a0d1ffc6765f886a6a78ab85f8e817798942330;hb=6a8a8531bad25c148b4c62263d16c0996a21ca6e;hp=f8ebd1361cf29681eb318f9dc880b5ed2d1e931e;hpb=8923bd7026755ecc35898c5573fa9c6670a8c56b;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index f8ebd136..0a0d1ffc 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,5 +1,5 @@ /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- - + client.c for the Openbox window manager Copyright (c) 2006 Mikael Magnusson Copyright (c) 2003-2007 Dana Jansens @@ -24,6 +24,7 @@ #include "xerror.h" #include "screen.h" #include "moveresize.h" +#include "ping.h" #include "place.h" #include "prop.h" #include "extensions.h" @@ -32,7 +33,6 @@ #include "event.h" #include "grab.h" #include "focus.h" -#include "propwin.h" #include "stacking.h" #include "openbox.h" #include "group.h" @@ -41,11 +41,16 @@ #include "keyboard.h" #include "mouse.h" #include "render/render.h" +#include "gettext.h" #ifdef HAVE_UNISTD_H # include #endif +#ifdef HAVE_SIGNAL_H +# include /* for kill() */ +#endif + #include #include @@ -75,6 +80,10 @@ static void client_get_state(ObClient *self); static void client_get_shaped(ObClient *self); static void client_get_mwm_hints(ObClient *self); static void client_get_colormap(ObClient *self); +static void client_set_desktop_recursive(ObClient *self, + guint target, + gboolean donthide, + gboolean dontraise); static void client_change_allowed_actions(ObClient *self); static void client_change_state(ObClient *self); static void client_change_wm_state(ObClient *self); @@ -88,11 +97,13 @@ static void client_update_transient_tree(ObClient *self, gboolean oldgtran, gboolean newgtran, ObClient* oldparent, ObClient *newparent); -static void client_present(ObClient *self, gboolean here, gboolean raise); +static void client_present(ObClient *self, gboolean here, gboolean raise, + gboolean unshade); static GSList *client_search_all_top_parents_internal(ObClient *self, gboolean bylayer, ObStackingLayer layer); static void client_call_notifies(ObClient *self, GSList *list); +static void client_ping_event(ObClient *self, gboolean dead); void client_startup(gboolean reconfig) @@ -140,7 +151,7 @@ void client_remove_destroy_notify(ObClientCallback func) } } -void client_set_list() +void client_set_list(void) { Window *windows, *win_it; GList *it; @@ -164,7 +175,7 @@ void client_set_list() stacking_set_list(); } -void client_manage_all() +void client_manage_all(void) { guint i, j, nchild; Window w, *children; @@ -212,8 +223,9 @@ void client_manage(Window window) XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; - gint placex, placey, placew, placeh; gboolean transient = FALSE; + Rect place, *monitor; + Time launch_time, map_time; grab_server(TRUE); @@ -236,7 +248,7 @@ void client_manage(Window window) grab_server(FALSE); return; /* don't manage it */ } - + /* is the window a docking app */ if ((wmhint = XGetWMHints(ob_display, window))) { if ((wmhint->flags & StateHint) && @@ -252,6 +264,8 @@ void client_manage(Window window) ob_debug("Managing window: 0x%lx\n", window); + map_time = event_get_server_time(); + /* choose the events we want to receive on the CLIENT window */ attrib_set.event_mask = CLIENT_EVENTMASK; attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; @@ -268,11 +282,13 @@ void client_manage(Window window) self->wmstate = WithdrawnState; /* make sure it gets updated first time */ self->gravity = NorthWestGravity; self->desktop = screen_num_desktops; /* always an invalid value */ - self->user_time = focus_client ? focus_client->user_time : CurrentTime; /* get all the stuff off the window */ client_get_all(self, TRUE); + ob_debug("Window type: %d\n", self->type); + ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0); + /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically */ XChangeSaveSet(ob_display, window, SetModeInsert); @@ -296,10 +312,8 @@ void client_manage(Window window) /* now we have all of the window's information so we can set this up */ client_setup_decor_and_functions(self, FALSE); - { - Time t = sn_app_started(self->startup_id, self->class); - if (t) self->user_time = t; - } + /* tell startup notification that this app started */ + launch_time = sn_app_started(self->startup_id, self->class, self->name); /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ @@ -319,7 +333,7 @@ void client_manage(Window window) client_search_focus_tree_full(self)) && /* this checks for focus=false for the window */ (!settings || settings->focus != 0) && - focus_valid_target(self, FALSE, TRUE, FALSE, FALSE)) + focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE)) { activate = TRUE; } @@ -333,10 +347,8 @@ void client_manage(Window window) frame_adjust_client_area(self->frame); /* where the frame was placed is where the window was originally */ - placex = self->area.x; - placey = self->area.y; - placew = self->area.width; - placeh = self->area.height; + place = self->area; + monitor = screen_physical_area_monitor(screen_find_monitor(&place)); /* figure out placement for the window if the window is new */ if (ob_state() == OB_STATE_RUNNING) { @@ -346,7 +358,7 @@ void client_manage(Window window) (self->positioned == USPosition ? "user specified" : (self->positioned == (PPosition | USPosition) ? "program + user specified" : - "BADNESS !?")))), placex, placey); + "BADNESS !?")))), place.x, place.y); ob_debug("Sized: %s @ %d %d\n", (!self->sized ? "no" : @@ -354,14 +366,15 @@ void client_manage(Window window) (self->sized == USSize ? "user specified" : (self->sized == (PSize | USSize) ? "program + user specified" : - "BADNESS !?")))), placew, placeh); + "BADNESS !?")))), place.width, place.height); /* splash screens are also returned as TRUE for transient, and so will be forced on screen below */ - transient = place_client(self, &placex, &placey, settings); + transient = place_client(self, &place.x, &place.y, settings); /* make sure the window is visible. */ - client_find_onscreen(self, &placex, &placey, placew, placeh, + client_find_onscreen(self, &place.x, &place.y, + place.width, place.height, /* non-normal clients has less rules, and windows that are being restored from a session do also. we can assume you want @@ -378,9 +391,16 @@ void client_manage(Window window) */ ob_state() == OB_STATE_RUNNING && (transient || - (!(self->positioned & USPosition) && + (!((self->positioned & USPosition) || + (settings && settings->pos_given)) && client_normal(self) && - !self->session))); + !self->session && + /* don't move oldschool fullscreen windows to + fit inside the struts (fixes Acroread, which + makes its fullscreen window fit the screen + but it is not USSize'd or USPosition'd) */ + !(self->decorations == 0 && + RECT_EQUAL(place, *monitor))))); } /* if the window isn't user-sized, then make it fit inside @@ -394,36 +414,36 @@ void client_manage(Window window) */ if (ob_state() == OB_STATE_RUNNING && (transient || - (!(self->sized & USSize) && + (!(self->sized & USSize || self->positioned & USPosition) && client_normal(self) && - !self->session))) + !self->session && + /* don't shrink oldschool fullscreen windows to fit inside the + struts (fixes Acroread, which makes its fullscreen window + fit the screen but it is not USSize'd or USPosition'd) */ + !(self->decorations == 0 && RECT_EQUAL(place, *monitor))))) { - Rect placer; + Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place); - RECT_SET(placer, placex, placey, placew, placeh); - frame_rect_to_frame(self->frame, &placer); + /* get the size of the frame */ + place.width += self->frame->size.left + self->frame->size.right; + place.height += self->frame->size.top + self->frame->size.bottom; - Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &placer); + /* fit the window inside the area */ + place.width = MIN(place.width, a->width); + place.height = MIN(place.height, a->height); - /* shrink by the frame's area */ - a->width -= self->frame->size.left + self->frame->size.right; - a->height -= self->frame->size.top + self->frame->size.bottom; + ob_debug("setting window size to %dx%d\n", place.width, place.height); - /* fit the window inside the area */ - if (placew > a->width || self->area.height > a->height) { - placew = MIN(self->area.width, a->width); - placeh = MIN(self->area.height, a->height); + /* get the size of the client back */ + place.width -= self->frame->size.left + self->frame->size.right; + place.height -= self->frame->size.top + self->frame->size.bottom; - ob_debug("setting window size to %dx%d\n", - self->area.width, self->area.height); - } g_free(a); } - ob_debug("placing window 0x%x at %d, %d with size %d x %d. " "some restrictions may apply\n", - self->window, placex, placey, placew, placeh); + self->window, place.x, place.y, place.width, place.height); if (self->session) ob_debug(" but session requested %d, %d %d x %d instead, " "overriding\n", @@ -435,25 +455,42 @@ void client_manage(Window window) this also places the window */ - client_apply_startup_state(self, placex, placey, placew, placeh); + client_apply_startup_state(self, place.x, place.y, + place.width, place.height); + g_free(monitor); + monitor = NULL; + + ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n", + activate ? "yes" : "no"); if (activate) { - guint32 last_time = focus_client ? - focus_client->user_time : CurrentTime; + gboolean raise = FALSE; /* This is focus stealing prevention */ ob_debug_type(OB_DEBUG_FOCUS, - "Want to focus new window 0x%x with time %u " - "(last time %u)\n", - self->window, self->user_time, last_time); + "Want to focus new window 0x%x at time %u " + "launched at %u (last user interaction time %u)\n", + self->window, map_time, launch_time, + event_last_user_time); + + if (menu_frame_visible || moveresize_in_progress) { + activate = FALSE; + raise = TRUE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because the user is inside " + "an Openbox menu or is move/resizing a window and " + "we don't want to interrupt them\n"); + } /* if it's on another desktop */ - if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) - && /* the timestamp is from before you changed desktops */ - self->user_time && screen_desktop_user_time && - !event_time_after(self->user_time, screen_desktop_user_time)) + else if (!(self->desktop == screen_desktop || + self->desktop == DESKTOP_ALL) && + /* the timestamp is from before you changed desktops */ + launch_time && screen_desktop_user_time && + !event_time_after(launch_time, screen_desktop_user_time)) { activate = FALSE; + raise = TRUE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because its on another " "desktop\n"); @@ -462,26 +499,25 @@ void client_manage(Window window) else if (focus_client && client_search_focus_tree_full(self) == NULL && client_search_focus_group_full(self) == NULL) { - /* If time stamp is old, don't steal focus */ - if (self->user_time && last_time && - !event_time_after(self->user_time, last_time)) + /* If the user is working in another window right now, then don't + steal focus */ + if (event_last_user_time && launch_time && + event_time_after(event_last_user_time, launch_time) && + event_last_user_time != launch_time && + event_time_after(event_last_user_time, + map_time - OB_EVENT_USER_TIME_DELAY)) { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because the time is " - "too old\n"); + "Not focusing the window because the user is " + "working in another window\n"); } - /* If its a transient (and parents aren't focused) and the time - is ambiguous (either the current focus target doesn't have - a timestamp, or they are the same (we probably inherited it - from them) */ - else if (client_has_parent(self) && - (!last_time || self->user_time == last_time)) - { + /* If its a transient (and its parents aren't focused) */ + else if (client_has_parent(self)) { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because it is a " - "transient, and the time is very ambiguous\n"); + "transient, and its relatives aren't focused\n"); } /* Don't steal focus from globally active clients. I stole this idea from KWin. It seems nice. @@ -498,20 +534,33 @@ void client_manage(Window window) anyway */ else if (client_focus_target(self) != self) { activate = FALSE; + raise = TRUE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because another window " "would get the focus anyway\n"); } + else if (!(self->desktop == screen_desktop || + self->desktop == DESKTOP_ALL)) + { + activate = FALSE; + raise = TRUE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because it is on " + "another desktop and no relatives are focused "); + } } if (!activate) { ob_debug_type(OB_DEBUG_FOCUS, - "Focus stealing prevention activated for %s with " - "time %u (last time %u)\n", - self->title, self->user_time, last_time); + "Focus stealing prevention activated for %s at " + "time %u (last user interactioon time %u)\n", + self->title, map_time, event_last_user_time); /* if the client isn't focused, then hilite it so the user knows it is there */ client_hilite(self, TRUE); + /* we may want to raise it even tho we're not activating it */ + if (raise && !client_restore_session_stacking(self)) + stacking_raise(CLIENT_AS_WINDOW(self)); } } else { @@ -544,7 +593,7 @@ void client_manage(Window window) if (activate) { gboolean stacked = client_restore_session_stacking(self); - client_present(self, FALSE, !stacked); + client_present(self, FALSE, !stacked, TRUE); } /* add to client list/map */ @@ -558,6 +607,15 @@ void client_manage(Window window) /* update the list hints */ client_set_list(); + /* watch for when the application stops responding. only do this for + normal windows, i.e. windows which have titlebars and close buttons + and things like that. + we don't need to stop pinging on unmanage, because it will be handled + automatically by the destroy callback! + */ + if (self->ping && client_normal(self)) + ping_start(self, client_ping_event); + /* free the ObAppSettings shallow copy */ g_free(settings); @@ -592,7 +650,7 @@ ObClient *client_fake_manage(Window window) frame_adjust_area(self->frame, FALSE, TRUE, TRUE); ob_debug("gave extents left %d right %d top %d bottom %d\n", - self->frame->size.left, self->frame->size.right, + self->frame->size.left, self->frame->size.right, self->frame->size.top, self->frame->size.bottom); /* free the ObAppSettings shallow copy */ @@ -601,7 +659,7 @@ ObClient *client_fake_manage(Window window) return self; } -void client_unmanage_all() +void client_unmanage_all(void) { while (client_list != NULL) client_unmanage(client_list->data); @@ -639,9 +697,6 @@ void client_unmanage(ObClient *self) /* remove the window from our save set */ XChangeSaveSet(ob_display, self->window, SetModeDelete); - /* kill the property windows */ - propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self); - /* update the focus lists */ focus_order_remove(self); if (client_focused(self)) { @@ -727,6 +782,11 @@ void client_unmanage(ObClient *self) XMapWindow(ob_display, self->window); } + /* these should not be left on the window ever. other window managers + don't necessarily use them and it will mess them up (like compiz) */ + PROP_ERASE(self->window, net_wm_visible_name); + PROP_ERASE(self->window, net_wm_visible_icon_name); + /* update the list hints */ client_set_list(); @@ -932,7 +992,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, frame_rect_to_frame(self->frame, &desired); /* get where the frame would be */ - frame_client_gravity(self->frame, x, y, w, h); + frame_client_gravity(self->frame, x, y); /* get the requested size of the window with decorations */ fw = self->frame->size.left + w + self->frame->size.right; @@ -979,10 +1039,15 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, for (i = 0; i < screen_num_monitors; ++i) { Rect *a; - if (!screen_physical_area_monitor_contains(i, &desired)) - continue; + if (!screen_physical_area_monitor_contains(i, &desired)) { + if (i < screen_num_monitors - 1) + continue; - a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired); + /* the window is not inside any monitor! so just use the first + one */ + a = screen_area(self->desktop, 0, NULL); + } else + a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired); /* This makes sure windows aren't entirely outside of the screen so you can't see them at all. @@ -999,7 +1064,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, if (!self->strut.left && *x + fw*9/10 - 1 < a->x) *x = a->x - fw*9/10; if (!self->strut.top && *y + fh*9/10 - 1 < a->y) - *y = a->y - fw*9/10; + *y = a->y - fh*9/10; } /* This here doesn't let windows even a pixel outside the @@ -1020,7 +1085,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, } /* get where the client should be */ - frame_frame_gravity(self->frame, x, y, w, h); + frame_frame_gravity(self->frame, x, y); return ox != *x || oy != *y; } @@ -1068,7 +1133,7 @@ static void client_get_all(ObClient *self, gboolean real) if (self->type == OB_CLIENT_TYPE_DESKTOP) self->desktop = DESKTOP_ALL; } - + #ifdef SYNC client_update_sync_request_counter(self); #endif @@ -1076,9 +1141,6 @@ static void client_get_all(ObClient *self, gboolean real) client_get_colormap(self); client_update_strut(self); client_update_icons(self); - client_update_user_time_window(self); - if (!self->user_time_window) /* check if this would have been called */ - client_update_user_time(self); client_update_icon_geometry(self); } @@ -1094,7 +1156,7 @@ static void client_get_area(ObClient *self) { XWindowAttributes wattrib; Status ret; - + ret = XGetWindowAttributes(ob_display, self->window, &wattrib); g_assert(ret != BadWindow); @@ -1115,7 +1177,7 @@ static void client_get_desktop(ObClient *self) self->desktop = screen_num_desktops - 1; else self->desktop = d; - ob_debug("client requested desktop 0x%x\n", self->desktop); + ob_debug("client requested desktop 0x%x\n", self->desktop); } else { GSList *it; gboolean first = TRUE; @@ -1162,7 +1224,7 @@ static void client_get_state(ObClient *self) { guint32 *state; guint num; - + if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) { gulong i; for (i = 0; i < num; ++i) { @@ -1245,7 +1307,7 @@ void client_update_transient_for(ObClient *self) self->transient_for_group, trangroup, client_direct_parent(self), target); self->transient_for_group = trangroup; - + } static void client_update_transient_tree(ObClient *self, @@ -1277,88 +1339,45 @@ static void client_update_transient_tree(ObClient *self, oldgtran == newgtran && oldparent == newparent) return; - /** Remove the client from the transient tree wherever it has changed **/ - - /* If the window is becoming a direct transient for a window in its group - then any group transients which were our children and are now becoming - our parents need to stop being our children. - - Group transients can't be children of group transients already, but - we could have any number of direct parents above up, any of which could - be transient for the group, and we need to remove it from our children. - */ - if (!oldgtran && oldparent != newparent && newparent != NULL && - newgroup != NULL && newgroup == oldgroup && - client_normal(newparent)) - { - ObClient *look = client_search_top_direct_parent(newparent); - self->transients = g_slist_remove(self->transients, look); - look->parents = g_slist_remove(look->parents, self); - } - - - /* If the group changed, or if we are just becoming transient for the - group, then we need to remove any old group transient windows - from our children. But if we were already transient for the group, then - other group transients are not our children. */ - if ((oldgroup != newgroup || (newgtran && oldgtran != newgtran)) && - oldgroup != NULL && !oldgtran) - { - for (it = self->transients; it; it = next) { - next = g_slist_next(it); - c = it->data; - if (c->group == oldgroup && client_normal(self)) { - self->transients = g_slist_delete_link(self->transients, it); - c->parents = g_slist_remove(c->parents, self); - } - } - } + /** Remove the client from the transient tree **/ - /* If we used to be transient for a group and now we are not, or we're - transient for a new group, then we need to remove ourselves from all - our ex-parents */ - if (oldgtran && (oldgroup != newgroup || oldgtran != newgtran)) - { - for (it = self->parents; it; it = next) { - next = g_slist_next(it); - c = it->data; - if (!c->transient_for_group && client_normal(c)) { - c->transients = g_slist_remove(c->transients, self); - self->parents = g_slist_delete_link(self->parents, it); - } - } + for (it = self->transients; it; it = next) { + next = g_slist_next(it); + c = it->data; + self->transients = g_slist_delete_link(self->transients, it); + c->parents = g_slist_remove(c->parents, self); } - /* If we used to be transient for a single window and we are no longer - transient for it, then we need to remove ourself from its children */ - else if (oldparent && oldparent != newparent && - client_normal(oldparent)) - { - oldparent->transients = g_slist_remove(oldparent->transients, self); - self->parents = g_slist_remove(self->parents, oldparent); + for (it = self->parents; it; it = next) { + next = g_slist_next(it); + c = it->data; + self->parents = g_slist_delete_link(self->parents, it); + c->transients = g_slist_remove(c->transients, self); } - /** Re-add the client to the transient tree wherever it has changed **/ + /** Re-add the client to the transient tree **/ - /* If we're now transient for a group and we weren't transient for it - before then we need to add ourselves to all our new parents */ - if (newgtran && (oldgroup != newgroup || oldgtran != newgtran)) - { - for (it = oldgroup->members; it; it = g_slist_next(it)) { + /* If we're transient for a group then we need to add ourselves to all our + parents */ + if (newgtran) { + for (it = newgroup->members; it; it = g_slist_next(it)) { c = it->data; - if (c != self && !c->transient_for_group && client_normal(c)) + if (c != self && + !client_search_top_direct_parent(c)->transient_for_group && + client_normal(c)) { c->transients = g_slist_prepend(c->transients, self); self->parents = g_slist_prepend(self->parents, c); } } } - /* If we are now transient for a single window which we weren't before, - we need to add ourselves to its children + + /* If we are now transient for a single window we need to add ourselves to + its children WARNING: Cyclical transient ness is possible if two windows are transient for eachother. */ - else if (newparent && newparent != oldparent && + else if (newparent && /* don't make ourself its child if it is already our child */ !client_is_direct_child(self, newparent) && client_normal(newparent)) @@ -1367,9 +1386,8 @@ static void client_update_transient_tree(ObClient *self, self->parents = g_slist_prepend(self->parents, newparent); } - /* If the group changed then we need to add any new group transient - windows to our children. But if we're transient for the group, then - other group transients are not our children. + /* Add any group transient windows to our children. But if we're transient + for the group, then other group transients are not our children. WARNING: Cyclical transient-ness is possible. For e.g. if: A is transient for the group @@ -1377,7 +1395,9 @@ static void client_update_transient_tree(ObClient *self, C is transient for B A can't be transient for C or we have a cycle */ - if (oldgroup != newgroup && newgroup != NULL && !newgtran && + if (!newgtran && newgroup && + (!newparent || + !client_search_top_direct_parent(newparent)->transient_for_group) && client_normal(self)) { for (it = newgroup->members; it; it = g_slist_next(it)) { @@ -1391,6 +1411,20 @@ static void client_update_transient_tree(ObClient *self, } } } + + /** If we change our group transient-ness, our children change their + effect group transient-ness, which affects how they relate to other + group windows **/ + + for (it = self->transients; it; it = g_slist_next(it)) { + c = it->data; + if (!c->transient_for_group) + client_update_transient_tree(c, c->group, c->group, + c->transient_for_group, + c->transient_for_group, + client_direct_parent(c), + client_direct_parent(c)); + } } static void client_get_mwm_hints(ObClient *self) @@ -1419,7 +1453,7 @@ void client_get_type_and_transientness(ObClient *self) self->type = -1; self->transient = FALSE; - + if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) { /* use the first value that we know about in the array */ for (i = 0; i < num; ++i) { @@ -1455,7 +1489,7 @@ void client_get_type_and_transientness(ObClient *self) if (XGetTransientForHint(ob_display, self->window, &t)) self->transient = TRUE; - + if (self->type == (ObClientType) -1) { /*the window type hint was not set, which means we either classify ourself as a normal window or a dialog, depending on if we are a @@ -1493,8 +1527,12 @@ void client_update_protocols(ObClient *self) /* if this protocol is requested, then the window will be notified whenever we want it to receive focus */ self->focus_notify = TRUE; + else if (proto[i] == prop_atoms.net_wm_ping) + /* if this protocol is requested, then the window will allow + pings to determine if it is still alive */ + self->ping = TRUE; #ifdef SYNC - else if (proto[i] == prop_atoms.net_wm_sync_request) + else if (proto[i] == prop_atoms.net_wm_sync_request) /* if this protocol is requested, then resizing the window will be synchronized between the frame and the client */ @@ -1517,7 +1555,7 @@ void client_update_sync_request_counter(ObClient *self) } #endif -void client_get_colormap(ObClient *self) +static void client_get_colormap(ObClient *self) { XWindowAttributes wa; @@ -1574,13 +1612,13 @@ void client_update_normal_hints(ObClient *self) if (size.flags & PMinSize) SIZE_SET(self->min_size, size.min_width, size.min_height); - + if (size.flags & PMaxSize) SIZE_SET(self->max_size, size.max_width, size.max_height); - + if (size.flags & PBaseSize) SIZE_SET(self->base_size, size.base_width, size.base_height); - + if (size.flags & PResizeInc && size.width_inc && size.height_inc) SIZE_SET(self->size_inc, size.width_inc, size.height_inc); @@ -1627,11 +1665,16 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) switch (self->type) { case OB_CLIENT_TYPE_NORMAL: /* normal windows retain all of the possible decorations and - functionality, and are the only windows that you can fullscreen */ + functionality, and can be fullscreen */ self->functions |= OB_CLIENT_FUNC_FULLSCREEN; break; case OB_CLIENT_TYPE_DIALOG: + /* sometimes apps make dialog windows fullscreen for some reason (for + e.g. kpdf does this..) */ + self->functions |= OB_CLIENT_FUNC_FULLSCREEN; + break; + case OB_CLIENT_TYPE_UTILITY: /* these windows don't have anything added or removed by default */ break; @@ -1814,7 +1857,7 @@ void client_update_wmhints(ObClient *self) /* assume a window takes input if it doesnt specify */ self->can_focus = TRUE; - + if ((hints = XGetWMHints(ob_display, self->window)) != NULL) { gboolean ur; @@ -1901,17 +1944,17 @@ void client_update_title(ObClient *self) gchar *visible = NULL; g_free(self->title); - + /* try netwm */ if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) { /* try old x stuff */ if (!(PROP_GETS(self->window, wm_name, locale, &data) || PROP_GETS(self->window, wm_name, utf8, &data))) { if (self->transient) { - /* - GNOME alert windows are not given titles: - http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html - */ + /* + GNOME alert windows are not given titles: + http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html + */ data = g_strdup(""); } else data = g_strdup("Unnamed Window"); @@ -1924,6 +1967,15 @@ void client_update_title(ObClient *self) } else visible = data; + if (self->not_responding) { + data = visible; + if (self->close_tried_term) + visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); + else + visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); + g_free(data); + } + PROP_SETS(self->window, net_wm_visible_name, visible); self->title = visible; @@ -1947,6 +1999,15 @@ void client_update_title(ObClient *self) } else visible = data; + if (self->not_responding) { + data = visible; + if (self->close_tried_term) + visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); + else + visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); + g_free(data); + } + PROP_SETS(self->window, net_wm_visible_icon_name, visible); self->icon_title = visible; } @@ -2029,7 +2090,7 @@ void client_update_icons(ObClient *self) } self->icons = g_new(ObClientIcon, self->nicons); - + /* store the icons */ i = 0; for (j = 0; j < self->nicons; ++j) { @@ -2084,8 +2145,11 @@ void client_update_icons(ObClient *self) /* set the default icon onto the window in theory, this could be a race, but if a window doesn't set an icon or removes it entirely, it's not very likely it is going to set one - right away afterwards */ - if (self->nicons == 0) { + right away afterwards + + if it has parents, then one of them will have an icon already + */ + if (self->nicons == 0 && !self->parents) { RrPixel32 *icon = ob_rr_theme->def_win_icon; gulong *data; @@ -2104,62 +2168,6 @@ void client_update_icons(ObClient *self) frame_adjust_icon(self->frame); } -void client_update_user_time(ObClient *self) -{ - guint32 time; - gboolean got = FALSE; - - if (self->user_time_window) - got = PROP_GET32(self->user_time_window, - net_wm_user_time, cardinal, &time); - if (!got) - got = PROP_GET32(self->window, net_wm_user_time, cardinal, &time); - - if (got) { - /* 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 - */ - self->user_time = time; - - /*ob_debug("window %s user time %u\n", self->title, time);*/ - } -} - -void client_update_user_time_window(ObClient *self) -{ - guint32 w; - - if (!PROP_GET32(self->window, net_wm_user_time_window, window, &w)) - w = None; - - if (w != self->user_time_window) { - /* remove the old window */ - propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self); - self->user_time_window = None; - - if (self->group && self->group->leader == w) { - ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its " - "_NET_WM_USER_TYPE_WINDOW to its group leader\n"); - /* do it anyways..? */ - } - else if (w == self->window) { - ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its " - "_NET_WM_USER_TIME_WINDOW to itself\n"); - w = None; /* don't do it */ - } - - /* add the new window */ - propwin_add(w, OB_PROPWIN_USER_TIME, self); - self->user_time_window = w; - - /* and update from it */ - client_update_user_time(self); - } -} - void client_update_icon_geometry(ObClient *self) { guint num; @@ -2260,6 +2268,7 @@ static void client_get_session_ids(ObClient *self) if (got) { gchar localhost[128]; + guint32 pid; gethostname(localhost, 127); localhost[127] = '\0'; @@ -2267,6 +2276,11 @@ static void client_get_session_ids(ObClient *self) self->client_machine = s; else g_free(s); + + /* see if it has the PID set too (the PID requires that the + WM_CLIENT_MACHINE be set) */ + if (PROP_GET32(self->window, net_wm_pid, cardinal, &pid)) + self->pid = pid; } } @@ -2457,7 +2471,7 @@ gboolean client_should_show(ObClient *self) return FALSE; if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) return TRUE; - + return FALSE; } @@ -2601,7 +2615,7 @@ static void client_apply_startup_state(ObClient *self, client_shade(self, TRUE); if (demands_attention) client_hilite(self, TRUE); - + if (max_vert && max_horz) client_maximize(self, TRUE, 0); else if (max_vert) @@ -2609,11 +2623,18 @@ static void client_apply_startup_state(ObClient *self, else if (max_horz) client_maximize(self, TRUE, 1); - /* if the window hasn't been configured yet, then do so now */ - if (!fullscreen && !max_vert && !max_horz) { - self->area = oldarea; - client_configure(self, x, y, w, h, FALSE, TRUE, FALSE); - } + /* if the window hasn't been configured yet, then do so now, in fact the + x,y,w,h may _not_ be the same as the area rect, which can end up + meaning that the client isn't properly moved/resized by the fullscreen + function + pho can cause this because it maps at size of the screen but not 0,0 + so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't). + then fullscreen'ing makes it go to 0,0 which it thinks it already is at + cuz thats where the pre-fullscreen will be. however the actual area is + not, so this needs to be called even if we have fullscreened/maxed + */ + self->area = oldarea; + client_configure(self, x, y, w, h, FALSE, TRUE, FALSE); /* set the desktop hint, to make sure that it always exists */ PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); @@ -2698,7 +2719,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, frame_adjust_area(self->frame, FALSE, TRUE, TRUE); /* gets the frame's position */ - frame_client_gravity(self->frame, x, y, *w, *h); + frame_client_gravity(self->frame, x, y); /* these positions are frame positions, not client positions */ @@ -2745,7 +2766,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, } /* gets the client's position */ - frame_frame_gravity(self->frame, x, y, *w, *h); + frame_frame_gravity(self->frame, x, y); /* work within the prefered sizes given by the window */ if (!(*w == self->area.width && *h == self->area.height)) { @@ -2796,7 +2817,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, /* you cannot resize to nothing */ if (basew + *w < 1) *w = 1 - basew; if (baseh + *h < 1) *h = 1 - baseh; - + /* save the logical size */ *logicalw = incw > 1 ? *w : *w + basew; *logicalh = inch > 1 ? *h : *h + baseh; @@ -3025,6 +3046,9 @@ void client_fullscreen(ObClient *self, gboolean fs) RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0); } + ob_debug("Window %s going fullscreen (%d)\n", + self->title, self->fullscreen); + client_setup_decor_and_functions(self, FALSE); client_move_resize(self, x, y, w, h); @@ -3108,7 +3132,7 @@ void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk, void client_maximize(ObClient *self, gboolean max, gint dir) { gint x, y, w, h; - + g_assert(dir == 0 || dir == 1 || dir == 2); if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */ @@ -3186,41 +3210,58 @@ void client_shade(ObClient *self, gboolean shade) frame_adjust_area(self->frame, FALSE, TRUE, FALSE); } -void client_close(ObClient *self) +static void client_ping_event(ObClient *self, gboolean dead) { - XEvent ce; + self->not_responding = dead; + client_update_title(self); + if (!dead) { + /* try kill it nicely the first time again, if it started responding + at some point */ + self->close_tried_term = FALSE; + } +} + +void client_close(ObClient *self) +{ if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; /* in the case that the client provides no means to requesting that it close, we just kill it */ if (!self->delete_window) + /* don't use client_kill(), we should only kill based on PID in + response to a lack of PING replies */ + XKillClient(ob_display, self->window); + else if (self->not_responding) client_kill(self); - - /* - XXX: itd be cool to do timeouts and shit here for killing the client's - process off - like... if the window is around after 5 seconds, then the close button - turns a nice red, and if this function is called again, the client is - explicitly killed. - */ - - ce.xclient.type = ClientMessage; - ce.xclient.message_type = prop_atoms.wm_protocols; - ce.xclient.display = ob_display; - 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_curtime; - ce.xclient.data.l[2] = 0l; - ce.xclient.data.l[3] = 0l; - ce.xclient.data.l[4] = 0l; - XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce); + else + /* request the client to close with WM_DELETE_WINDOW */ + PROP_MSG_TO(self->window, self->window, wm_protocols, + prop_atoms.wm_delete_window, event_curtime, 0, 0, 0, + NoEventMask); } void client_kill(ObClient *self) { - XKillClient(ob_display, self->window); + if (!self->client_machine && self->pid) { + /* running on the local host */ + if (!self->close_tried_term) { + ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n", + self->window, self->pid); + kill(self->pid, SIGTERM); + self->close_tried_term = TRUE; + + /* show that we're trying to kill it */ + client_update_title(self); + } + else { + ob_debug("killing window 0x%x with pid %lu, with SIGKILL\n", + self->window, self->pid); + kill(self->pid, SIGKILL); /* kill -9 */ + } + } + else + XKillClient(ob_display, self->window); } void client_hilite(ObClient *self, gboolean hilite) @@ -3239,10 +3280,10 @@ void client_hilite(ObClient *self, gboolean hilite) } } -void client_set_desktop_recursive(ObClient *self, - guint target, - gboolean donthide, - gboolean dontraise) +static void client_set_desktop_recursive(ObClient *self, + guint target, + gboolean donthide, + gboolean dontraise) { guint old; GSList *it; @@ -3260,7 +3301,8 @@ void client_set_desktop_recursive(ObClient *self, frame_adjust_state(self->frame); /* 'move' the window to the new desktop */ if (!donthide) - client_showhide(self); + client_hide(self); + client_show(self); /* raise if it was not already on the desktop */ if (old != DESKTOP_ALL && !dontraise) stacking_raise(CLIENT_AS_WINDOW(self)); @@ -3297,7 +3339,7 @@ ObClient *client_search_modal_child(ObClient *self) { GSList *it; ObClient *ret; - + for (it = self->transients; it; it = g_slist_next(it)) { ObClient *c = it->data; if ((ret = client_search_modal_child(c))) return ret; @@ -3308,7 +3350,7 @@ ObClient *client_search_modal_child(ObClient *self) gboolean client_validate(ObClient *self) { - XEvent e; + XEvent e; XSync(ob_display, FALSE); /* get all events on the server */ @@ -3324,7 +3366,7 @@ gboolean client_validate(ObClient *self) void client_set_wm_state(ObClient *self, glong state) { if (state == self->wmstate) return; /* no change */ - + switch (state) { case IconicState: client_iconify(self, TRUE, TRUE, FALSE); @@ -3353,11 +3395,11 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action == prop_atoms.net_wm_state_remove || action == prop_atoms.net_wm_state_toggle)) /* an invalid action was passed to the client message, ignore it */ - return; + return; for (i = 0; i < 2; ++i) { Atom state = i == 0 ? data1 : data2; - + if (!state) continue; /* if toggling, then pick whether we're adding or removing */ @@ -3404,7 +3446,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action = undecorated ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; } - + if (action == prop_atoms.net_wm_state_add) { if (state == prop_atoms.net_wm_state_modal) { modal = TRUE; @@ -3557,6 +3599,10 @@ gboolean client_focus(ObClient *self) "Focusing client \"%s\" (0x%x) at time %u\n", self->title, self->window, event_curtime); + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ + event_halt_focus_delay(); + /* if there is a grab going on, then we need to cancel it. if we move focus during the grab, applications will get NotifyWhileGrabbed events and ignore them ! @@ -3597,17 +3643,9 @@ gboolean client_focus(ObClient *self) return !xerror_occured; } -/*! Present the client to the user. - @param raise If the client should be raised or not. You should only set - raise to false if you don't care if the window is completely - hidden. -*/ -static void client_present(ObClient *self, gboolean here, gboolean raise) +static void client_present(ObClient *self, gboolean here, gboolean raise, + gboolean unshade) { - /* if using focus_delay, stop the timer now so that focus doesn't - go moving on us */ - event_halt_focus_delay(); - if (client_normal(self) && screen_showing_desktop) screen_show_desktop(FALSE, self); if (self->iconic) @@ -3623,7 +3661,7 @@ static void client_present(ObClient *self, gboolean here, gboolean raise) /* if its not visible for other reasons, then don't mess with it */ return; - if (self->shaded) + if (self->shaded && unshade) client_shade(self, FALSE); if (raise) stacking_raise(CLIENT_AS_WINDOW(self)); @@ -3631,36 +3669,10 @@ static void client_present(ObClient *self, gboolean here, gboolean raise) client_focus(self); } -void client_activate(ObClient *self, gboolean here, gboolean user) +void client_activate(ObClient *self, gboolean here, gboolean raise, + gboolean unshade, gboolean user) { - guint32 last_time = focus_client ? focus_client->user_time : CurrentTime; - gboolean allow = FALSE; - - /* if the currently focused app doesn't set a user_time, then it can't - benefit from any focus stealing prevention. - - if the timestamp is missing in the request then let it go through - even if it is source=app, because EVERY APPLICATION DOES THIS because - GTK IS VERY BUGGY AND HARDCODES source=application... WHY!? - */ - if (!last_time || !event_curtime) - allow = TRUE; - /* otherwise, if they didn't give a time stamp or if it is too old, they - don't get focus */ - else - allow = event_time_after(event_curtime, last_time); - - ob_debug_type(OB_DEBUG_FOCUS, - "Want to activate window 0x%x with time %u (last time %u), " - "source=%s allowing? %d\n", - self->window, event_curtime, last_time, - (user ? "user" : "application"), allow); - - if (allow) - client_present(self, here, TRUE); - else - /* don't focus it but tell the user it wants attention */ - client_hilite(self, TRUE); + client_present(self, here, raise, unshade); } static void client_bring_windows_recursive(ObClient *self, @@ -3716,7 +3728,7 @@ static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) if ((parent = client_icon_recursive(c, w, h))) break; } - + return parent; } @@ -3770,7 +3782,7 @@ void client_set_layer(ObClient *self, gint layer) void client_set_undecorated(ObClient *self, gboolean undecorated) { if (self->undecorated != undecorated && - /* don't let it undecorate if the function is missing, but let + /* don't let it undecorate if the function is missing, but let it redecorate */ (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated)) { @@ -3790,7 +3802,7 @@ ObClient *client_direct_parent(ObClient *self) if (!self->parents) return NULL; if (self->transient_for_group) return NULL; return self->parents->data; -} +} ObClient *client_search_top_direct_parent(ObClient *self) { @@ -3805,7 +3817,7 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, { GSList *ret; ObClient *p; - + /* move up the direct transient chain as far as possible */ while ((p = client_direct_parent(self)) && (!bylayer || p->layer == layer)) @@ -3862,191 +3874,331 @@ ObClient *client_search_transient(ObClient *self, ObClient *search) return NULL; } -#define WANT_EDGE(cur, c) \ - if (cur == c) \ - continue; \ - if (c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && \ - cur->desktop != screen_desktop) \ - continue; \ - if (cur->iconic) \ - continue; +static void detect_edge(Rect area, ObDirection dir, + gint my_head, gint my_size, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge) +{ + gint edge_start, edge_size, head, tail; + gboolean skip_head = FALSE, skip_tail = FALSE; + + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + edge_start = area.x; + edge_size = area.width; + break; + case OB_DIRECTION_EAST: + case OB_DIRECTION_WEST: + edge_start = area.y; + edge_size = area.height; + break; + default: + g_assert_not_reached(); + } + + /* do we collide with this window? */ + if (!RANGES_INTERSECT(my_edge_start, my_edge_size, + edge_start, edge_size)) + return; + + switch (dir) { + case OB_DIRECTION_NORTH: + head = RECT_BOTTOM(area); + tail = RECT_TOP(area); + break; + case OB_DIRECTION_SOUTH: + head = RECT_TOP(area); + tail = RECT_BOTTOM(area); + break; + case OB_DIRECTION_WEST: + head = RECT_RIGHT(area); + tail = RECT_LEFT(area); + break; + case OB_DIRECTION_EAST: + head = RECT_LEFT(area); + tail = RECT_RIGHT(area); + break; + default: + g_assert_not_reached(); + } + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_WEST: + /* check if our window is past the head of this window */ + if (my_head <= head + 1) + skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ + if (my_head + my_size - 1 <= tail) + skip_tail = TRUE; + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head + (*near_edge ? 0 : my_size) < *dest) + skip_head = TRUE; + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail - (!*near_edge ? 0 : my_size) < *dest) + skip_tail = TRUE; + break; + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_EAST: + /* check if our window is past the head of this window */ + if (my_head >= head - 1) + skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ + if (my_head - my_size + 1 >= tail) + skip_tail = TRUE; + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head - (*near_edge ? 0 : my_size) > *dest) + skip_head = TRUE; + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail + (!*near_edge ? 0 : my_size) > *dest) + skip_tail = TRUE; + break; + default: + g_assert_not_reached(); + } + + ob_debug("my head %d size %d\n", my_head, my_size); + ob_debug("head %d tail %d deest %d\n", head, tail, *dest); + if (!skip_head) { + ob_debug("using near edge %d\n", head); + *dest = head; + *near_edge = TRUE; + } + else if (!skip_tail) { + ob_debug("using far edge %d\n", tail); + *dest = tail; + *near_edge = FALSE; + } +} -#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, gboolean hang) -{ - gint dest, monitor_dest; - gint my_edge_start, my_edge_end, my_offset; +void client_find_edge_directional(ObClient *self, ObDirection dir, + gint my_head, gint my_size, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge) +{ GList *it; Rect *a, *mon; - - if(!client_list) - return -1; + Rect dock_area; + gint edge; - a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area); - mon = screen_area(c->desktop, SCREEN_AREA_ONE_MONITOR, &c->frame->area); + a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, + &self->frame->area); + mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, + &self->frame->area); - switch(dir) { + 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 + (hang ? c->frame->area.height : 0); - - /* default: top of screen */ - dest = a->y + (hang ? c->frame->area.height : 0); - monitor_dest = mon->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; - - 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 + - (hang ? 0 : cur->frame->area.height); - - if(his_offset + 1 > my_offset) - continue; - - if(his_offset < dest) - continue; - - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + if (my_head >= RECT_TOP(*mon) + 1) + edge = RECT_TOP(*mon) - 1; + else + edge = RECT_TOP(*a) - 1; 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 + (hang ? 0 : c->frame->area.height); + if (my_head <= RECT_BOTTOM(*mon) - 1) + edge = RECT_BOTTOM(*mon) + 1; + else + edge = RECT_BOTTOM(*a) + 1; + break; + case OB_DIRECTION_EAST: + if (my_head <= RECT_RIGHT(*mon) - 1) + edge = RECT_RIGHT(*mon) + 1; + else + edge = RECT_RIGHT(*a) + 1; + break; + case OB_DIRECTION_WEST: + if (my_head >= RECT_LEFT(*mon) + 1) + edge = RECT_LEFT(*mon) - 1; + else + edge = RECT_LEFT(*a) - 1; + break; + default: + g_assert_not_reached(); + } + /* default to the far edge, then narrow it down */ + *dest = edge; + *near_edge = TRUE; - /* default: bottom of screen */ - dest = a->y + a->height - (hang ? c->frame->area.height : 0); - monitor_dest = mon->y + mon->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; it = g_list_next(it)) { + ObClient *cur = it->data; - 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; + /* skip windows to not bump into */ + if (cur == self) + continue; + if (cur->iconic) + continue; + if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && + cur->desktop != screen_desktop) + continue; - WANT_EDGE(cur, c) + ob_debug("trying window %s\n", cur->title); - his_edge_start = cur->frame->area.x; - his_edge_end = cur->frame->area.x + cur->frame->area.width; - his_offset = cur->frame->area.y + - (hang ? cur->frame->area.height : 0); + detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + } + dock_get_area(&dock_area); + detect_edge(dock_area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + g_free(a); + g_free(mon); +} +void client_find_move_directional(ObClient *self, ObDirection dir, + gint *x, gint *y) +{ + gint head, size; + gint e, e_start, e_size; + gboolean near; - if(his_offset - 1 < my_offset) - continue; - - if(his_offset > dest) - continue; + switch (dir) { + case OB_DIRECTION_EAST: + head = RECT_RIGHT(self->frame->area); + size = self->frame->area.width; + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + break; + case OB_DIRECTION_WEST: + head = RECT_LEFT(self->frame->area); + size = self->frame->area.width; + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + break; + case OB_DIRECTION_NORTH: + head = RECT_TOP(self->frame->area); + size = self->frame->area.height; + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + break; + case OB_DIRECTION_SOUTH: + head = RECT_BOTTOM(self->frame->area); + size = self->frame->area.height; + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + break; + default: + g_assert_not_reached(); + } - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + client_find_edge_directional(self, dir, head, size, + e_start, e_size, &e, &near); + *x = self->frame->area.x; + *y = self->frame->area.y; + switch (dir) { + case OB_DIRECTION_EAST: + if (near) e -= self->frame->area.width; + else e++; + *x = e; 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 + (hang ? c->frame->area.width : 0); - - /* default: leftmost egde of screen */ - dest = a->x + (hang ? c->frame->area.width : 0); - monitor_dest = mon->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; - - 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 + - (hang ? 0 : cur->frame->area.width); - - if(his_offset + 1 > my_offset) - continue; + if (near) e++; + else e -= self->frame->area.width; + *x = e; + break; + case OB_DIRECTION_NORTH: + if (near) e++; + else e -= self->frame->area.height; + *y = e; + break; + case OB_DIRECTION_SOUTH: + if (near) e -= self->frame->area.height; + else e++; + *y = e; + break; + default: + g_assert_not_reached(); + } + frame_frame_gravity(self->frame, x, y); +} - if(his_offset < dest) - continue; +void client_find_resize_directional(ObClient *self, ObDirection side, + gboolean grow, + gint *x, gint *y, gint *w, gint *h) +{ + gint head; + gint e, e_start, e_size, delta; + gboolean near; + ObDirection dir; - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } - break; + switch (side) { 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 + (hang ? 0 : c->frame->area.width); - - /* default: rightmost edge of screen */ - dest = a->x + a->width - (hang ? c->frame->area.width : 0); - monitor_dest = mon->x + mon->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; - - 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 + - (hang ? cur->frame->area.width : 0); - - if(his_offset - 1 < my_offset) - continue; - - if(his_offset > dest) - continue; - - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + head = RECT_RIGHT(self->frame->area) + + (self->size_inc.width - 1) * (grow ? 1 : -1); + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST; + break; + case OB_DIRECTION_WEST: + head = RECT_LEFT(self->frame->area) - + (self->size_inc.width - 1) * (grow ? 1 : -1); + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST; + break; + case OB_DIRECTION_NORTH: + head = RECT_TOP(self->frame->area) - + (self->size_inc.height - 1) * (grow ? 1 : -1); + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH; + break; + case OB_DIRECTION_SOUTH: + head = RECT_BOTTOM(self->frame->area) + + (self->size_inc.height - 1) * (grow ? 1 : -1); + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH; break; - case OB_DIRECTION_NORTHEAST: - case OB_DIRECTION_SOUTHEAST: - case OB_DIRECTION_NORTHWEST: - case OB_DIRECTION_SOUTHWEST: - /* not implemented */ default: g_assert_not_reached(); - dest = 0; /* suppress warning */ } - g_free(a); - g_free(mon); - return dest; + ob_debug("head %d dir %d\n", head, dir); + client_find_edge_directional(self, dir, head, 1, + e_start, e_size, &e, &near); + ob_debug("edge %d\n", e); + *x = self->frame->area.x; + *y = self->frame->area.y; + *w = self->frame->area.width; + *h = self->frame->area.height; + switch (side) { + case OB_DIRECTION_EAST: + if (grow == near) --e; + delta = e - RECT_RIGHT(self->frame->area); + *w += delta; + break; + case OB_DIRECTION_WEST: + if (grow == near) ++e; + delta = RECT_LEFT(self->frame->area) - e; + *x -= delta; + *w += delta; + break; + case OB_DIRECTION_NORTH: + if (grow == near) ++e; + delta = RECT_TOP(self->frame->area) - e; + *y -= delta; + *h += delta; + break; + case OB_DIRECTION_SOUTH: + if (grow == near) --e; + delta = e - RECT_BOTTOM(self->frame->area); + *h += delta; + break; + default: + g_assert_not_reached(); + } + frame_frame_gravity(self->frame, x, y); + *w -= self->frame->size.left + self->frame->size.right; + *h -= self->frame->size.top + self->frame->size.bottom; } -ObClient* client_under_pointer() +ObClient* client_under_pointer(void) { gint x, y; GList *it;