client.c for the Openbox window manager
Copyright (c) 2006 Mikael Magnusson
- Copyright (c) 2003 Ben Jansens
+ Copyright (c) 2003-2007 Dana Jansens
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <X11/Xutil.h>
/*! The event mask to grab on client windows */
-#define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
- StructureNotifyMask)
+#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask)
#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
ButtonMotionMask)
static void client_get_shaped(ObClient *self);
static void client_get_mwm_hints(ObClient *self);
static void client_get_gravity(ObClient *self);
-static void client_showhide(ObClient *self);
static void client_change_allowed_actions(ObClient *self);
static void client_change_state(ObClient *self);
-static void client_apply_startup_state(ObClient *self);
+static void client_change_wm_state(ObClient *self);
+static void client_apply_startup_state(ObClient *self, gint x, gint y);
static void client_restore_session_state(ObClient *self);
static void client_restore_session_stacking(ObClient *self);
+static ObAppSettings *client_get_settings_state(ObClient *self);
void client_startup(gboolean reconfig)
{
XFree(children);
}
-static ObAppSettings *get_settings(ObClient *client)
-{
- GSList *a = config_per_app_settings;
-
- while (a) {
- ObAppSettings *app = (ObAppSettings *) a->data;
-
- if (
- (app->name && !app->class && !strcmp(app->name, client->name))
- || (app->class && !app->name && !strcmp(app->class, client->class))
- || (app->class && app->name && !strcmp(app->class, client->class)
- && !strcmp(app->name, client->name))
- ) {
- ob_debug("Window matching: %s\n", app->name);
- /* Match if no role was specified in the per app setting, or if the
- * string matches the beginning of the role, since apps like to set
- * the role to things like browser-window-23c4b2f */
- if (!app->role
- || !strncmp(app->role, client->role, strlen(app->role)))
- return app;
- }
-
- a = a->next;
- }
- return NULL;
-}
-
void client_manage(Window window)
{
ObClient *self;
XWMHints *wmhint;
gboolean activate = FALSE;
ObAppSettings *settings;
+ gint newx, newy;
grab_server(TRUE);
/* non-zero defaults */
self->title_count = 1;
- self->wmstate = NormalState;
+ self->wmstate = WithdrawnState; /* make sure it gets updated first time */
self->layer = -1;
self->desktop = screen_num_desktops; /* always an invalid value */
self->user_time = ~0; /* maximum value, always newer than the real time */
client_get_all(self);
client_restore_session_state(self);
+ /* per-app settings override stuff, and return the settings for other
+ uses too */
+ settings = client_get_settings_state(self);
client_calc_layer(self);
it can end up in the list twice! */
focus_order_add_new(self);
- client_change_state(self);
-
/* remove the client's border (and adjust re gravity) */
client_toggle_border(self, FALSE);
frame_grab_client(self->frame, self);
- grab_server(FALSE);
-
- client_apply_startup_state(self);
+ /* do this after we have a frame.. it uses the frame to help determine the
+ WM_STATE to apply. */
+ client_change_state(self);
- /* get and set application level settings */
- settings = get_settings(self);
+ grab_server(FALSE);
stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
client_restore_session_stacking(self);
- if (settings) {
- /* Don't worry, we won't actually both shade and undecorate the
- * window when push comes to shove. */
- if (settings->shade != -1)
- client_shade(self, !!settings->shade);
- if (settings->decor != -1)
- client_set_undecorated(self, !settings->decor);
- if (settings->iconic != -1)
- client_iconify(self, !!settings->iconic, FALSE);
- if (settings->skip_pager != -1) {
- self->skip_pager = !!settings->skip_pager;
- client_change_state(self);
- }
- if (settings->skip_taskbar != -1) {
- self->skip_taskbar = !!settings->skip_taskbar;
- client_change_state(self);
- }
-
- /* 1 && -1 shouldn't be possible by the code in config.c */
- if (settings->max_vert == 1 && settings->max_horz == 1)
- client_maximize(self, TRUE, 0, TRUE);
- else if (settings->max_vert == 0 && settings->max_horz == 0)
- client_maximize(self, FALSE, 0, TRUE);
- else if (settings->max_vert == 1 && settings->max_horz == 0) {
- client_maximize(self, TRUE, 2, TRUE);
- client_maximize(self, FALSE, 1, TRUE);
- } else if (settings->max_vert == 0 && settings->max_horz == 1) {
- client_maximize(self, TRUE, 1, TRUE);
- client_maximize(self, FALSE, 2, TRUE);
- }
-
- if (settings->fullscreen != -1)
- client_fullscreen(self, !!settings->fullscreen, TRUE);
-
- if (settings->desktop < screen_num_desktops
- || settings->desktop == DESKTOP_ALL)
- client_set_desktop(self, settings->desktop, TRUE);
-
- if (settings->layer > -2 && settings->layer < 2)
- client_set_layer(self, settings->layer);
-
- }
-
/* focus the new window? */
if (ob_state() != OB_STATE_STARTING &&
/* this means focus=true for window is same as config_focus_new=true */
#endif
}
+ /* get the current position */
+ newx = self->area.x;
+ newy = self->area.y;
+
+ /* figure out placement for the window */
if (ob_state() == OB_STATE_RUNNING) {
- gint x = self->area.x, ox = x;
- gint y = self->area.y, oy = y;
gboolean transient;
- transient = place_client(self, &x, &y, settings);
+ transient = place_client(self, &newx, &newy, settings);
/* make sure the window is visible. */
- client_find_onscreen(self, &x, &y,
+ client_find_onscreen(self, &newx, &newy,
self->frame->area.width,
self->frame->area.height,
/* non-normal clients has less rules, and
!(self->positioned & USPosition)) &&
client_normal(self) &&
!self->session));
- if (x != ox || y != oy)
- client_move(self, x, y);
}
+ /* do this after the window is placed, so the premax/prefullscreen numbers
+ won't be all wacko!!
+ also, this moves the window to the position where it has been placed
+ */
+ client_apply_startup_state(self, newx, newy);
+
keyboard_grab_for_client(self, TRUE);
mouse_grab_for_client(self, TRUE);
always
*/
if (!focus_client || client_search_focus_parent(self) != NULL)
- {
activate = TRUE;
- }
else
{
/* If time stamp is old, don't steal focus */
I stole this idea from KWin. It seems nice.
*/
if (!focus_client->can_focus && focus_client->focus_notify)
- {
activate = FALSE;
- }
}
if (activate)
guint j;
GSList *it;
- ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
+ ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class,
+ self->title ? self->title : "");
g_assert(self != NULL);
/* reparent the window out of the frame, and free the frame */
frame_release_client(self->frame, self);
self->frame = NULL;
+
+ /* restore the window's original geometry so it is not lost */
+ if (self->fullscreen)
+ XMoveResizeWindow(ob_display, self->window,
+ self->pre_fullscreen_area.x,
+ self->pre_fullscreen_area.y,
+ self->pre_fullscreen_area.width,
+ self->pre_fullscreen_area.height);
+ else if (self->max_horz || self->max_vert) {
+ Rect a = self->area;
+ if (self->max_horz) {
+ a.x = self->pre_max_area.x;
+ a.width = self->pre_max_area.width;
+ }
+ if (self->max_vert) {
+ a.y = self->pre_max_area.y;
+ a.height = self->pre_max_area.height;
+ }
+ XMoveResizeWindow(ob_display, self->window,
+ a.x, a.y, a.width, a.height);
+ }
if (ob_state() != OB_STATE_EXITING) {
/* these values should not be persisted across a window
grab_pointer(FALSE, OB_CURSOR_NONE);
}
+static ObAppSettings *client_get_settings_state(ObClient *self)
+{
+ ObAppSettings *settings = NULL;
+ GSList *it;
+
+ for (it = config_per_app_settings; it; it = g_slist_next(it)) {
+ ObAppSettings *app = it->data;
+
+ if ((app->name && !app->class && !strcmp(app->name, self->name))
+ || (app->class && !app->name && !strcmp(app->class, self->class))
+ || (app->class && app->name && !strcmp(app->class, self->class)
+ && !strcmp(app->name, self->name)))
+ {
+ ob_debug("Window matching: %s\n", app->name);
+ /* Match if no role was specified in the per app setting, or if the
+ * string matches the beginning of the role, since apps like to set
+ * the role to things like browser-window-23c4b2f */
+ if (!app->role
+ || !strncmp(app->role, self->role, strlen(app->role)))
+ {
+ /* use this one */
+ settings = app;
+ break;
+ }
+ }
+ }
+
+ if (settings) {
+ if (settings->shade != -1)
+ self->shaded = !!settings->shade;
+ if (settings->decor != -1)
+ self->undecorated = !settings->decor;
+ if (settings->iconic != -1)
+ self->iconic = !!settings->iconic;
+ if (settings->skip_pager != -1)
+ self->skip_pager = !!settings->skip_pager;
+ if (settings->skip_taskbar != -1)
+ self->skip_taskbar = !!settings->skip_taskbar;
+
+ if (settings->max_vert != -1)
+ self->max_vert = !!settings->max_vert;
+ if (settings->max_horz != -1)
+ self->max_vert = !!settings->max_horz;
+
+ if (settings->fullscreen != -1)
+ self->fullscreen = !!settings->fullscreen;
+
+ if (settings->desktop < screen_num_desktops
+ || settings->desktop == DESKTOP_ALL)
+ client_set_desktop(self, settings->desktop, TRUE);
+
+ if (settings->layer == -1) {
+ self->below = TRUE;
+ self->above = FALSE;
+ }
+ else if (settings->layer == 0) {
+ self->below = FALSE;
+ self->above = FALSE;
+ }
+ else if (settings->layer == 1) {
+ self->below = FALSE;
+ self->above = TRUE;
+ }
+ }
+ return settings;
+}
+
static void client_restore_session_state(ObClient *self)
{
GList *it;
RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
self->border_width = wattrib.border_width;
+
+ ob_debug("client area: %d %d %d %d\n", wattrib.x, wattrib.y,
+ wattrib.width, wattrib.height);
}
static void client_get_desktop(ObClient *self)
}
}
}
- } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
- self->transient = TRUE;
- target = OB_TRAN_GROUP;
+ } else if (self->group) {
+ if (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR ||
+ self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_UTILITY)
+ {
+ self->transient = TRUE;
+ target = OB_TRAN_GROUP;
+ }
} else
self->transient = FALSE;
else self->iconic = FALSE;
}
if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
- if (self->frame) client_fullscreen(self, FALSE, TRUE);
+ if (self->frame) client_fullscreen(self, FALSE);
else self->fullscreen = FALSE;
}
if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
self->max_vert)) {
- if (self->frame) client_maximize(self, FALSE, 0, TRUE);
+ if (self->frame) client_maximize(self, FALSE, 0);
else self->max_vert = self->max_horz = FALSE;
}
}
}
}
-static void client_change_state(ObClient *self)
+static void client_change_wm_state(ObClient *self)
{
gulong state[2];
+ glong old;
+
+ old = self->wmstate;
+
+ if (self->shaded || self->iconic || !self->frame->visible)
+ self->wmstate = IconicState;
+ else
+ self->wmstate = NormalState;
+
+ if (old != self->wmstate) {
+ PROP_MSG(self->window, kde_wm_change_state,
+ self->wmstate, 1, 0, 0);
+
+ state[0] = self->wmstate;
+ state[1] = None;
+ PROP_SETA32(self->window, wm_state, wm_state, state, 2);
+ }
+}
+
+static void client_change_state(ObClient *self)
+{
gulong netstate[11];
guint num;
- state[0] = self->wmstate;
- state[1] = None;
- PROP_SETA32(self->window, wm_state, wm_state, state, 2);
-
num = 0;
if (self->modal)
netstate[num++] = prop_atoms.net_wm_state_modal;
own = calc_layer(self);
self->layer = MAX(own, min);
- ob_debug("layer for %s: %d\n", self->title, self->layer);
-
for (it = self->transients; it; it = g_slist_next(it))
client_calc_layer_recursive(it->data, orig,
self->layer,
orig = self;
/* transients take on the layer of their parents */
- it = client_search_top_transients(self);
+ it = client_search_all_top_parents(self);
for (; it; it = g_slist_next(it))
client_calc_layer_recursive(it->data, orig, 0, FALSE);
return FALSE;
}
-static void client_showhide(ObClient *self)
+void client_showhide(ObClient *self)
{
- if (client_should_show(self))
- frame_show(self->frame);
- else
+ if (client_should_show(self)) {
+ if (!self->frame->visible)
+ frame_show(self->frame);
+ }
+ else if (self->frame->visible) {
frame_hide(self->frame);
+
+ /* Fall back focus since we're disappearing */
+ if (focus_client == self)
+ client_unfocus(self);
+ }
+
+ /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
+ needs to be in IconicState. This includes when it is on another
+ desktop!
+ */
+ client_change_wm_state(self);
}
gboolean client_normal(ObClient *self) {
self->type == OB_CLIENT_TYPE_SPLASH);
}
-static void client_apply_startup_state(ObClient *self)
+static void client_apply_startup_state(ObClient *self, gint x, gint y)
{
+ gboolean pos = FALSE; /* has the window's position been configured? */
+ gint ox, oy;
+
+ /* save the position, and set self->area for these to use */
+ ox = self->area.x;
+ oy = self->area.y;
+ self->area.x = x;
+ self->area.y = y;
+
/* these are in a carefully crafted order.. */
if (self->iconic) {
}
if (self->fullscreen) {
self->fullscreen = FALSE;
- client_fullscreen(self, TRUE, FALSE);
+ client_fullscreen(self, TRUE);
+ pos = TRUE;
}
if (self->undecorated) {
self->undecorated = FALSE;
if (self->max_vert && self->max_horz) {
self->max_vert = self->max_horz = FALSE;
- client_maximize(self, TRUE, 0, FALSE);
+ client_maximize(self, TRUE, 0);
+ pos = TRUE;
} else if (self->max_vert) {
self->max_vert = FALSE;
- client_maximize(self, TRUE, 2, FALSE);
+ client_maximize(self, TRUE, 2);
+ pos = TRUE;
} else if (self->max_horz) {
self->max_horz = FALSE;
- client_maximize(self, TRUE, 1, FALSE);
+ client_maximize(self, TRUE, 1);
+ pos = TRUE;
+ }
+
+ /* if the client didn't get positioned yet, then do so now */
+ if (!pos && (ox != x || oy != y)) {
+ /* use the saved position */
+ self->area.x = ox;
+ self->area.y = oy;
+ client_move(self, x, y);
}
/* nothing to do for the other states:
XFlush(ob_display);
}
-void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
+void client_fullscreen(ObClient *self, gboolean fs)
{
gint x, y, w, h;
client_calc_layer(self); /* and adjust out layer/stacking */
if (fs) {
- if (savearea)
- self->pre_fullscreen_area = self->area;
+ self->pre_fullscreen_area = self->area;
+ /* if the window is maximized, its area isn't all that meaningful.
+ save it's premax area instead. */
+ if (self->max_horz) {
+ self->pre_fullscreen_area.x = self->pre_max_area.x;
+ self->pre_fullscreen_area.width = self->pre_max_area.width;
+ }
+ if (self->max_vert) {
+ self->pre_fullscreen_area.y = self->pre_max_area.y;
+ self->pre_fullscreen_area.height = self->pre_max_area.height;
+ }
/* these are not actually used cuz client_configure will set them
as appropriate when the window is fullscreened */
if (iconic) {
if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
- glong old;
-
- old = self->wmstate;
- self->wmstate = IconicState;
- if (old != self->wmstate)
- PROP_MSG(self->window, kde_wm_change_state,
- self->wmstate, 1, 0, 0);
-
/* update the focus lists.. iconic windows go to the bottom of
the list, put the new iconic window at the 'top of the
bottom'. */
changed = TRUE;
}
} else {
- glong old;
-
if (curdesk)
client_set_desktop(self, screen_desktop, FALSE);
- old = self->wmstate;
- self->wmstate = self->shaded ? IconicState : NormalState;
- if (old != self->wmstate)
- PROP_MSG(self->window, kde_wm_change_state,
- self->wmstate, 1, 0, 0);
-
/* this puts it after the current focused window */
focus_order_remove(self);
focus_order_add_new(self);
screen_update_areas();
}
- /* iconify all transients */
+ /* iconify all direct transients */
for (it = self->transients; it; it = g_slist_next(it))
- if (it->data != self) client_iconify_recursive(it->data,
- iconic, curdesk);
+ if (it->data != self)
+ if (client_is_direct_child(self, it->data))
+ client_iconify_recursive(it->data, iconic, curdesk);
}
void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
{
- GSList *it;
-
/* move up the transient chain as far as possible first */
- it = client_search_top_transients(self);
-
- for (; it; it = g_slist_next(it))
- client_iconify_recursive(it->data, iconic, curdesk);
+ self = client_search_top_parent(self);
+ client_iconify_recursive(self, iconic, curdesk);
}
-void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
+void client_maximize(ObClient *self, gboolean max, gint dir)
{
gint x, y, w, h;
h = self->area.height;
if (max) {
- if (savearea) {
- if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
- RECT_SET(self->pre_max_area,
- self->area.x, self->pre_max_area.y,
- self->area.width, self->pre_max_area.height);
- }
- if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
- RECT_SET(self->pre_max_area,
- self->pre_max_area.x, self->area.y,
- self->pre_max_area.width, self->area.height);
- }
+ if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
+ RECT_SET(self->pre_max_area,
+ self->area.x, self->pre_max_area.y,
+ self->area.width, self->pre_max_area.height);
+ }
+ if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
+ RECT_SET(self->pre_max_area,
+ self->pre_max_area.x, self->area.y,
+ self->pre_max_area.width, self->area.height);
}
} else {
Rect *a;
shade) || /* can't shade */
self->shaded == shade) return; /* already done */
- /* when we're iconic, don't change the wmstate */
- if (!self->iconic) {
- glong old;
-
- old = self->wmstate;
- self->wmstate = shade ? IconicState : NormalState;
- if (old != self->wmstate)
- PROP_MSG(self->window, kde_wm_change_state,
- self->wmstate, 1, 0, 0);
- }
-
self->shaded = shade;
client_change_state(self);
+ client_change_wm_state(self); /* the window is being hidden/shown */
/* resize the frame to just the titlebar */
frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
}
void client_hilite(ObClient *self, gboolean hilite)
{
+ if (self->demands_attention == hilite)
+ return; /* no change */
+
/* don't allow focused windows to hilite */
self->demands_attention = hilite && !client_focused(self);
if (self->demands_attention)
/* move all transients */
for (it = self->transients; it; it = g_slist_next(it))
- if (it->data != self) client_set_desktop_recursive(it->data,
- target, donthide);
+ if (it->data != self)
+ if (client_is_direct_child(self, it->data))
+ client_set_desktop_recursive(it->data, target, donthide);
}
void client_set_desktop(ObClient *self, guint target, gboolean donthide)
{
- GSList *it;
-
- it = client_search_top_transients(self);
+ self = client_search_top_parent(self);
+ client_set_desktop_recursive(self, target, donthide);
+}
- for(; it; it = g_slist_next(it))
- client_set_desktop_recursive(it->data, target, donthide);
+gboolean client_is_direct_child(ObClient *parent, ObClient *child)
+{
+ while (child != parent &&
+ child->transient_for && child->transient_for != OB_TRAN_GROUP)
+ child = child->transient_for;
+ return child == parent;
}
ObClient *client_search_modal_child(ObClient *self)
if (max_horz != self->max_horz && max_vert != self->max_vert) {
/* toggling both */
if (max_horz == max_vert) { /* both going the same way */
- client_maximize(self, max_horz, 0, TRUE);
+ client_maximize(self, max_horz, 0);
} else {
- client_maximize(self, max_horz, 1, TRUE);
- client_maximize(self, max_vert, 2, TRUE);
+ client_maximize(self, max_horz, 1);
+ client_maximize(self, max_vert, 2);
}
} else {
/* toggling one */
if (max_horz != self->max_horz)
- client_maximize(self, max_horz, 1, TRUE);
+ client_maximize(self, max_horz, 1);
else
- client_maximize(self, max_vert, 2, TRUE);
+ client_maximize(self, max_vert, 2);
}
}
/* change fullscreen state before shading, as it will affect if the window
can shade or not */
if (fullscreen != self->fullscreen)
- client_fullscreen(self, fullscreen, TRUE);
+ client_fullscreen(self, fullscreen);
if (shaded != self->shaded)
client_shade(self, shaded);
if (undecorated != self->undecorated)
/* choose the correct target */
self = client_focus_target(self);
+#if 0
+ if (!client_validate(self))
+ return FALSE;
+#endif
+
if (!client_can_focus(self)) {
if (!self->frame->visible) {
/* update the focus lists */
return FALSE;
}
+ ob_debug("Focusing client \"%s\" at time %u\n", self->title, event_curtime);
+
if (self->can_focus) {
/* RevertToPointerRoot causes much more headache than RevertToNone, so
I choose to use it always, hopefully to find errors quicker, if any
return TRUE;
}
-/* Used when the current client is closed, focus_last will then prevent
- * focus from going to the mouse pointer */
+/* Used when the current client is closed or otherwise hidden, focus_last will
+ then prevent focus from going to the mouse pointer
+*/
void client_unfocus(ObClient *self)
{
if (focus_client == self) {
return screen_find_monitor(&self->frame->area);
}
-GSList *client_search_top_transients(ObClient *self)
+ObClient *client_search_top_parent(ObClient *self)
+{
+ while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
+ self = self->transient_for;
+ return self;
+}
+
+GSList *client_search_all_top_parents(ObClient *self)
{
GSList *ret = NULL;