X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=5b96fb3c85f688f97e553709ea9a26078f8c60a7;hb=8387c6cce85136426837e1528cdd2bf851a1ce3a;hp=d2378bf7150fddb3f82c474b2f326255a2d7af00;hpb=a0acc01f2ca1f4cff337f359d64136b754678b93;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index d2378bf7..5b96fb3c 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -29,6 +29,7 @@ #include "session.h" #include "event.h" #include "grab.h" +#include "prompt.h" #include "focus.h" #include "stacking.h" #include "openbox.h" @@ -66,9 +67,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); @@ -103,10 +105,24 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, 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(); @@ -114,6 +130,9 @@ void client_startup(gboolean reconfig) void client_shutdown(gboolean reconfig) { + RrImageUnref(client_default_icon); + client_default_icon = NULL; + if (reconfig) return; } @@ -174,99 +193,24 @@ void client_set_list(void) stacking_set_list(); } -void client_manage_all(void) -{ - guint i, j, nchild; - Window w, *children; - XWMHints *wmhints; - XWindowAttributes attrib; - - XQueryTree(obt_display, RootWindow(obt_display, ob_screen), - &w, &w, &children, &nchild); - - /* remove all icon windows from the list */ - for (i = 0; i < nchild; i++) { - if (children[i] == None) continue; - wmhints = XGetWMHints(obt_display, children[i]); - if (wmhints) { - if ((wmhints->flags & IconWindowHint) && - (wmhints->icon_window != children[i])) - for (j = 0; j < nchild; j++) - if (children[j] == wmhints->icon_window) { - children[j] = None; - break; - } - XFree(wmhints); - } - } - - for (i = 0; i < nchild; ++i) { - if (children[i] == None) - continue; - if (XGetWindowAttributes(obt_display, children[i], &attrib)) { - if (attrib.override_redirect) continue; - - if (attrib.map_state != IsUnmapped) - client_manage(children[i]); - } - } - XFree(children); -} - -void client_manage(Window window) +void client_manage(Window window, ObPrompt *prompt) { ObClient *self; - XEvent e; - XWindowAttributes attrib; XSetWindowAttributes attrib_set; - XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; gboolean transient = FALSE; Rect place, *monitor; Time launch_time, map_time; - grab_server(TRUE); - - /* check if it has already been unmapped by the time we started - mapping. the grab does a sync so we don't have to here */ - if (XCheckTypedWindowEvent(obt_display, window, DestroyNotify, &e) || - XCheckTypedWindowEvent(obt_display, window, UnmapNotify, &e)) - { - XPutBackEvent(obt_display, &e); - - ob_debug("Trying to manage unmapped window. Aborting that.\n"); - grab_server(FALSE); - return; /* don't manage it */ - } - - /* make sure it isn't an override-redirect window */ - if (!XGetWindowAttributes(obt_display, window, &attrib) || - attrib.override_redirect) - { - grab_server(FALSE); - return; /* don't manage it */ - } - - /* is the window a docking app */ - if ((wmhint = XGetWMHints(obt_display, window))) { - if ((wmhint->flags & StateHint) && - wmhint->initial_state == WithdrawnState) - { - dock_add(window, wmhint); - grab_server(FALSE); - XFree(wmhint); - return; - } - XFree(wmhint); - } - - ob_debug("Managing window: 0x%lx\n", window); + ob_debug("Managing window: 0x%lx", 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(obt_display, window, CWEventMask|CWDontPropagate, &attrib_set); @@ -276,6 +220,7 @@ void client_manage(Window window) self = g_new0(ObClient, 1); self->obwin.type = OB_WINDOW_CLASS_CLIENT; self->window = window; + self->prompt = prompt; /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ @@ -285,12 +230,19 @@ void client_manage(Window window) /* 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); + ob_debug("Window type: %d", self->type); + ob_debug("Window group: 0x%x", 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(obt_display, window, SetModeInsert); + should be reparented back to root automatically, unless we are managing + an internal ObPrompt window */ + if (!self->prompt) + XChangeSaveSet(obt_display, window, SetModeInsert); /* create the decoration frame for the client window */ self->frame = frame_new(self); @@ -308,11 +260,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. */ @@ -351,7 +300,7 @@ void client_manage(Window window) /* figure out placement for the window if the window is new */ if (ob_state() == OB_STATE_RUNNING) { - ob_debug("Positioned: %s @ %d %d\n", + ob_debug("Positioned: %s @ %d %d", (!self->positioned ? "no" : (self->positioned == PPosition ? "program specified" : (self->positioned == USPosition ? "user specified" : @@ -359,7 +308,7 @@ void client_manage(Window window) "program + user specified" : "BADNESS !?")))), place.x, place.y); - ob_debug("Sized: %s @ %d %d\n", + ob_debug("Sized: %s @ %d %d", (!self->sized ? "no" : (self->sized == PSize ? "program specified" : (self->sized == USSize ? "user specified" : @@ -431,7 +380,7 @@ void client_manage(Window window) place.width = MIN(place.width, a->width); place.height = MIN(place.height, a->height); - ob_debug("setting window size to %dx%d\n", place.width, place.height); + ob_debug("setting window size to %dx%d", place.width, place.height); /* get the size of the client back */ place.width -= self->frame->size.left + self->frame->size.right; @@ -441,11 +390,11 @@ void client_manage(Window window) } ob_debug("placing window 0x%x at %d, %d with size %d x %d. " - "some restrictions may apply\n", + "some restrictions may apply", 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", + "overriding", self->session->x, self->session->y, self->session->w, self->session->h); @@ -460,7 +409,7 @@ void client_manage(Window window) g_free(monitor); monitor = NULL; - ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n", + ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s", activate ? "yes" : "no"); if (activate) { gboolean raise = FALSE; @@ -468,7 +417,7 @@ void client_manage(Window window) /* This is focus stealing prevention */ ob_debug_type(OB_DEBUG_FOCUS, "Want to focus new window 0x%x at time %u " - "launched at %u (last user interaction time %u)\n", + "launched at %u (last user interaction time %u)", self->window, map_time, launch_time, event_last_user_time); @@ -478,7 +427,7 @@ void client_manage(Window window) 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"); + "we don't want to interrupt them"); } /* if it's on another desktop */ @@ -492,7 +441,7 @@ void client_manage(Window window) raise = TRUE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because its on another " - "desktop\n"); + "desktop"); } /* If something is focused, and it's not our relative... */ else if (focus_client && client_search_focus_tree_full(self) == NULL && @@ -509,14 +458,14 @@ void client_manage(Window window) activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because the user is " - "working in another window\n"); + "working in another window"); } /* 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 its relatives aren't focused\n"); + "transient, and its relatives aren't focused"); } /* Don't steal focus from globally active clients. I stole this idea from KWin. It seems nice. @@ -527,7 +476,7 @@ void client_manage(Window window) activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because a globally " - "active client has focus\n"); + "active client has focus"); } /* Don't move focus if it's not going to go to this window anyway */ @@ -536,7 +485,7 @@ void client_manage(Window window) raise = TRUE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because another window " - "would get the focus anyway\n"); + "would get the focus anyway"); } else if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)) @@ -552,7 +501,7 @@ void client_manage(Window window) if (!activate) { ob_debug_type(OB_DEBUG_FOCUS, "Focus stealing prevention activated for %s at " - "time %u (last user interactioon time %u)\n", + "time %u (last user interactioon time %u)", self->title, map_time, event_last_user_time); /* if the client isn't focused, then hilite it so the user knows it is there */ @@ -606,22 +555,11 @@ 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); - ob_debug("Managed window 0x%lx plate 0x%x (%s)\n", + ob_debug("Managed window 0x%lx plate 0x%x (%s)", window, self->frame->window, self->class); - - return; } @@ -630,7 +568,7 @@ ObClient *client_fake_manage(Window window) ObClient *self; ObAppSettings *settings; - ob_debug("Pretend-managing window: %lx\n", window); + ob_debug("Pretend-managing window: %lx", window); /* do this minimal stuff to figure out the client's decorations */ @@ -648,7 +586,7 @@ ObClient *client_fake_manage(Window window) self->frame = frame_new(self); frame_adjust_area(self->frame, FALSE, TRUE, TRUE); - ob_debug("gave extents left %d right %d top %d bottom %d\n", + ob_debug("gave extents left %d right %d top %d bottom %d", self->frame->size.left, self->frame->size.right, self->frame->size.top, self->frame->size.bottom); @@ -666,11 +604,10 @@ void client_unmanage_all(void) void client_unmanage(ObClient *self) { - guint j; GSList *it; gulong ignore_start; - ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n", + ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)", self->window, self->frame->window, self->class, self->title ? self->title : ""); @@ -693,8 +630,10 @@ void client_unmanage(ObClient *self) mouse_grab_for_client(self, FALSE); - /* remove the window from our save set */ - XChangeSaveSet(obt_display, self->window, SetModeDelete); + /* remove the window from our save set, unless we are managing an internal + ObPrompt window */ + if (!self->prompt) + XChangeSaveSet(obt_display, self->window, SetModeDelete); /* update the focus lists */ focus_order_remove(self); @@ -703,6 +642,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); window_remove(self->window); @@ -789,17 +732,16 @@ void client_unmanage(ObClient *self) /* update the list hints */ client_set_list(); - ob_debug("Unmanaged window 0x%lx\n", self->window); + ob_debug("Unmanaged window 0x%lx", 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); @@ -837,16 +779,18 @@ 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); + ob_debug("Window matching: %s", app->name); /* copy the settings to our struct, overriding the existing settings if they are not defaults */ @@ -901,17 +845,17 @@ static void client_restore_session_state(ObClient *self) GList *it; ob_debug_type(OB_DEBUG_SM, - "Restore session for client %s\n", self->title); + "Restore session for client %s", self->title); if (!(it = session_state_find(self))) { ob_debug_type(OB_DEBUG_SM, - "Session data not found for client %s\n", self->title); + "Session data not found for client %s", self->title); return; } self->session = it->data; - ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n", + ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s", self->title); RECT_SET_POINT(self->area, self->session->x, self->session->y); @@ -986,6 +930,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); @@ -1035,18 +980,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. @@ -1164,7 +1117,7 @@ static void client_get_area(ObClient *self) POINT_SET(self->root_pos, wattrib.x, wattrib.y); self->border_width = wattrib.border_width; - ob_debug("client area: %d %d %d %d bw %d\n", wattrib.x, wattrib.y, + ob_debug("client area: %d %d %d %d bw %d", wattrib.x, wattrib.y, wattrib.width, wattrib.height, wattrib.border_width); } @@ -1177,7 +1130,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", self->desktop); } else { GSList *it; gboolean first = TRUE; @@ -1200,7 +1153,7 @@ static void client_get_desktop(ObClient *self) if (all != screen_num_desktops) { self->desktop = all; - ob_debug("client desktop set from parents: 0x%x\n", + ob_debug("client desktop set from parents: 0x%x", self->desktop); } /* try get from the startup-notification protocol */ @@ -1208,13 +1161,13 @@ static void client_get_desktop(ObClient *self) if (self->desktop >= screen_num_desktops && self->desktop != DESKTOP_ALL) self->desktop = screen_num_desktops - 1; - ob_debug("client desktop set from startup-notification: 0x%x\n", + ob_debug("client desktop set from startup-notification: 0x%x", self->desktop); } /* defaults to the current desktop */ else { self->desktop = screen_desktop; - ob_debug("client desktop set to the current desktop: %d\n", + ob_debug("client desktop set to the current desktop: %d", self->desktop); } } @@ -1569,7 +1522,7 @@ void client_update_colormap(ObClient *self, Colormap colormap) { if (colormap == self->colormap) return; - ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap); + ob_debug("Setting client %s colormap: 0x%x", self->title, colormap); if (client_focused(self)) { screen_install_colormap(self, FALSE); /* uninstall old one */ @@ -1588,7 +1541,7 @@ void client_update_normal_hints(ObClient *self) self->min_ratio = 0.0f; self->max_ratio = 0.0f; SIZE_SET(self->size_inc, 1, 1); - SIZE_SET(self->base_size, 0, 0); + SIZE_SET(self->base_size, -1, -1); SIZE_SET(self->min_size, 0, 0); SIZE_SET(self->max_size, G_MAXINT, G_MAXINT); @@ -1624,15 +1577,15 @@ void client_update_normal_hints(ObClient *self) if (size.flags & PResizeInc && size.width_inc && size.height_inc) SIZE_SET(self->size_inc, size.width_inc, size.height_inc); - ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n " - "size inc (%d %d) base size (%d %d)\n", + ob_debug("Normal hints: min size (%d %d) max size (%d %d)", self->min_size.width, self->min_size.height, - self->max_size.width, self->max_size.height, + self->max_size.width, self->max_size.height); + ob_debug("size inc (%d %d) base size (%d %d)", self->size_inc.width, self->size_inc.height, self->base_size.width, self->base_size.height); } else - ob_debug("Normal hints: not set\n"); + ob_debug("Normal hints: not set"); } void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) @@ -1832,16 +1785,16 @@ static void client_change_allowed_actions(ObClient *self) OBT_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; @@ -1946,6 +1899,7 @@ void client_update_title(ObClient *self) gchar *visible = NULL; g_free(self->title); + g_free(self->original_title); /* try netwm */ if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) { @@ -1962,6 +1916,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); @@ -1971,7 +1926,7 @@ void client_update_title(ObClient *self) if (self->not_responding) { data = visible; - if (self->close_tried_term) + if (self->kill_level > 0) visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); else visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); @@ -2003,7 +1958,7 @@ void client_update_title(ObClient *self) if (self->not_responding) { data = visible; - if (self->close_tried_term) + if (self->kill_level > 0) visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); else visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); @@ -2074,101 +2029,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 (OBT_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(obt_display, self->window))) { if (hints->flags & IconPixmapHint) { - self->nicons = 1; - self->icons = g_new(ObClientIcon, self->nicons); + gboolean xicon; obt_display_ignore_errors(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); obt_display_ignore_errors(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); - OBT_PROP_SETA32(self->window, NET_WM_ICON, CARDINAL, data, 48*48+2); - g_free(data); + OBT_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) @@ -2179,11 +2162,13 @@ void client_update_icon_geometry(ObClient *self) RECT_SET(self->icon_geometry, 0, 0, 0, 0); if (OBT_PROP_GETA32(self->window, NET_WM_ICON_GEOMETRY, CARDINAL, - &data, &num) && num == 4) + &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); } } @@ -2421,7 +2406,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; @@ -2447,23 +2440,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) @@ -2483,6 +2507,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; @@ -2524,6 +2552,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; @@ -2600,7 +2632,7 @@ static void client_apply_startup_state(ObClient *self, pre-max/pre-fullscreen values */ client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE); - ob_debug("placed window 0x%x at %d, %d with size %d x %d\n", + ob_debug("placed window 0x%x at %d, %d with size %d x %d", self->window, x, y, w, h); /* save the area, and make it where it should be for the premax stuff */ oldarea = self->area; @@ -2785,7 +2817,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, 0 : self->max_ratio; /* base size is substituted with min size if not specified */ - if (self->base_size.width || self->base_size.height) { + if (self->base_size.width >= 0 || self->base_size.height >= 0) { basew = self->base_size.width; baseh = self->base_size.height; } else { @@ -2881,6 +2913,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; @@ -2903,6 +2936,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. @@ -2937,6 +2971,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) @@ -2980,7 +3018,7 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, event.xconfigure.event = self->window; event.xconfigure.window = self->window; - ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n", + ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d", self->title, self->root_pos.x, self->root_pos.y, w, h); /* root window real coords */ @@ -3007,6 +3045,14 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, } XFlush(obt_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) @@ -3049,7 +3095,7 @@ 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", + ob_debug("Window %s going fullscreen (%d)", self->title, self->fullscreen); client_setup_decor_and_functions(self, FALSE); @@ -3075,7 +3121,7 @@ static void client_iconify_recursive(ObClient *self, if (self->iconic != iconic) { - ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"), + ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"), self->window); if (iconic) { @@ -3215,13 +3261,23 @@ void client_shade(ObClient *self, gboolean shade) static void client_ping_event(ObClient *self, gboolean dead) { - self->not_responding = dead; - client_update_title(self); + if (self->not_responding != dead) { + 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; + 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; + } } } @@ -3229,42 +3285,114 @@ 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(obt_display, self->window); - else if (self->not_responding) - client_kill(self); - else + else { /* request the client to close with WM_DELETE_WINDOW */ OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS, OBT_PROP_ATOM(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); + + 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[] = { + { _("Cancel"), OB_KILL_RESULT_NO }, + { _("Force Exit"), 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); + + + 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, TRUE); } void client_kill(ObClient *self) { - if (!self->client_machine && self->pid) { + /* don't kill our own windows */ + if (self->prompt) return; + + if (client_on_localhost(self) && self->pid) { /* running on the local host */ - if (!self->close_tried_term) { - ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n", + 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->close_tried_term = TRUE; + ++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", + ob_debug("killing window 0x%x with pid %lu, with SIGKILL", self->window, self->pid); kill(self->pid, SIGKILL); /* kill -9 */ } } - else + else { + /* running on a remote host */ XKillClient(obt_display, self->window); + } } void client_hilite(ObClient *self, gboolean hilite) @@ -3293,7 +3421,7 @@ static void client_set_desktop_recursive(ObClient *self, if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) { - ob_debug("Setting desktop %u\n", target+1); + ob_debug("Setting desktop %u", target+1); g_assert(target < screen_num_desktops || target == DESKTOP_ALL); @@ -3595,12 +3723,12 @@ gboolean client_focus(ObClient *self) if (!client_can_focus(self)) { ob_debug_type(OB_DEBUG_FOCUS, - "Client %s can't be focused\n", self->title); + "Client %s can't be focused", self->title); return FALSE; } ob_debug_type(OB_DEBUG_FOCUS, - "Focusing client \"%s\" (0x%x) at time %u\n", + "Focusing client \"%s\" (0x%x) at time %u", self->title, self->window, event_curtime); /* if using focus_delay, stop the timer now so that focus doesn't @@ -3642,7 +3770,7 @@ gboolean client_focus(ObClient *self) obt_display_ignore_errors(FALSE); - ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", + ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d", obt_display_error_occured); return !obt_display_error_occured; } @@ -3718,53 +3846,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; } @@ -3969,15 +4065,15 @@ static void detect_edge(Rect area, ObDirection dir, 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); + ob_debug("my head %d size %d", my_head, my_size); + ob_debug("head %d tail %d deest %d", head, tail, *dest); if (!skip_head) { - ob_debug("using near edge %d\n", head); + ob_debug("using near edge %d", head); *dest = head; *near_edge = TRUE; } else if (!skip_tail) { - ob_debug("using far edge %d\n", tail); + ob_debug("using far edge %d", tail); *dest = tail; *near_edge = FALSE; } @@ -4042,7 +4138,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, cur->desktop != screen_desktop) continue; - ob_debug("trying window %s\n", cur->title); + ob_debug("trying window %s", cur->title); detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start, my_edge_size, dest, near_edge); @@ -4163,10 +4259,10 @@ void client_find_resize_directional(ObClient *self, ObDirection side, g_assert_not_reached(); } - ob_debug("head %d dir %d\n", head, dir); + ob_debug("head %d dir %d", head, dir); client_find_edge_directional(self, dir, head, 1, e_start, e_size, &e, &near); - ob_debug("edge %d\n", e); + ob_debug("edge %d", e); *x = self->frame->area.x; *y = self->frame->area.y; *w = self->frame->area.width; @@ -4235,3 +4331,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; +}