#include "session.h"
#include "event.h"
#include "grab.h"
+#include "prompt.h"
#include "focus.h"
#include "stacking.h"
#include "openbox.h"
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)
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);
- }
- }
-
- /* 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;
- 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", 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);
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 */
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);
/* 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)",
window, self->frame->window, self->class);
-
- return;
}
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);
window_remove(self->window);
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);
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.
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"));
(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);
- 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)
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;
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)
}
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)
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)
+ /* 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;
+ }
- if (!dead) {
- /* try kill it nicely the first time again, if it started responding
- at some point */
- self->close_tried_term = FALSE;
+ 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[] = {
+ { _("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);
+
+
+ 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)
{
- 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) {
+ 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);
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)
{
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;
+}