X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=9fa311cfbdb1fce62123c416284cabe45b527cb6;hb=4c7cc1cfa64bf5722f059eae0528d510c2ae636f;hp=1dd48bf23ff95099ed62ffd02d5a24102698b255;hpb=e5c7c95f2db1eef776d5acd3db46334fb932e3a6;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 1dd48bf2..9fa311cf 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" @@ -31,6 +32,7 @@ #include "session.h" #include "event.h" #include "grab.h" +#include "prompt.h" #include "focus.h" #include "stacking.h" #include "openbox.h" @@ -40,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 @@ -61,9 +68,10 @@ typedef struct gpointer data; } ClientCallback; -GList *client_list = NULL; +GList *client_list = NULL; -static GSList *client_destroy_notifies = NULL; +static GSList *client_destroy_notifies = NULL; +static RrImage *client_default_icon = NULL; static void client_get_all(ObClient *self, gboolean real); static void client_get_startup_id(ObClient *self); @@ -74,6 +82,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,10 +105,25 @@ 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) { + if ((client_default_icon = RrImageCacheFind(ob_rr_icons, + ob_rr_theme->def_win_icon, + ob_rr_theme->def_win_icon_w, + ob_rr_theme->def_win_icon_h))) + RrImageRef(client_default_icon); + else { + client_default_icon = RrImageNew(ob_rr_icons); + RrImageAddPicture(client_default_icon, + ob_rr_theme->def_win_icon, + ob_rr_theme->def_win_icon_w, + ob_rr_theme->def_win_icon_h); + } + if (reconfig) return; client_set_list(); @@ -104,6 +131,9 @@ void client_startup(gboolean reconfig) void client_shutdown(gboolean reconfig) { + RrImageUnref(client_default_icon); + client_default_icon = NULL; + if (reconfig) return; } @@ -140,7 +170,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 +194,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 +220,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 +230,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; @@ -255,8 +288,10 @@ void client_manage(Window window) map_time = event_get_server_time(); - /* choose the events we want to receive on the CLIENT window */ - attrib_set.event_mask = CLIENT_EVENTMASK; + /* 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); @@ -266,6 +301,7 @@ 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 */ @@ -278,9 +314,16 @@ 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); + 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); @@ -298,11 +341,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 +490,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; @@ -637,7 +679,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); @@ -645,7 +687,6 @@ void client_unmanage_all() void client_unmanage(ObClient *self) { - guint j; GSList *it; gulong ignore_start; @@ -672,8 +713,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); + /* 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); @@ -682,6 +725,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); @@ -760,20 +807,24 @@ 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(); ob_debug("Unmanaged window 0x%lx\n", self->window); /* free all data allocated in the client struct */ + RrImageUnref(self->icon_set); g_slist_free(self->transients); - for (j = 0; j < self->nicons; ++j) - 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); @@ -811,13 +862,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); @@ -960,6 +1013,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 +1063,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 +1562,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 +1590,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 +1700,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 +1865,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; @@ -1908,6 +1979,7 @@ 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)) { @@ -1924,6 +1996,7 @@ void client_update_title(ObClient *self) 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); @@ -1931,6 +2004,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; @@ -1954,6 +2036,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; } @@ -2017,101 +2108,129 @@ void client_update_icons(ObClient *self) guint num; guint32 *data; guint w, h, i, j; + guint num_seen; /* number of icons present */ + RrImage *img; - for (i = 0; i < self->nicons; ++i) - g_free(self->icons[i].data); - if (self->nicons > 0) - g_free(self->icons); - self->nicons = 0; + img = NULL; + + /* grab the server, because we might be setting the window's icon and + we don't want them to set it in between and we overwrite their own + icon */ + grab_server(TRUE); 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) { + num_seen = 0; + while (i + 2 < num) { /* +2 is to make sure there is a w and h */ w = data[i++]; h = data[i++]; - i += w * h; - if (i > num || w*h == 0) break; - ++self->nicons; + /* watch for the data being too small for the specified size, + or for zero sized icons. */ + if (i + w*h > num || w == 0 || h == 0) break; + + /* convert it to the right bit order for ObRender */ + for (j = 0; j < w*h; ++j) + data[i+j] = + (((data[i+j] >> 24) & 0xff) << RrDefaultAlphaOffset) + + (((data[i+j] >> 16) & 0xff) << RrDefaultRedOffset) + + (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) + + (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset); + + /* is it in the cache? */ + img = RrImageCacheFind(ob_rr_icons, &data[i], w, h); + if (img) RrImageRef(img); /* own it */ + + i += w*h; + ++num_seen; + + /* don't bother looping anymore if we already found it in the cache + since we'll just use that! */ + if (img) break; } - self->icons = g_new(ObClientIcon, self->nicons); - - /* store the icons */ - i = 0; - for (j = 0; j < self->nicons; ++j) { - guint x, y, t; - - w = self->icons[j].width = data[i++]; - h = self->icons[j].height = data[i++]; - - if (w*h == 0) continue; - - self->icons[j].data = g_new(RrPixel32, w * h); - for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) { - if (x >= w) { - x = 0; - ++y; - } - self->icons[j].data[t] = - (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) + - (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) + - (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) + - (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset); + /* if it's not in the cache yet, then add it to the cache now. + we have already converted it to the correct bit order above */ + if (!img && num_seen > 0) { + img = RrImageNew(ob_rr_icons); + i = 0; + for (j = 0; j < num_seen; ++j) { + w = data[i++]; + h = data[i++]; + RrImageAddPicture(img, &data[i], w, h); + i += w*h; } - g_assert(i <= num); } g_free(data); - } else { + } + + /* if we didn't find an image from the NET_WM_ICON stuff, then try the + legacy X hints */ + if (!img) { XWMHints *hints; if ((hints = XGetWMHints(ob_display, self->window))) { if (hints->flags & IconPixmapHint) { - self->nicons = 1; - self->icons = g_new(ObClientIcon, self->nicons); + gboolean xicon; xerror_set_ignore(TRUE); - if (!RrPixmapToRGBA(ob_rr_inst, - hints->icon_pixmap, - (hints->flags & IconMaskHint ? - hints->icon_mask : None), - &self->icons[0].width, - &self->icons[0].height, - &self->icons[0].data)) - { - g_free(self->icons); - self->nicons = 0; - } + xicon = RrPixmapToRGBA(ob_rr_inst, + hints->icon_pixmap, + (hints->flags & IconMaskHint ? + hints->icon_mask : None), + (gint*)&w, (gint*)&h, &data); xerror_set_ignore(FALSE); + + + if (xicon) { + if (w > 0 && h > 0) { + /* is this icon in the cache yet? */ + img = RrImageCacheFind(ob_rr_icons, data, w, h); + if (img) RrImageRef(img); /* own it */ + + /* if not, then add it */ + if (!img) { + img = RrImageNew(ob_rr_icons); + RrImageAddPicture(img, data, w, h); + } + } + + g_free(data); + } } XFree(hints); } } - /* 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 + /* set the client's icons to be whatever we found */ + RrImageUnref(self->icon_set); + self->icon_set = img; - if it has parents, then one of them will have an icon already + /* if the client has no icon at all, then we set a default icon onto it. + but, if it has parents, then one of them will have an icon already */ - if (self->nicons == 0 && !self->parents) { + if (!self->icon_set && !self->parents) { RrPixel32 *icon = ob_rr_theme->def_win_icon; - gulong *data; - - data = g_new(gulong, 48*48+2); - data[0] = data[1] = 48; - for (i = 0; i < 48*48; ++i) - data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) + + gulong *ldata; /* use a long here to satisfy OBT_PROP_SETA32 */ + + w = ob_rr_theme->def_win_icon_w; + h = ob_rr_theme->def_win_icon_h; + ldata = g_new(gulong, w*h+2); + ldata[0] = w; + ldata[1] = h; + for (i = 0; i < w*h; ++i) + ldata[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) + (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) + (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) + (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0); - PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2); - g_free(data); + PROP_SETA32(self->window, net_wm_icon, cardinal, ldata, w*h+2); + g_free(ldata); } else if (self->frame) /* don't draw the icon empty if we're just setting one now anyways, we'll get the property change any second */ frame_adjust_icon(self->frame); + + grab_server(FALSE); } void client_update_icon_geometry(ObClient *self) @@ -2121,12 +2240,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 +2334,7 @@ static void client_get_session_ids(ObClient *self) if (got) { gchar localhost[128]; + guint32 pid; gethostname(localhost, 127); localhost[127] = '\0'; @@ -2221,6 +2342,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 +2484,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 +2518,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) @@ -2420,6 +2585,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 +2630,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 +2991,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 +3014,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 +3049,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 +3123,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 +3337,140 @@ 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; + if (self->not_responding != dead) { + self->not_responding = dead; + client_update_title(self); + + if (dead) + /* the client isn't responding, so ask to kill it */ + client_prompt_kill(self); + else { + /* 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 closing an internal obprompt, that is just cancelling it */ + 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); + + /* we're trying to close the window, so see if it is responding. if it + is not, then we will let them kill the window */ + if (self->ping) + ping_start(self, client_ping_event); + + /* if we already know the window isn't responding (maybe they clicked + no in the kill dialog but it hasn't come back to life), then show + the kill dialog */ + 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. - */ + 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; + + if (client_on_localhost(self)) { + const gchar *sig; + + if (self->kill_level == 0) + sig = "terminate"; + else + sig = "kill"; + + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), self->original_title, sig); + } + else + m = g_strdup_printf + (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"), self->original_title); - 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); + + 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 (client_on_localhost(self) && 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) @@ -3203,10 +3489,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; @@ -3637,53 +3923,21 @@ gboolean client_focused(ObClient *self) return self == focus_client; } -static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) -{ - guint i; - gulong min_diff, min_i; - - if (!self->nicons) { - ObClientIcon *parent = NULL; - GSList *it; - - for (it = self->parents; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if ((parent = client_icon_recursive(c, w, h))) - break; - } - - return parent; - } - - /* some kind of crappy approximation to find the icon closest in size to - what we requested, but icons are generally all the same ratio as - eachother so it's good enough. */ - - min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h); - min_i = 0; - for (i = 1; i < self->nicons; ++i) { - gulong diff; - diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h); - if (diff < min_diff) { - min_diff = diff; - min_i = i; - } - } - return &self->icons[min_i]; -} - -const ObClientIcon* client_icon(ObClient *self, gint w, gint h) +RrImage* client_icon(ObClient *self) { - ObClientIcon *ret; - static ObClientIcon deficon; + RrImage *ret = NULL; - if (!(ret = client_icon_recursive(self, w, h))) { - deficon.width = deficon.height = 48; - deficon.data = ob_rr_theme->def_win_icon; - ret = &deficon; + if (self->icon_set) + ret = self->icon_set; + else if (self->parents) { + GSList *it; + for (it = self->parents; it && !ret; it = g_slist_next(it)) + ret = client_icon(it->data); } + if (!ret) + ret = client_default_icon; return ret; } @@ -4121,7 +4375,7 @@ void client_find_resize_directional(ObClient *self, ObDirection side, *h -= self->frame->size.top + self->frame->size.bottom; } -ObClient* client_under_pointer() +ObClient* client_under_pointer(void) { gint x, y; GList *it; @@ -4154,3 +4408,9 @@ gboolean client_has_group_siblings(ObClient *self) { return self->group && self->group->members->next; } + +/*! Returns TRUE if the client is running on the same machine as Openbox */ +gboolean client_on_localhost(ObClient *self) +{ + return self->client_machine == NULL; +}