X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=43a2f551f8252d2f5aec58faa2570690ecd4527c;hb=91a1b089097cb3ba66f779df80fa7a51490aa785;hp=d0fed5457c1e9e6d022c6fdebc4e496b35c6a1e3;hpb=556eb7b7fb20b3b0db03b6d92259ad3bb16dccde;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index d0fed545..43a2f551 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -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" @@ -40,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 @@ -74,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); @@ -93,6 +103,7 @@ 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) @@ -190,6 +201,9 @@ void client_manage_all(void) } } + /* 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; @@ -278,6 +292,11 @@ void client_manage(Window window) 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); @@ -298,11 +317,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); - /* tell startup notification that this app started */ - launch_time = sn_app_started(self->startup_id, self->class); + 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. */ @@ -450,6 +466,8 @@ void client_manage(Window window) g_free(monitor); monitor = NULL; + ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n", + activate ? "yes" : "no"); if (activate) { gboolean raise = FALSE; @@ -594,6 +612,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); @@ -760,6 +787,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(); @@ -771,6 +803,7 @@ 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); @@ -960,6 +993,7 @@ 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); @@ -1009,18 +1043,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)) { - if (i < screen_num_monitors - 1) + if (i < screen_num_monitors - 1 || found_mon) continue; /* the window is not inside any monitor! so just use the first one */ a = screen_area(self->desktop, 0, NULL); - } else + } 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. @@ -1500,6 +1542,10 @@ 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) /* if this protocol is requested, then resizing the @@ -1524,7 +1570,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; @@ -1634,11 +1680,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; @@ -1794,16 +1845,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; @@ -1931,6 +1982,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; @@ -1954,6 +2014,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; } @@ -2012,11 +2081,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); @@ -2027,25 +2102,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) { @@ -2060,6 +2164,8 @@ void client_update_icons(ObClient *self) (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset); } g_assert(i <= num); + + ++j; } g_free(data); @@ -2121,12 +2227,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); } } @@ -2214,6 +2321,7 @@ static void client_get_session_ids(ObClient *self) if (got) { gchar localhost[128]; + guint32 pid; gethostname(localhost, 127); localhost[127] = '\0'; @@ -2221,6 +2329,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; } } @@ -2358,7 +2471,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; @@ -2384,23 +2505,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); + + /* 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; - for (; it; it = g_slist_next(it)) - client_calc_layer_recursive(it->data, orig, 0); + /* 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) @@ -2420,6 +2572,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; @@ -2461,6 +2617,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; @@ -2818,6 +2978,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; @@ -2840,6 +3001,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. @@ -2874,6 +3036,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) @@ -2944,6 +3110,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) @@ -3150,41 +3324,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) @@ -3203,10 +3394,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;