X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=ed2e5b561e1c75a214de30b36817a967ee445bb6;hb=7b4556b2111b03dcdca4a58e1bde94a66ba86806;hp=506653752b357ff8164551b9856658811b675aea;hpb=e3b9c8b6814aca2ca44b60517c2784178570fe52;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 50665375..ed2e5b56 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" @@ -31,8 +32,8 @@ #include "session.h" #include "event.h" #include "grab.h" +#include "prompt.h" #include "focus.h" -#include "propwin.h" #include "stacking.h" #include "openbox.h" #include "group.h" @@ -41,11 +42,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 +81,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 +98,14 @@ 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); +static void client_prompt_kill(ObClient *self); void client_startup(gboolean reconfig) @@ -140,7 +153,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 +177,7 @@ void client_set_list() stacking_set_list(); } -void client_manage_all() +void client_manage_all(void) { guint i, j, nchild; Window w, *children; @@ -190,6 +203,9 @@ void client_manage_all() } } + /* manage windows in reverse order from how they were originally mapped. + this is an attempt to manage children windows before their parents, so + that when the parent is mapped, it can find the child */ for (i = 0; i < nchild; ++i) { if (children[i] == None) continue; @@ -197,13 +213,13 @@ void client_manage_all() if (attrib.override_redirect) continue; if (attrib.map_state != IsUnmapped) - client_manage(children[i]); + client_manage(children[i], NULL); } } XFree(children); } -void client_manage(Window window) +void client_manage(Window window, ObPrompt *prompt) { ObClient *self; XEvent e; @@ -212,8 +228,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 +253,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,8 +269,12 @@ void client_manage(Window window) ob_debug("Managing window: 0x%lx\n", window); - /* choose the events we want to receive on the CLIENT window */ - attrib_set.event_mask = CLIENT_EVENTMASK; + map_time = event_get_server_time(); + + /* choose the events we want to receive on the CLIENT window + (ObPrompt windows can request events too) */ + attrib_set.event_mask = CLIENT_EVENTMASK | + (prompt ? prompt->event_mask : 0); attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; XChangeWindowAttributes(ob_display, window, CWEventMask|CWDontPropagate, &attrib_set); @@ -263,19 +284,29 @@ void client_manage(Window window) self = g_new0(ObClient, 1); self->obwin.type = Window_Client; self->window = window; + self->prompt = prompt; /* non-zero defaults */ 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); + + /* now we have all of the window's information so we can set this up. + do this before creating the frame, so it can tell that we are still + mapping and doesn't go applying things right away */ + client_setup_decor_and_functions(self, FALSE); + /* specify that if we exit, the window should not be destroyed and - should be reparented back to root automatically */ - XChangeSaveSet(ob_display, window, SetModeInsert); + should be reparented back to root automatically, unless we are managing + an internal ObPrompt window */ + if (!self->prompt) + XChangeSaveSet(ob_display, window, SetModeInsert); /* create the decoration frame for the client window */ self->frame = frame_new(self); @@ -293,13 +324,8 @@ void client_manage(Window window) /* the session should get the last say though */ client_restore_session_state(self); - /* 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 +345,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 +359,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 +370,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 +378,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 +403,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 +426,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 +467,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 +511,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 +546,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 +605,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 +619,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 +662,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 +671,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); @@ -636,11 +706,10 @@ void client_unmanage(ObClient *self) mouse_grab_for_client(self, FALSE); - /* 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); + /* remove the window from our save set, unless we are managing an internal + ObPrompt window */ + if (!self->prompt) + XChangeSaveSet(ob_display, self->window, SetModeDelete); /* update the focus lists */ focus_order_remove(self); @@ -649,6 +718,10 @@ void client_unmanage(ObClient *self) focus_client = NULL; } + /* if we're prompting to kill the client, close that */ + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + client_list = g_list_remove(client_list, self); stacking_remove(self); g_hash_table_remove(window_map, &self->window); @@ -727,6 +800,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(); @@ -738,9 +816,11 @@ void client_unmanage(ObClient *self) g_free(self->icons[j].data); if (self->nicons > 0) g_free(self->icons); + g_free(self->startup_id); g_free(self->wm_command); g_free(self->title); g_free(self->icon_title); + g_free(self->original_title); g_free(self->name); g_free(self->class); g_free(self->role); @@ -778,13 +858,15 @@ static ObAppSettings *client_get_settings_state(ObClient *self) !g_pattern_match(app->name, strlen(self->name), self->name, NULL)) match = FALSE; else if (app->class && - !g_pattern_match(app->class, - strlen(self->class), self->class, NULL)) + !g_pattern_match(app->class, + strlen(self->class), self->class, NULL)) match = FALSE; else if (app->role && !g_pattern_match(app->role, strlen(self->role), self->role, NULL)) match = FALSE; + else if ((signed)app->type >= 0 && app->type != self->type) + match = FALSE; if (match) { ob_debug("Window matching: %s\n", app->name); @@ -927,12 +1009,13 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, gint fw, fh; Rect desired; guint i; + gboolean found_mon; RECT_SET(desired, *x, *y, w, 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; @@ -976,13 +1059,26 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, rudeb = TRUE; } + /* we iterate through every monitor that the window is at least partially + on, to make sure it is obeying the rules on them all + + if the window does not appear on any monitors, then use the first one + */ + found_mon = FALSE; 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 || found_mon) + 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 { + found_mon = TRUE; + 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 +1095,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 +1116,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 +1164,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 +1172,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 +1187,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 +1208,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 +1255,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 +1338,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 +1370,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. + /** Remove the client from the transient tree **/ - 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); - } - } + 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 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); - } - } - } - /* 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 +1417,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 +1426,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 +1442,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 +1484,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 +1520,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 +1558,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 +1586,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 +1643,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 +1696,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; @@ -1787,16 +1861,16 @@ static void client_change_allowed_actions(ObClient *self) PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num); - /* make sure the window isn't breaking any rules now */ + /* make sure the window isn't breaking any rules now + + don't check ICONIFY here. just cuz a window can't iconify doesnt mean + it can't be iconified with its parent + */ if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) { if (self->frame) client_shade(self, FALSE); else self->shaded = FALSE; } - if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) { - if (self->frame) client_iconify(self, FALSE, TRUE, FALSE); - else self->iconic = FALSE; - } if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) { if (self->frame) client_fullscreen(self, FALSE); else self->fullscreen = FALSE; @@ -1814,7 +1888,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,22 +1975,24 @@ void client_update_title(ObClient *self) gchar *visible = NULL; g_free(self->title); - + g_free(self->original_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"); } } + self->original_title = g_strdup(data); if (self->client_machine) { visible = g_strdup_printf("%s (%s)", data, self->client_machine); @@ -1924,6 +2000,15 @@ void client_update_title(ObClient *self) } else visible = data; + if (self->not_responding) { + data = visible; + if (self->kill_level > 0) + 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 +2032,15 @@ void client_update_title(ObClient *self) } else visible = data; + if (self->not_responding) { + data = visible; + if (self->kill_level > 0) + 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; } @@ -2005,11 +2099,17 @@ void client_update_strut(ObClient *self) } } +/* Avoid storing icons above this size if possible */ +#define AVOID_ABOVE 64 + void client_update_icons(ObClient *self) { guint num; guint32 *data; guint w, h, i, j; + guint num_seen; /* number of icons present */ + guint num_small_seen; /* number of icons small enough present */ + guint smallest, smallest_area; for (i = 0; i < self->nicons; ++i) g_free(self->icons[i].data); @@ -2020,25 +2120,54 @@ void client_update_icons(ObClient *self) if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) { /* figure out how many valid icons are in here */ i = 0; - while (num - i > 2) { - w = data[i++]; - h = data[i++]; - i += w * h; - if (i > num || w*h == 0) break; - ++self->nicons; - } + num_seen = num_small_seen = 0; + smallest = smallest_area = 0; + if (num > 2) + while (i < num) { + w = data[i++]; + h = data[i++]; + i += w * h; + /* watch for it being too small for the specified size, or for + zero sized icons. */ + if (i > num || w == 0 || h == 0) break; + + if (!smallest_area || w*h < smallest_area) { + smallest = num_seen; + smallest_area = w*h; + } + ++num_seen; + if (w <= AVOID_ABOVE && h <= AVOID_ABOVE) + ++num_small_seen; + } + if (num_small_seen > 0) + self->nicons = num_small_seen; + else if (num_seen) + self->nicons = 1; self->icons = g_new(ObClientIcon, self->nicons); - + /* store the icons */ i = 0; - for (j = 0; j < self->nicons; ++j) { + for (j = 0; j < self->nicons;) { guint x, y, t; w = self->icons[j].width = data[i++]; h = self->icons[j].height = data[i++]; - if (w*h == 0) continue; + /* if there are some icons smaller than the threshold, we're + skipping all the ones above */ + if (num_small_seen > 0) { + if (w > AVOID_ABOVE || h > AVOID_ABOVE) { + i += w*h; + continue; + } + } + /* if there were no icons smaller than the threshold, then we are + only taking the smallest available one we saw */ + else if (j != smallest) { + i += w*h; + continue; + } self->icons[j].data = g_new(RrPixel32, w * h); for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) { @@ -2053,6 +2182,8 @@ void client_update_icons(ObClient *self) (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset); } g_assert(i <= num); + + ++j; } g_free(data); @@ -2086,7 +2217,7 @@ void client_update_icons(ObClient *self) or removes it entirely, it's not very likely it is going to set one right away afterwards - if it has parents, then one of them will have an icon already + 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; @@ -2107,62 +2238,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; @@ -2170,12 +2245,13 @@ void client_update_icon_geometry(ObClient *self) RECT_SET(self->icon_geometry, 0, 0, 0, 0); - if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num) - && num == 4) + if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)) { - /* don't let them set it with an area < 0 */ - RECT_SET(self->icon_geometry, data[0], data[1], - MAX(data[2],0), MAX(data[3],0)); + if (num == 4) + /* don't let them set it with an area < 0 */ + RECT_SET(self->icon_geometry, data[0], data[1], + MAX(data[2],0), MAX(data[3],0)); + g_free(data); } } @@ -2263,6 +2339,7 @@ static void client_get_session_ids(ObClient *self) if (got) { gchar localhost[128]; + guint32 pid; gethostname(localhost, 127); localhost[127] = '\0'; @@ -2270,6 +2347,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; } } @@ -2407,7 +2489,15 @@ static ObStackingLayer calc_layer(ObClient *self) (self->decorations == 0 && !(self->max_horz && self->max_vert) && RECT_EQUAL(self->area, *monitor))) && - (client_focused(self) || client_search_focus_tree(self))) + /* you are fullscreen while you or your children are focused.. */ + (client_focused(self) || client_search_focus_tree(self) || + /* you can be fullscreen if you're on another desktop */ + (self->desktop != screen_desktop && + self->desktop != DESKTOP_ALL) || + /* and you can also be fullscreen if the focused client is on + another monitor, or nothing else is focused */ + (!focus_client || + client_monitor(focus_client) != client_monitor(self)))) l = OB_STACKING_LAYER_FULLSCREEN; else if (self->above) l = OB_STACKING_LAYER_ABOVE; else if (self->below) l = OB_STACKING_LAYER_BELOW; @@ -2433,23 +2523,54 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig, stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); } + /* we've been restacked */ + self->visited = TRUE; + for (it = self->transients; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, self->layer); } +static void client_calc_layer_internal(ObClient *self) +{ + GSList *sit; + + /* transients take on the layer of their parents */ + sit = client_search_all_top_parents(self); + + for (; sit; sit = g_slist_next(sit)) + client_calc_layer_recursive(sit->data, self, 0); +} + void client_calc_layer(ObClient *self) { - ObClient *orig; - GSList *it; + GList *it; - orig = self; + /* skip over stuff above fullscreen layer */ + for (it = stacking_list; it; it = g_list_next(it)) + if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break; - /* transients take on the layer of their parents */ - it = client_search_all_top_parents(self); + /* find the windows in the fullscreen layer, and mark them not-visited */ + for (; it; it = g_list_next(it)) { + if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break; + else if (WINDOW_IS_CLIENT(it->data)) + WINDOW_AS_CLIENT(it->data)->visited = FALSE; + } + + client_calc_layer_internal(self); - for (; it; it = g_slist_next(it)) - client_calc_layer_recursive(it->data, orig, 0); + /* skip over stuff above fullscreen layer */ + for (it = stacking_list; it; it = g_list_next(it)) + if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break; + + /* now recalc any windows in the fullscreen layer which have not + had their layer recalced already */ + for (; it; it = g_list_next(it)) { + if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break; + else if (WINDOW_IS_CLIENT(it->data) && + !WINDOW_AS_CLIENT(it->data)->visited) + client_calc_layer_internal(it->data); + } } gboolean client_should_show(ObClient *self) @@ -2460,7 +2581,7 @@ gboolean client_should_show(ObClient *self) return FALSE; if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) return TRUE; - + return FALSE; } @@ -2469,6 +2590,10 @@ gboolean client_show(ObClient *self) gboolean show = FALSE; if (client_should_show(self)) { + /* replay pending pointer event before showing the window, in case it + should be going to something under the window */ + mouse_replay_pointer(); + frame_show(self->frame); show = TRUE; @@ -2510,6 +2635,10 @@ gboolean client_hide(ObClient *self) so trying to ignore them is futile in case 3 anyways */ + /* replay pending pointer event before hiding the window, in case it + should be going to the window */ + mouse_replay_pointer(); + frame_hide(self->frame); hide = TRUE; @@ -2604,7 +2733,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) @@ -2612,11 +2741,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); @@ -2701,7 +2837,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 */ @@ -2748,7 +2884,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)) { @@ -2799,7 +2935,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; @@ -2860,6 +2996,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, void client_configure(ObClient *self, gint x, gint y, gint w, gint h, gboolean user, gboolean final, gboolean force_reply) { + Rect oldframe; gint oldw, oldh; gboolean send_resize_client; gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE; @@ -2882,6 +3019,7 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, oldw = self->area.width; oldh = self->area.height; + oldframe = self->frame->area; RECT_SET(self->area, x, y, w, h); /* for app-requested resizes, always resize if 'resized' is true. @@ -2916,6 +3054,10 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, if (!user) ignore_start = event_start_ignore_all_enters(); + /* replay pending pointer event before move the window, in case it + would change what window gets the event */ + mouse_replay_pointer(); + frame_adjust_area(self->frame, fmoved, fresized, FALSE); if (!user) @@ -2986,6 +3128,14 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, } XFlush(ob_display); + + /* if it moved between monitors, then this can affect the stacking + layer of this window or others - for fullscreen windows */ + if (screen_find_monitor(&self->frame->area) != + screen_find_monitor(&oldframe)) + { + client_calc_layer(self); + } } void client_fullscreen(ObClient *self, gboolean fs) @@ -3028,6 +3178,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); @@ -3111,7 +3264,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 */ @@ -3189,41 +3342,113 @@ 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) { + /* it came back to life ! */ + + if (self->kill_prompt) { + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; + } + self->kill_level = 0; + } +} + +void client_close(ObClient *self) +{ if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; + if (self->prompt) { + prompt_cancel(self->prompt); + 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 { + /* 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); + + if (self->not_responding) + client_prompt_kill(self); + } +} + +#define OB_KILL_RESULT_NO 0 +#define OB_KILL_RESULT_YES 1 + +static void client_kill_requested(ObPrompt *p, gint result, gpointer data) +{ + ObClient *self = data; + + if (result == OB_KILL_RESULT_YES) 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); + prompt_unref(self->kill_prompt); + self->kill_prompt = NULL; +} + +static void client_prompt_kill(ObClient *self) +{ + /* check if we're already prompting */ + if (!self->kill_prompt) { + ObPromptAnswer answers[] = { + { _("No"), OB_KILL_RESULT_NO }, + { _("Yes"), OB_KILL_RESULT_YES } + }; + gchar *m; + + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), self->original_title); + + self->kill_prompt = prompt_new(m, answers, + sizeof(answers)/sizeof(answers[0]), + OB_KILL_RESULT_NO, /* default = no */ + OB_KILL_RESULT_NO, /* cancel = no */ + client_kill_requested, self); + g_free(m); + } + + prompt_show(self->kill_prompt, self); } void client_kill(ObClient *self) { - XKillClient(ob_display, self->window); + /* don't kill our own windows */ + if (self->prompt) return; + + if (!self->client_machine && self->pid) { + /* running on the local host */ + if (self->kill_level == 0) { + ob_debug("killing window 0x%x with pid %lu, with SIGTERM", + self->window, self->pid); + kill(self->pid, SIGTERM); + ++self->kill_level; + + /* 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 { + /* running on a remote host */ + XKillClient(ob_display, self->window); + } } void client_hilite(ObClient *self, gboolean hilite) @@ -3242,10 +3467,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; @@ -3301,7 +3526,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; @@ -3312,7 +3537,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 */ @@ -3328,7 +3553,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); @@ -3357,11 +3582,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 */ @@ -3408,7 +3633,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; @@ -3561,6 +3786,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 ! @@ -3601,17 +3830,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) @@ -3627,7 +3848,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)); @@ -3635,36 +3856,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, @@ -3720,7 +3915,7 @@ static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) if ((parent = client_icon_recursive(c, w, h))) break; } - + return parent; } @@ -3774,7 +3969,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)) { @@ -3794,7 +3989,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) { @@ -3809,7 +4004,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)) @@ -3866,191 +4061,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;