#include "event.h"
#include "grab.h"
#include "focus.h"
+#include "propwin.h"
#include "stacking.h"
#include "openbox.h"
#include "group.h"
GList *client_list = NULL;
-static GSList *client_destructors = NULL;
+static GSList *client_destroy_notifies = NULL;
-static void client_get_all(ObClient *self);
+static void client_get_all(ObClient *self, gboolean real);
static void client_toggle_border(ObClient *self, gboolean show);
static void client_get_startup_id(ObClient *self);
static void client_get_session_ids(ObClient *self);
static void client_get_area(ObClient *self);
static void client_get_desktop(ObClient *self);
static void client_get_state(ObClient *self);
-static void client_get_layer(ObClient *self);
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_get_colormap(ObClient *self);
-static void client_get_transientness(ObClient *self);
static void client_change_allowed_actions(ObClient *self);
static void client_change_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_apply_startup_state(ObClient *self);
static void client_restore_session_state(ObClient *self);
static gboolean client_restore_session_stacking(ObClient *self);
static ObAppSettings *client_get_settings_state(ObClient *self);
static GSList *client_search_all_top_parents_internal(ObClient *self,
gboolean bylayer,
ObStackingLayer layer);
+static void client_call_notifies(ObClient *self, GSList *list);
void client_startup(gboolean reconfig)
{
void client_shutdown(gboolean reconfig)
{
+ if (reconfig) return;
+}
+
+static void client_call_notifies(ObClient *self, GSList *list)
+{
+ GSList *it;
+
+ for (it = list; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ d->func(self, d->data);
+ }
}
-void client_add_destructor(ObClientCallback func, gpointer data)
+void client_add_destroy_notify(ObClientCallback func, gpointer data)
{
ClientCallback *d = g_new(ClientCallback, 1);
d->func = func;
d->data = data;
- client_destructors = g_slist_prepend(client_destructors, d);
+ client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
}
-void client_remove_destructor(ObClientCallback func)
+void client_remove_destroy_notify(ObClientCallback func)
{
GSList *it;
- for (it = client_destructors; it; it = g_slist_next(it)) {
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
ClientCallback *d = it->data;
if (d->func == func) {
g_free(d);
- client_destructors = g_slist_delete_link(client_destructors, it);
+ client_destroy_notifies =
+ g_slist_delete_link(client_destroy_notifies, it);
break;
}
}
XWMHints *wmhint;
gboolean activate = FALSE;
ObAppSettings *settings;
- gint newx, newy;
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 */
+ /* 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(ob_display, window, DestroyNotify, &e) ||
XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
{
XChangeWindowAttributes(ob_display, window,
CWEventMask|CWDontPropagate, &attrib_set);
-
/* create the ObClient struct, and populate it from the hints on the
window */
self = g_new0(ObClient, 1);
/* non-zero defaults */
self->wmstate = WithdrawnState; /* make sure it gets updated first time */
- self->layer = -1;
+ self->gravity = NorthWestGravity;
self->desktop = screen_num_desktops; /* always an invalid value */
self->user_time = focus_client ? focus_client->user_time : CurrentTime;
- client_get_all(self);
- /* per-app settings override stuff, and return the settings for other
- uses too */
+ /* get all the stuff off the window */
+ client_get_all(self, TRUE);
+
+ /* specify that if we exit, the window should not be destroyed and
+ should be reparented back to root automatically */
+ XChangeSaveSet(ob_display, window, SetModeInsert);
+
+ /* create the decoration frame for the client window */
+ self->frame = frame_new(self);
+
+ frame_grab_client(self->frame);
+
+ /* we've grabbed everything and set everything that we need to at mapping
+ time now */
+ grab_server(FALSE);
+
+ /* per-app settings override stuff from client_get_all, and return the
+ settings for other uses too */
settings = client_get_settings_state(self);
- /* the session should get the last say */
+ /* the session should get the last say thought */
client_restore_session_state(self);
- client_calc_layer(self);
+ /* now we have all of the window's information so we can set this up */
+ client_setup_decor_and_functions(self);
+ /* remove the client's border (and adjust re gravity) */
+ client_toggle_border(self, FALSE);
+
{
Time t = sn_app_started(self->startup_id, self->class);
if (t) self->user_time = t;
}
- /* update the focus lists, do this before the call to change_state or
- it can end up in the list twice! */
- focus_order_add_new(self);
-
- /* remove the client's border (and adjust re gravity) */
- client_toggle_border(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);
-
- /* create the decoration frame for the client window */
- self->frame = frame_new(self);
-
- frame_grab_client(self->frame, self);
-
/* do this after we have a frame.. it uses the frame to help determine the
WM_STATE to apply. */
client_change_state(self);
- grab_server(FALSE);
+ /* add ourselves to the focus order */
+ focus_order_add_new(self);
- stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+ /* do this to add ourselves to the stacking list in a non-intrusive way */
+ client_calc_layer(self);
/* focus the new window? */
if (ob_state() != OB_STATE_STARTING &&
activate = TRUE;
}
- /* get the current position */
- newx = self->area.x;
- newy = self->area.y;
-
/* figure out placement for the window */
if (ob_state() == OB_STATE_RUNNING) {
gboolean transient;
- transient = place_client(self, &newx, &newy, settings);
+ ob_debug("Positioned: %s @ %d %d\n",
+ (!self->positioned ? "no" :
+ (self->positioned == PPosition ? "program specified" :
+ (self->positioned == USPosition ? "user specified" :
+ "BADNESS !?"))), self->area.x, self->area.y);
+
+ transient = place_client(self, &self->area.x, &self->area.y, settings);
/* make sure the window is visible. */
- client_find_onscreen(self, &newx, &newy,
- self->area.width,
- self->area.height,
+ client_find_onscreen(self, &self->area.x, &self->area.y,
+ self->area.width, self->area.height,
/* non-normal clients has less rules, and
windows that are being restored from a
session do also. we can assume you want
!self->session));
}
- /* 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
- */
ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
- self->window, newx, newy, self->area.width, self->area.height);
+ self->window, self->area.x, self->area.y,
+ self->area.width, self->area.height);
if (self->session)
- ob_debug("session requested %d %d\n",
+ ob_debug(" but session requested %d %d instead, overriding\n",
self->session->x, self->session->y);
- client_apply_startup_state(self, newx, newy);
+ /* adjust the frame to the client's size before showing the window */
+ frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
+ frame_adjust_client_area(self->frame);
- mouse_grab_for_client(self, TRUE);
+
+ /* move the client to its placed position, or it it's already there,
+ generate a ConfigureNotify telling the client where it is.
+
+ do this after adjusting the frame. otherwise it gets all weird and
+ clients don't work right */
+ client_configure(self, self->area.x, self->area.y,
+ self->area.width, self->area.height,
+ FALSE, TRUE);
+
+ /* 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);
if (activate) {
guint32 last_time = focus_client ?
raised to the top. Legacy begets legacy I guess?
*/
if (!client_restore_session_stacking(self))
- client_raise(self);
+ stacking_raise(CLIENT_AS_WINDOW(self));
}
+ mouse_grab_for_client(self, TRUE);
+
/* this has to happen before we try focus the window, but we want it to
happen after the client's stacking has been determined or it looks bad
*/
client_show(self);
- /* use client_focus instead of client_activate cuz client_activate does
- stuff like switch desktops etc and I'm not interested in all that when
- a window maps since its not based on an action from the user like
- clicking a window to activate it. so keep the new window out of the way
- but do focus it. */
if (activate) {
gboolean stacked = client_restore_session_stacking(self);
client_present(self, FALSE, !stacked);
/* update the list hints */
client_set_list();
- ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
+ ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
+ window, self->frame->plate, self->class);
+
+ return;
+}
+
+
+ObClient *client_fake_manage(Window window)
+{
+ ObClient *self;
+ ObAppSettings *settings;
+
+ ob_debug("Pretend-managing window: %lx\n", window);
+
+ /* do this minimal stuff to figure out the client's decorations */
+
+ self = g_new0(ObClient, 1);
+ self->window = window;
+
+ client_get_all(self, FALSE);
+ /* per-app settings override stuff, and return the settings for other
+ uses too */
+ settings = client_get_settings_state(self);
+
+ client_setup_decor_and_functions(self);
+
+ /* create the decoration frame for the client window and adjust its size */
+ self->frame = frame_new(self);
+ frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+ return self;
}
void client_unmanage_all()
guint j;
GSList *it;
- ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class,
- self->title ? self->title : "");
+ ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
+ self->window, self->frame->plate,
+ self->class, self->title ? self->title : "");
g_assert(self != NULL);
/* flush to send the hide to the server quickly */
XFlush(ob_display);
- /* ignore enter events from the unmap so it doesnt mess with the focus */
- event_ignore_queued_enters();
+ /* ignore enter events from the unmap so it doesnt mess with the
+ focus */
+ event_ignore_all_queued_enters();
mouse_grab_for_client(self, FALSE);
/* remove the window from our save set */
XChangeSaveSet(ob_display, self->window, SetModeDelete);
+ /* kill the property windows */
+ propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
+
/* update the focus lists */
focus_order_remove(self);
if (client_focused(self)) {
if (STRUT_EXISTS(self->strut))
screen_update_areas();
- for (it = client_destructors; it; it = g_slist_next(it)) {
- ClientCallback *d = it->data;
- d->func(self, d->data);
- }
+ client_call_notifies(self, client_destroy_notifies);
/* tell our parent(s) that we're gone */
if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
for (it = self->group->members; it; it = g_slist_next(it))
if (it->data != self)
((ObClient*)it->data)->transients =
- g_slist_remove(((ObClient*)it->data)->transients, self);
+ g_slist_remove(((ObClient*)it->data)->transients,self);
} else if (self->transient_for) { /* transient of window */
self->transient_for->transients =
g_slist_remove(self->transient_for->transients, self);
/* restore the window's original geometry so it is not lost */
{
- Rect a = self->area;
+ Rect a;
+
+ /* give the client its border back */
+ client_toggle_border(self, TRUE);
+
+ a = self->area;
if (self->fullscreen)
a = self->pre_fullscreen_area;
}
}
- /* give the client its border back */
- client_toggle_border(self, TRUE);
-
self->fullscreen = self->max_horz = self->max_vert = FALSE;
self->decorations = 0; /* unmanaged windows have no decor */
}
/* reparent the window out of the frame, and free the frame */
- frame_release_client(self->frame, self);
+ frame_release_client(self->frame);
+ frame_free(self->frame);
self->frame = NULL;
if (ob_state() != OB_STATE_EXITING) {
PROP_ERASE(self->window, net_wm_state);
PROP_ERASE(self->window, wm_state);
} else {
- /* if we're left in an unmapped state, the client wont be mapped. this
- is bad, since we will no longer be managing the window on restart */
+ /* if we're left in an unmapped state, the client wont be mapped.
+ this is bad, since we will no longer be managing the window on
+ restart */
XMapWindow(ob_display, self->window);
}
+ /* update the list hints */
+ client_set_list();
+
ob_debug("Unmanaged window 0x%lx\n", self->window);
/* free all data allocated in the client struct */
g_free(self->client_machine);
g_free(self->sm_client_id);
g_free(self);
-
- /* update the list hints */
- client_set_list();
+}
+
+void client_fake_unmanage(ObClient *self)
+{
+ /* this is all that got allocated to get the decorations */
+
+ frame_free(self->frame);
+ g_free(self);
}
static ObAppSettings *client_get_settings_state(ObClient *self)
}
-static void client_get_all(ObClient *self)
+static void client_get_all(ObClient *self, gboolean real)
{
+ /* this is needed for the frame to set itself up */
client_get_area(self);
+
+ /* these things can change the decor and functions of the window */
+
client_get_mwm_hints(self);
+ /* this can change the mwmhints for special cases */
+ client_get_type_and_transientness(self);
+ client_get_state(self);
+ client_update_normal_hints(self);
+
+ /* get the session related properties, these can change decorations
+ from per-app settings */
+ client_get_session_ids(self);
- /* The transient-ness of a window is used to pick a type, but the type can
- also affect transiency.
+ /* now we got everything that can affect the decorations */
+ if (!real)
+ return;
- Dialogs are always made transients for their group if they have one.
+ /* get this early so we have it for debugging */
+ client_update_title(self);
- I also have made non-application type windows be transients for their
- group (eg utility windows).
- */
- client_get_transientness(self);
- client_get_type(self);/* this can change the mwmhints for special cases */
- client_get_state(self);
+ client_update_protocols(self);
client_update_wmhints(self);
/* this may have already been called from client_update_wmhints */
if (self->transient_for == NULL)
client_update_transient_for(self);
+
client_get_startup_id(self);
client_get_desktop(self);/* uses transient data/group/startup id if a
desktop is not specified */
client_get_shaped(self);
- client_get_layer(self); /* if layer hasn't been specified, get it from
- other sources if possible */
-
{
/* a couple type-based defaults for new windows */
if (self->type == OB_CLIENT_TYPE_DESKTOP)
self->desktop = DESKTOP_ALL;
}
-
- client_update_protocols(self);
-
- client_get_gravity(self); /* get the attribute gravity */
- client_update_normal_hints(self); /* this may override the attribute
- gravity */
-
- /* got the type, the mwmhints, the protocols, and the normal hints
- (min/max sizes), so we're ready to set up the decorations/functions */
- client_setup_decor_and_functions(self);
#ifdef SYNC
client_update_sync_request_counter(self);
#endif
- /* get the session related properties */
- client_get_session_ids(self);
-
client_get_colormap(self);
- client_update_title(self);
client_update_strut(self);
client_update_icons(self);
- client_update_user_time(self);
+ client_update_user_time_window(self);
+ if (!self->user_time_window) /* check if this would have been called */
+ client_update_user_time(self);
client_update_icon_geometry(self);
}
}
}
-static void client_get_layer(ObClient *self)
-{
- if (!(self->above || self->below)) {
- if (self->group) {
- /* apply stuff from the group */
- GSList *it;
- gint layer = -2;
-
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
- if (c != self && !client_search_transient(self, c) &&
- client_normal(self) && client_normal(c))
- {
- layer = MAX(layer,
- (c->above ? 1 : (c->below ? -1 : 0)));
- }
- }
- switch (layer) {
- case -1:
- self->below = TRUE;
- break;
- case -2:
- case 0:
- break;
- case 1:
- self->above = TRUE;
- break;
- default:
- g_assert_not_reached();
- break;
- }
- }
- }
-}
-
static void client_get_state(ObClient *self)
{
guint32 *state;
self->below = TRUE;
else if (state[i] == prop_atoms.net_wm_state_demands_attention)
self->demands_attention = TRUE;
- else if (state[i] == prop_atoms.openbox_wm_state_undecorated)
+ else if (state[i] == prop_atoms.ob_wm_state_undecorated)
self->undecorated = TRUE;
}
#endif
}
-void client_get_transientness(ObClient *self)
-{
- Window t;
- if (XGetTransientForHint(ob_display, self->window, &t))
- self->transient = TRUE;
-}
-
void client_update_transient_for(ObClient *self)
{
Window t = None;
ObClient *target = NULL;
if (XGetTransientForHint(ob_display, self->window, &t)) {
- self->transient = TRUE;
if (t != self->window) { /* cant be transient to itself! */
target = g_hash_table_lookup(window_map, &t);
/* if this happens then we need to check for it*/
}
}
}
- } else 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;
- if (self->group)
- target = OB_TRAN_GROUP;
- } else
- self->transient = FALSE;
+ } else if (self->transient && self->group)
+ target = OB_TRAN_GROUP;
client_update_transient_tree(self, self->group, self->group,
self->transient_for, target);
GSList *it, *next;
ObClient *c;
+ /* * *
+ Group transient windows are not allowed to have other group
+ transient windows as their children.
+ * * */
+
+
/* No change has occured */
if (oldgroup == newgroup && oldparent == newparent) return;
/** Remove the client from the transient tree wherever it has changed **/
/* If the window is becoming a direct transient for a window in its group
- then that window can't be a child of this window anymore */
+ then any group transients which were our children and are now becoming
+ our parents need to stop being our children.
+
+ Group transients can't be children of group transients already, but
+ we could have any number of direct parents above up, any of which could
+ be transient for the group, and we need to remove it from our children.
+ */
if (oldparent != newparent &&
newparent != NULL && newparent != OB_TRAN_GROUP &&
- newparent->transient_for == OB_TRAN_GROUP &&
newgroup != NULL && newgroup == oldgroup)
{
- self->transients = g_slist_remove(self->transients, newparent);
+ ObClient *look = newparent;
+ do {
+ self->transients = g_slist_remove(self->transients, look);
+ look = look->transient_for;
+ } while (look != NULL && look != OB_TRAN_GROUP);
}
WARNING: Cyclical transient-ness is possible. For e.g. if:
A is transient for the group
- B is a member of the group and transient for A
+ B is transient for A
+ C is transient for B
+ A can't be transient for C or we have a cycle
*/
if (oldgroup != newgroup && newgroup != NULL &&
newparent != OB_TRAN_GROUP)
}
}
-void client_get_type(ObClient *self)
+void client_get_type_and_transientness(ObClient *self)
{
guint num, i;
guint32 *val;
+ Window t;
self->type = -1;
+ self->transient = FALSE;
if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
/* use the first value that we know about in the array */
}
g_free(val);
}
-
+
+ if (XGetTransientForHint(ob_display, self->window, &t))
+ self->transient = TRUE;
+
if (self->type == (ObClientType) -1) {
/*the window type hint was not set, which means we either classify
ourself as a normal window or a dialog, depending on if we are a
else
self->type = OB_CLIENT_TYPE_NORMAL;
}
+
+ /* then, based on our type, we can update our transientness.. */
+ 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;
+ }
}
void client_update_protocols(ObClient *self)
}
#endif
-static void client_get_gravity(ObClient *self)
-{
- XWindowAttributes wattrib;
- Status ret;
-
- ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
- g_assert(ret != BadWindow);
- self->gravity = wattrib.win_gravity;
-}
-
void client_get_colormap(ObClient *self)
{
XWindowAttributes wa;
OB_CLIENT_FUNC_ICONIFY |
OB_CLIENT_FUNC_MAXIMIZE |
OB_CLIENT_FUNC_SHADE |
- OB_CLIENT_FUNC_CLOSE);
+ OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_BELOW |
+ OB_CLIENT_FUNC_ABOVE |
+ OB_CLIENT_FUNC_UNDECORATE);
if (!(self->min_size.width < self->max_size.width ||
self->min_size.height < self->max_size.height))
self->functions = OB_CLIENT_FUNC_MOVE;
case OB_CLIENT_TYPE_DESKTOP:
- case OB_CLIENT_TYPE_DOCK:
/* these windows are not manipulated by the window manager */
self->decorations = 0;
self->functions = 0;
+
+ case OB_CLIENT_TYPE_DOCK:
+ /* these windows are not manipulated by the window manager, but they
+ can set below layer which has a special meaning */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_BELOW;
break;
}
if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
- self->decorations &= ~OB_FRAME_DECOR_GRIPS;
+ self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
/* can't maximize without moving/resizing */
if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
/* kill the handle on fully maxed windows */
if (self->max_vert && self->max_horz)
- self->decorations &= ~OB_FRAME_DECOR_HANDLE;
+ self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
+
+ /* If there are no decorations to remove, don't allow the user to try
+ toggle the state */
+ if (self->decorations == 0)
+ self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
/* finally, the user can have requested no decorations, which overrides
everything (but doesnt give it a border if it doesnt have one) */
static void client_change_allowed_actions(ObClient *self)
{
- gulong actions[9];
+ gulong actions[12];
gint num = 0;
/* desktop windows are kept on all desktops */
actions[num++] = prop_atoms.net_wm_action_maximize_horz;
actions[num++] = prop_atoms.net_wm_action_maximize_vert;
}
+ if (self->functions & OB_CLIENT_FUNC_ABOVE)
+ actions[num++] = prop_atoms.net_wm_action_above;
+ if (self->functions & OB_CLIENT_FUNC_BELOW)
+ actions[num++] = prop_atoms.net_wm_action_below;
+ if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
+ actions[num++] = prop_atoms.ob_wm_action_undecorate;
PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
else self->shaded = FALSE;
}
if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
- if (self->frame) client_iconify(self, FALSE, TRUE);
+ if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
else self->iconic = FALSE;
}
if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
void client_update_user_time(ObClient *self)
{
guint32 time;
+ gboolean got = FALSE;
+
+ if (self->user_time_window)
+ got = PROP_GET32(self->user_time_window,
+ net_wm_user_time, cardinal, &time);
+ if (!got)
+ got = PROP_GET32(self->window, net_wm_user_time, cardinal, &time);
- if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) {
+ if (got) {
/* we set this every time, not just when it grows, because in practice
sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
backward we don't want all windows to stop focusing. we'll just
*/
self->user_time = time;
- /*
- ob_debug("window %s user time %u\n", self->title, time);
- */
+ /*ob_debug("window %s user time %u\n", self->title, time);*/
+ }
+}
+
+void client_update_user_time_window(ObClient *self)
+{
+ guint32 w;
+
+ if (!PROP_GET32(self->window, net_wm_user_time_window, window, &w))
+ w = None;
+
+ if (w != self->user_time_window) {
+ /* remove the old window */
+ propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
+ self->user_time_window = None;
+
+ if (self->group && self->group->leader == w) {
+ ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
+ "_NET_WM_USER_TYPE_WINDOW to its group leader\n");
+ /* do it anyways..? */
+ }
+ else if (w == self->window) {
+ ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
+ "_NET_WM_USER_TIME_WINDOW to itself\n");
+ w = None; /* don't do it */
+ }
+
+ /* add the new window */
+ propwin_add(w, OB_PROPWIN_USER_TIME, self);
+ self->user_time_window = w;
+
+ /* and update from it */
+ client_update_user_time(self);
}
}
localhost[127] = '\0';
if (strcmp(localhost, s) != 0)
self->client_machine = s;
+ else
+ g_free(s);
}
}
if (self->demands_attention)
netstate[num++] = prop_atoms.net_wm_state_demands_attention;
if (self->undecorated)
- netstate[num++] = prop_atoms.openbox_wm_state_undecorated;
+ netstate[num++] = prop_atoms.ob_wm_state_undecorated;
PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
if (self->frame)
else l = OB_STACKING_LAYER_ABOVE;
}
else if ((self->fullscreen ||
- /* no decorations and fills the monitor = oldskool fullscreen */
- (self->frame != NULL &&
- (self->frame->size.right == 0 && self->frame->size.left == 0 &&
- self->frame->size.bottom == 0 && self->frame->size.top == 0 &&
- RECT_EQUAL(self->area,
- *screen_physical_area_monitor
- (client_monitor(self)))))) &&
+ /* No decorations and fills the monitor = oldskool fullscreen.
+ But not for undecorated windows, because the user can do that
+ */
+ (self->decorations == 0 &&
+ !self->undecorated &&
+ RECT_EQUAL(self->area,
+ *screen_physical_area_monitor
+ (client_monitor(self))))) &&
(client_focused(self) || client_search_focus_tree(self)))
l = OB_STACKING_LAYER_FULLSCREEN;
else if (self->above) l = OB_STACKING_LAYER_ABOVE;
}
static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
- ObStackingLayer min, gboolean raised)
+ ObStackingLayer min)
{
ObStackingLayer old, own;
GSList *it;
own = calc_layer(self);
self->layer = MAX(own, min);
+ if (self->layer != old) {
+ stacking_remove(CLIENT_AS_WINDOW(self));
+ stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+ }
+
for (it = self->transients; it; it = g_slist_next(it))
client_calc_layer_recursive(it->data, orig,
- self->layer,
- raised ? raised : self->layer != old);
-
- if (!raised && self->layer != old)
- if (orig->frame) { /* only restack if the original window is managed */
- stacking_remove(CLIENT_AS_WINDOW(self));
- stacking_add(CLIENT_AS_WINDOW(self));
- }
+ self->layer);
}
void client_calc_layer(ObClient *self)
it = client_search_all_top_parents(self);
for (; it; it = g_slist_next(it))
- client_calc_layer_recursive(it->data, orig, 0, FALSE);
+ client_calc_layer_recursive(it->data, orig, 0);
}
gboolean client_should_show(ObClient *self)
return FALSE;
}
-void client_show(ObClient *self)
+gboolean client_show(ObClient *self)
{
+ gboolean show = FALSE;
if (client_should_show(self)) {
frame_show(self->frame);
+ show = TRUE;
}
/* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
desktop!
*/
client_change_wm_state(self);
+ return show;
}
-void client_hide(ObClient *self)
+gboolean client_hide(ObClient *self)
{
+ gboolean hide = FALSE;
+
if (!client_should_show(self)) {
+ if (self == focus_client) {
+ /* if there is a grab going on, then we need to cancel it. if we
+ move focus during the grab, applications will get
+ NotifyWhileGrabbed events and ignore them !
+
+ actions should not rely on being able to move focus during an
+ interactive grab.
+ */
+ if (keyboard_interactively_grabbed())
+ keyboard_interactive_cancel();
+ }
+
frame_hide(self->frame);
+ hide = TRUE;
}
/* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
desktop!
*/
client_change_wm_state(self);
+ return hide;
}
void client_showhide(ObClient *self)
{
-
- if (client_should_show(self)) {
- frame_show(self->frame);
- }
- else {
- frame_hide(self->frame);
- }
+ if (!client_show(self))
+ client_hide(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
}
-static void client_apply_startup_state(ObClient *self, gint x, gint y)
+static void client_apply_startup_state(ObClient *self)
{
- 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;
-
/* set the desktop hint, to make sure that it always exists */
PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
if (self->iconic) {
self->iconic = FALSE;
- client_iconify(self, TRUE, FALSE);
+ client_iconify(self, TRUE, FALSE, TRUE);
}
if (self->fullscreen) {
self->fullscreen = 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);
- pos = TRUE;
} else if (self->max_vert) {
self->max_vert = FALSE;
client_maximize(self, TRUE, 2);
- pos = TRUE;
} else if (self->max_horz) {
self->max_horz = FALSE;
client_maximize(self, TRUE, 1);
- pos = TRUE;
- }
-
- /* if the client didn't get positioned yet, then do so now.
- call client_move even if the window is not being moved anywhere, because
- when we reparent it and decorate it, it is getting moved and we need to
- be telling it so with a ConfigureNotify event.
- */
- if (!pos) {
- /* use the saved position */
- self->area.x = ox;
- self->area.y = oy;
- client_move(self, x, y);
}
/* nothing to do for the other states:
}
-void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
- gboolean user, gboolean final,
- gboolean force_reply)
+void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
+ gboolean user, gboolean final)
{
- gint oldw, oldh, oldrx, oldry;
+ gint oldw, oldh;
gboolean send_resize_client;
- gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
+ gboolean moved = FALSE, resized = FALSE;
+ gboolean fmoved, fresized;
guint fdecor = self->frame->decorations;
gboolean fhorz = self->frame->max_horz;
gint logicalw, logicalh;
(resized && config_resize_redraw))));
/* if the client is enlarging, then resize the client before the frame */
- if (send_resize_client && user && (w > oldw || h > oldh)) {
- XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
+ if (send_resize_client && (w > oldw || h > oldh)) {
+ XResizeWindow(ob_display, self->window,
+ MAX(w, oldw), MAX(h, oldh));
/* resize the plate to show the client padding color underneath */
frame_adjust_client_area(self->frame);
}
/* find the frame's dimensions and move/resize it */
+ fmoved = moved;
+ fresized = resized;
if (self->decorations != fdecor || self->max_horz != fhorz)
- moved = resized = TRUE;
- if (moved || resized)
- frame_adjust_area(self->frame, moved, resized, FALSE);
-
- /* find the client's position relative to the root window */
- oldrx = self->root_pos.x;
- oldry = self->root_pos.y;
- rootmoved = (oldrx != (signed)(self->frame->area.x +
- self->frame->size.left -
- self->border_width) ||
- oldry != (signed)(self->frame->area.y +
- self->frame->size.top -
- self->border_width));
-
- if (force_reply || ((!user || (user && final)) && rootmoved))
+ fmoved = fresized = TRUE;
+ if (fmoved || fresized)
+ frame_adjust_area(self->frame, fmoved, fresized, FALSE);
+
+ if ((!user || (user && final)) && !resized)
{
XEvent event;
event.xconfigure.event = self->window;
event.xconfigure.window = self->window;
+ ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
+ self->title, self->root_pos.x, self->root_pos.y, w, h);
+
/* root window real coords */
event.xconfigure.x = self->root_pos.x;
event.xconfigure.y = self->root_pos.y;
}
/* if the client is shrinking, then resize the frame before the client */
- if (send_resize_client && (!user || (w <= oldw || h <= oldh))) {
+ if (send_resize_client && (w <= oldw || h <= oldh)) {
/* resize the plate to show the client padding color underneath */
frame_adjust_client_area(self->frame);
- XResizeWindow(ob_display, self->window, w, h);
+ if (send_resize_client)
+ XResizeWindow(ob_display, self->window, w, h);
}
XFlush(ob_display);
self->fullscreen = fs;
client_change_state(self); /* change the state hints on the client */
- client_calc_layer(self); /* and adjust out layer/stacking */
if (fs) {
self->pre_fullscreen_area = self->area;
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 */
- x = y = w = h = 0;
+ /* these will help configure_full figure out where to fullscreen
+ the window */
+ x = self->area.x;
+ y = self->area.y;
+ w = self->area.width;
+ h = self->area.height;
} else {
- Rect *a;
+ g_assert(self->pre_fullscreen_area.width > 0 &&
+ self->pre_fullscreen_area.height > 0);
- if (self->pre_fullscreen_area.width > 0 &&
- self->pre_fullscreen_area.height > 0)
- {
- x = self->pre_fullscreen_area.x;
- y = self->pre_fullscreen_area.y;
- w = self->pre_fullscreen_area.width;
- h = self->pre_fullscreen_area.height;
- RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
- } else {
- /* pick some fallbacks... */
- a = screen_area_monitor(self->desktop, 0);
- x = a->x + a->width / 4;
- y = a->y + a->height / 4;
- w = a->width / 2;
- h = a->height / 2;
- }
+ x = self->pre_fullscreen_area.x;
+ y = self->pre_fullscreen_area.y;
+ w = self->pre_fullscreen_area.width;
+ h = self->pre_fullscreen_area.height;
+ RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
}
client_setup_decor_and_functions(self);
client_move_resize(self, x, y, w, h);
- /* try focus us when we go into fullscreen mode */
- client_focus(self);
+ /* and adjust our layer/stacking. do this after resizing the window,
+ and applying decorations, because windows which fill the screen are
+ considered "fullscreen" and it affects their layer */
+ client_calc_layer(self);
+
+ if (fs) {
+ /* try focus us when we go into fullscreen mode */
+ client_focus(self);
+ }
}
static void client_iconify_recursive(ObClient *self,
- gboolean iconic, gboolean curdesk)
+ gboolean iconic, gboolean curdesk,
+ gboolean hide_animation)
{
GSList *it;
gboolean changed = FALSE;
if (changed) {
client_change_state(self);
- if (ob_state() != OB_STATE_STARTING && config_animate_iconify)
+ if (config_animate_iconify && !hide_animation)
frame_begin_iconify_animation(self->frame, iconic);
/* do this after starting the animation so it doesn't flash */
client_showhide(self);
for (it = self->transients; it; it = g_slist_next(it))
if (it->data != self)
if (client_is_direct_child(self, it->data) || !iconic)
- client_iconify_recursive(it->data, iconic, curdesk);
+ client_iconify_recursive(it->data, iconic, curdesk,
+ hide_animation);
}
-void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
+void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
+ gboolean hide_animation)
{
if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
/* move up the transient chain as far as possible first */
self = client_search_top_normal_parent(self);
- client_iconify_recursive(self, iconic, curdesk);
+ client_iconify_recursive(self, iconic, curdesk, hide_animation);
}
}
if (dir == 2 && !self->max_vert) return;
}
- /* we just tell it to configure in the same place and client_configure
- worries about filling the screen with the window */
+ /* these will help configure_full figure out which screen to fill with
+ the window */
x = self->area.x;
y = self->area.y;
w = self->area.width;
self->pre_max_area.width, self->area.height);
}
} else {
- Rect *a;
-
- a = screen_area_monitor(self->desktop, 0);
if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
- if (self->pre_max_area.width > 0) {
- x = self->pre_max_area.x;
- w = self->pre_max_area.width;
+ g_assert(self->pre_max_area.width > 0);
- RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
- 0, self->pre_max_area.height);
- } else {
- /* pick some fallbacks... */
- x = a->x + a->width / 4;
- w = a->width / 2;
- }
+ x = self->pre_max_area.x;
+ w = self->pre_max_area.width;
+
+ RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
+ 0, self->pre_max_area.height);
}
if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
- if (self->pre_max_area.height > 0) {
- y = self->pre_max_area.y;
- h = self->pre_max_area.height;
+ g_assert(self->pre_max_area.height > 0);
- RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
- self->pre_max_area.width, 0);
- } else {
- /* pick some fallbacks... */
- y = a->y + a->height / 4;
- h = a->height / 2;
- }
+ y = self->pre_max_area.y;
+ h = self->pre_max_area.height;
+
+ RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
+ self->pre_max_area.width, 0);
}
}
client_showhide(self);
/* raise if it was not already on the desktop */
if (old != DESKTOP_ALL)
- client_raise(self);
+ stacking_raise(CLIENT_AS_WINDOW(self));
if (STRUT_EXISTS(self->strut))
screen_update_areas();
}
switch (state) {
case IconicState:
- client_iconify(self, TRUE, TRUE);
+ client_iconify(self, TRUE, TRUE, FALSE);
break;
case NormalState:
- client_iconify(self, FALSE, TRUE);
+ client_iconify(self, FALSE, TRUE, FALSE);
break;
}
}
action = self->demands_attention ?
prop_atoms.net_wm_state_remove :
prop_atoms.net_wm_state_add;
- else if (state == prop_atoms.openbox_wm_state_undecorated)
+ else if (state == prop_atoms.ob_wm_state_undecorated)
action = undecorated ? prop_atoms.net_wm_state_remove :
prop_atoms.net_wm_state_add;
}
below = TRUE;
} else if (state == prop_atoms.net_wm_state_demands_attention) {
demands_attention = TRUE;
- } else if (state == prop_atoms.openbox_wm_state_undecorated) {
+ } else if (state == prop_atoms.ob_wm_state_undecorated) {
undecorated = TRUE;
}
below = FALSE;
} else if (state == prop_atoms.net_wm_state_demands_attention) {
demands_attention = FALSE;
- } else if (state == prop_atoms.openbox_wm_state_undecorated) {
+ } else if (state == prop_atoms.ob_wm_state_undecorated) {
undecorated = FALSE;
}
}
}
+
if (max_horz != self->max_horz || max_vert != self->max_vert) {
if (max_horz != self->max_horz && max_vert != self->max_vert) {
/* toggling both */
client_shade(self, shaded);
if (undecorated != self->undecorated)
client_set_undecorated(self, undecorated);
+ if (above != self->above || below != self->below) {
+ self->above = above;
+ self->below = below;
+ client_calc_layer(self);
+ }
+
if (modal != self->modal) {
self->modal = modal;
/* when a window changes modality, then its stacking order with its
transients needs to change */
- client_raise(self);
+ stacking_raise(CLIENT_AS_WINDOW(self));
+
+ /* it also may get focused. if something is focused that shouldn't
+ be focused anymore, then move the focus */
+ if (focus_client && client_focus_target(focus_client) != focus_client)
+ client_focus(focus_client);
}
+
if (iconic != self->iconic)
- client_iconify(self, iconic, FALSE);
+ client_iconify(self, iconic, FALSE, FALSE);
if (demands_attention != self->demands_attention)
client_hilite(self, demands_attention);
- if (above != self->above || below != self->below) {
- self->above = above;
- self->below = below;
- client_calc_layer(self);
- }
-
client_change_state(self); /* change the hint to reflect these changes */
}
gboolean client_can_focus(ObClient *self)
{
- XEvent ev;
-
/* choose the correct target */
self = client_focus_target(self);
if (!(self->can_focus || self->focus_notify))
return FALSE;
- /* do a check to see if the window has already been unmapped or destroyed
- do this intelligently while watching out for unmaps we've generated
- (ignore_unmaps > 0) */
- if (XCheckTypedWindowEvent(ob_display, self->window,
- DestroyNotify, &ev)) {
- XPutBackEvent(ob_display, &ev);
- return FALSE;
- }
- while (XCheckTypedWindowEvent(ob_display, self->window,
- UnmapNotify, &ev)) {
- if (self->ignore_unmaps) {
- self->ignore_unmaps--;
- } else {
- XPutBackEvent(ob_display, &ev);
- return FALSE;
- }
- }
-
return TRUE;
}
"Focusing client \"%s\" at time %u\n",
self->title, event_curtime);
+ /* if there is a grab going on, then we need to cancel it. if we move
+ focus during the grab, applications will get NotifyWhileGrabbed events
+ and ignore them !
+
+ actions should not rely on being able to move focus during an
+ interactive grab.
+ */
+ if (keyboard_interactively_grabbed())
+ keyboard_interactive_cancel();
+
+ xerror_set_ignore(TRUE);
+ xerror_occured = FALSE;
+
if (self->can_focus) {
/* This can cause a BadMatch error with CurrentTime, or if an app
passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
- xerror_set_ignore(TRUE);
XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
event_curtime);
- xerror_set_ignore(FALSE);
}
if (self->focus_notify) {
XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
}
-#ifdef DEBUG_FOCUS
- ob_debug("%sively focusing %lx at %d\n",
- (self->can_focus ? "act" : "pass"),
- self->window, (gint) event_curtime);
-#endif
+ xerror_set_ignore(FALSE);
- /* Cause the FocusIn to come back to us. Important for desktop switches,
- since otherwise we'll have no FocusIn on the queue and send it off to
- the focus_backup. */
- XSync(ob_display, FALSE);
- return TRUE;
+ return !xerror_occured;
}
/*! Present the client to the user.
event_halt_focus_delay();
if (client_normal(self) && screen_showing_desktop)
- screen_show_desktop(FALSE, FALSE);
+ screen_show_desktop(FALSE, self);
if (self->iconic)
- client_iconify(self, FALSE, here);
+ client_iconify(self, FALSE, here, FALSE);
if (self->desktop != DESKTOP_ALL &&
self->desktop != screen_desktop)
{
return;
if (self->shaded)
client_shade(self, FALSE);
+ if (raise)
+ stacking_raise(CLIENT_AS_WINDOW(self));
client_focus(self);
-
- if (raise) {
- /* we do this as an action here. this is rather important. this is
- because we want the results from the focus change to take place
- BEFORE we go about raising the window. when a fullscreen window
- loses focus, we need this or else the raise wont be able to raise
- above the to-lose-focus fullscreen window. */
- client_raise(self);
- }
}
void client_activate(ObClient *self, gboolean here, gboolean user)
client_bring_helper_windows_recursive(self, self->desktop);
}
-void client_raise(ObClient *self)
-{
- action_run_string("Raise", self, CurrentTime);
-}
-
-void client_lower(ObClient *self)
-{
- action_run_string("Lower", self, CurrentTime);
-}
-
gboolean client_focused(ObClient *self)
{
return self == focus_client;
static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
{
guint i;
- /* si is the smallest image >= req */
- /* li is the largest image < req */
- gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
+ gulong min_diff, min_i;
if (!self->nicons) {
ObClientIcon *parent = NULL;
return parent;
}
- for (i = 0; i < self->nicons; ++i) {
- size = self->icons[i].width * self->icons[i].height;
- if (size < smallest && size >= (unsigned)(w * h)) {
- smallest = size;
- si = i;
- }
- if (size > largest && size <= (unsigned)(w * h)) {
- largest = size;
- li = i;
+ /* 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[0].width - w) + ABS(self->icons[0].height - h);
+ if (diff < min_diff) {
+ min_diff = diff;
+ min_i = i;
}
}
- if (largest == 0) /* didnt find one smaller than the requested size */
- return &self->icons[si];
- return &self->icons[li];
+ return &self->icons[min_i];
}
const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
void client_set_undecorated(ObClient *self, gboolean undecorated)
{
- if (self->undecorated != undecorated) {
+ if (self->undecorated != undecorated &&
+ /* don't let it undecorate if the function is missing, but let
+ it redecorate */
+ (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
+ {
self->undecorated = undecorated;
client_setup_decor_and_functions(self);
- /* Make sure the client knows it might have moved. Maybe there is a
- * better way of doing this so only one client_configure is sent, but
- * since 125 of these are sent per second when moving the window (with
- * user = FALSE) i doubt it matters much.
- */
- client_configure(self, self->area.x, self->area.y,
- self->area.width, self->area.height, TRUE, TRUE);
client_change_state(self); /* reflect this in the state hints */
}
}
if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
continue; \
if(cur->iconic) \
- continue; \
- if(cur->layer < c->layer && !config_resist_layers_below) \
continue;
#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \