#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)
/* 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 = nchild; i > 0; --i) {
- if (children[i--1] == None)
+ for (i = 0; i < nchild; ++i) {
+ if (children[i] == None)
continue;
- if (XGetWindowAttributes(ob_display, children[i-1], &attrib)) {
+ if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
if (attrib.override_redirect) continue;
if (attrib.map_state != IsUnmapped)
- client_manage(children[i-1]);
+ client_manage(children[i], NULL);
}
}
XFree(children);
}
-void client_manage(Window window)
+void client_manage(Window window, ObPrompt *prompt)
{
ObClient *self;
XEvent e;
map_time = event_get_server_time();
- /* choose the events we want to receive on the CLIENT window */
- attrib_set.event_mask = CLIENT_EVENTMASK;
+ /* choose the events we want to receive on the CLIENT window
+ (ObPrompt windows can request events too) */
+ attrib_set.event_mask = CLIENT_EVENTMASK |
+ (prompt ? prompt->event_mask : 0);
attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
XChangeWindowAttributes(ob_display, window,
CWEventMask|CWDontPropagate, &attrib_set);
self = g_new0(ObClient, 1);
self->obwin.type = Window_Client;
self->window = window;
+ self->prompt = prompt;
/* non-zero defaults */
self->wmstate = WithdrawnState; /* make sure it gets updated first time */
client_setup_decor_and_functions(self, FALSE);
/* specify that if we exit, the window should not be destroyed and
- should be reparented back to root automatically */
- XChangeSaveSet(ob_display, window, SetModeInsert);
+ should be reparented back to root automatically, unless we are managing
+ an internal ObPrompt window */
+ if (!self->prompt)
+ XChangeSaveSet(ob_display, window, SetModeInsert);
/* create the decoration frame for the client window */
self->frame = frame_new(self);
mouse_grab_for_client(self, FALSE);
- /* remove the window from our save set */
- XChangeSaveSet(ob_display, self->window, SetModeDelete);
+ /* remove the window from our save set, unless we are managing an internal
+ ObPrompt window */
+ if (!self->prompt)
+ XChangeSaveSet(ob_display, self->window, SetModeDelete);
/* update the focus lists */
focus_order_remove(self);
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);
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);
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.
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 (!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);
+
+ /* 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;
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(ob_display);
+
+ /* if it moved between monitors, then this can affect the stacking
+ layer of this window or others - for fullscreen windows */
+ if (screen_find_monitor(&self->frame->area) !=
+ screen_find_monitor(&oldframe))
+ {
+ client_calc_layer(self);
+ }
}
void client_fullscreen(ObClient *self, gboolean fs)
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;
+ /* 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 (self->prompt) {
+ prompt_cancel(self->prompt);
+ return;
+ }
+
/* in the case that the client provides no means to requesting that it
close, we just kill it */
if (!self->delete_window)
/* don't use client_kill(), we should only kill based on PID in
response to a lack of PING replies */
XKillClient(ob_display, self->window);
- else if (self->not_responding)
- client_kill(self);
- else
+ else {
/* request the client to close with WM_DELETE_WINDOW */
PROP_MSG_TO(self->window, self->window, wm_protocols,
prop_atoms.wm_delete_window, event_curtime, 0, 0, 0,
NoEventMask);
+
+ 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;
+
+ m = g_strdup_printf
+ (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit?"), 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)
{
+ /* don't kill our own windows */
+ if (self->prompt) return;
+
if (!self->client_machine && self->pid) {
/* running on the local host */
- if (!self->close_tried_term) {
- ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n",
+ 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(ob_display, self->window);
+ }
}
void client_hilite(ObClient *self, gboolean hilite)