GList *client_list = NULL;
-static GSList *client_destructors = NULL;
+static GSList *client_destroy_notifies = NULL;
+static GSList *client_hide_notifies = NULL;
static void client_get_all(ObClient *self, gboolean real);
static void client_toggle_border(ObClient *self, gboolean show);
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)
{
if (reconfig) return;
}
-void client_add_destructor(ObClientCallback func, gpointer data)
+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_destroy_notify(ObClientCallback func, gpointer data)
+{
+ ClientCallback *d = g_new(ClientCallback, 1);
+ d->func = func;
+ d->data = data;
+ client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
+}
+
+void client_remove_destroy_notify(ObClientCallback func)
+{
+ GSList *it;
+
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ if (d->func == func) {
+ g_free(d);
+ client_destroy_notifies =
+ g_slist_delete_link(client_destroy_notifies, it);
+ break;
+ }
+ }
+}
+
+void client_add_hide_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_hide_notifies = g_slist_prepend(client_hide_notifies, d);
}
-void client_remove_destructor(ObClientCallback func)
+void client_remove_hide_notify(ObClientCallback func)
{
GSList *it;
- for (it = client_destructors; it; it = g_slist_next(it)) {
+ for (it = client_hide_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_hide_notifies =
+ g_slist_delete_link(client_hide_notifies, it);
break;
}
}
XWMHints *wmhint;
gboolean activate = FALSE;
ObAppSettings *settings;
- gint newx, newy;
grab_server(TRUE);
/* 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;
+ /* get all the stuff off the window */
client_get_all(self, TRUE);
- /* per-app settings override stuff, and return the settings for other
- uses too */
- settings = client_get_settings_state(self);
- /* the session should get the last say */
- client_restore_session_state(self);
-
- client_setup_decor_and_functions(self);
-
- client_calc_layer(self);
-
- {
- 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);
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 thought */
+ client_restore_session_state(self);
+
+ /* now we have all of the window's information so we can set this up */
+ client_setup_decor_and_functions(self);
+
+ /* 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;
+ }
+
/* 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);
+ 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
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);
+ /* generate a ConfigureNotify telling the client where it is */
+ client_configure_full(self, self->area.x, self->area.y,
+ self->area.width, self->area.height,
+ FALSE, TRUE);
+
+ client_apply_startup_state(self);
mouse_grab_for_client(self, TRUE);
stacking_raise(CLIENT_AS_WINDOW(self));
}
+ /* 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);
+
/* 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);
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 */
/* 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 */
if (!real)
return;
+ /* get this early so we have it for debugging */
+ client_update_title(self);
+
client_update_protocols(self);
client_update_wmhints(self);
#endif
client_get_colormap(self);
- client_update_title(self);
client_update_strut(self);
client_update_icons(self);
client_update_user_time_window(self);
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)
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);
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) &&
static void client_change_allowed_actions(ObClient *self)
{
- gulong actions[9];
+ gulong actions[11];
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;
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) {
}
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);
-
- /* restack. but only if the original window is managed.
-
- raised is used so that only the bottom-most window in the stacking
- order is raised, the others will automatically come with it.
-
- also only the highest windows in the stacking order (no transients)
- are lowered, cuz the rest come for free
- */
- if (!raised && orig->frame) {
- if (self->layer > old) {
- stacking_remove(CLIENT_AS_WINDOW(self));
- stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
- } else if (self->layer < old && self->transients == NULL) {
- stacking_remove(CLIENT_AS_WINDOW(self));
- stacking_add_nonintrusive(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)
{
if (!client_should_show(self)) {
frame_hide(self->frame);
+
+ client_call_notifies(self, client_hide_notifies);
}
/* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
}
else {
frame_hide(self->frame);
+
+ client_call_notifies(self, client_hide_notifies);
}
/* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
}
-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)
+ 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;
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);
}
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))
+ 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);
}
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);
}
}
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;
}
}
stacking_raise(CLIENT_AS_WINDOW(self));
}
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);
"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();
+
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. */
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)
{
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)
continue; \
if(cur->iconic) \
continue; \
- if(cur->layer < c->layer && !config_resist_layers_below) \
+ if(cur->layer == c->layer) \
continue;
#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \