#include "session.h"
#include "event.h"
#include "grab.h"
+#include "prompt.h"
#include "focus.h"
#include "stacking.h"
#include "openbox.h"
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);
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();
void client_shutdown(gboolean reconfig)
{
+ RrImageUnref(client_default_icon);
+ client_default_icon = NULL;
+
if (reconfig) return;
}
} else
windows = NULL;
- OBT_PROP_SETA32(RootWindow(obt_display, ob_screen),
- NET_CLIENT_LIST, WINDOW, (gulong*)windows, size);
+ OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
+ (gulong*)windows, size);
if (windows)
g_free(windows);
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);
/* create the ObClient struct, and populate it from the hints on the
window */
self = g_new0(ObClient, 1);
- self->obwin.type = Window_Client;
+ 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 */
/* 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);
/* 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. */
/* 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" :
"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" :
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;
}
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);
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;
/* 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);
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 */
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 &&
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.
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 */
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))
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 */
/* add to client list/map */
client_list = g_list_append(client_list, self);
- g_hash_table_insert(window_map, &self->window, self);
+ window_add(&self->window, CLIENT_AS_WINDOW(self));
/* this has to happen after we're in the client_list */
if (STRUT_EXISTS(self->strut))
/* 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;
}
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 */
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);
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 : "");
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);
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);
+ window_remove(self->window);
/* once the client is out of the list, update the struts to remove its
influence */
/* 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);
!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 */
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);
gint fw, fh;
Rect desired;
guint i;
+ gboolean found_mon;
RECT_SET(desired, *x, *y, w, h);
frame_rect_to_frame(self->frame, &desired);
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.
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);
}
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;
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 */
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);
}
}
if (XGetTransientForHint(obt_display, self->window, &t)) {
if (t != self->window) { /* cant be transient to itself! */
- target = g_hash_table_lookup(window_map, &t);
+ ObWindow *tw = window_find(t);
/* if this happens then we need to check for it*/
- g_assert(target != self);
- if (target && !WINDOW_IS_CLIENT(target)) {
- /* this can happen when a dialog is a child of
- a dockapp, for example */
- target = NULL;
+ g_assert(tw != CLIENT_AS_WINDOW(self));
+ if (tw && WINDOW_IS_CLIENT(tw)) {
+ /* watch out for windows with a parent that is something
+ different, like a dockapp for example */
+ target = WINDOW_AS_CLIENT(tw);
}
}
/* Setting the transient_for to Root is actually illegal, however
applications from time have done this to specify transient for
their group */
- if (!target && self->group && t == RootWindow(obt_display, ob_screen))
+ if (!target && self->group && t == obt_root(ob_screen))
trangroup = TRUE;
} else if (self->group && self->transient)
trangroup = TRUE;
{
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 */
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);
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)
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;
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)) {
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);
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"));
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"));
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)
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);
}
}
(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;
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)
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;
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;
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;
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 {
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;
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.
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)
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 */
}
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)
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);
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) {
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;
+ }
}
}
{
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)
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);
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
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;
}
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;
}
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;
}
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);
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;
{
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;
+}