GList *client_list = NULL;
-static GSList *client_destructors = NULL;
-static GSList *client_hide_notifies = NULL;
+static GSList *client_destroy_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_callbacks(ObClient *self, GSList *list);
+static void client_call_notifies(ObClient *self, GSList *list);
void client_startup(gboolean reconfig)
{
if (reconfig) return;
}
-static void client_call_callbacks(ObClient *self, GSList *list)
+static void client_call_notifies(ObClient *self, GSList *list)
{
GSList *it;
}
}
-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);
- break;
- }
- }
-}
-
-void client_add_hide_notify(ObClientCallback func, gpointer data)
-{
- ClientCallback *d = g_new(ClientCallback, 1);
- d->func = func;
- d->data = data;
- client_hide_notifies = g_slist_prepend(client_destructors, d);
-}
-
-void client_remove_hide_notify(ObClientCallback func)
-{
- GSList *it;
-
- for (it = client_hide_notifies; it; it = g_slist_next(it)) {
- ClientCallback *d = it->data;
- if (d->func == func) {
- g_free(d);
- client_hide_notifies =
- g_slist_delete_link(client_hide_notifies, 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);
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_full(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 ?
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);
+ 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
/* 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;
}
guint j;
GSList *it;
- ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window,
+ 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);
if (STRUT_EXISTS(self->strut))
screen_update_areas();
- client_call_callbacks(self, client_destructors);
+ 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 */
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;
}
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;
}
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);
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)
void client_hide(ObClient *self)
{
- if (!client_should_show(self)) {
+ if (!client_should_show(self))
frame_hide(self->frame);
- client_call_callbacks(self, client_hide_notifies);
- }
-
/* 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!
void client_showhide(ObClient *self)
{
- if (client_should_show(self)) {
+ if (client_should_show(self))
frame_show(self->frame);
- }
- else {
+ else
frame_hide(self->frame);
- client_call_callbacks(self, client_hide_notifies);
- }
-
/* 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!
}
-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->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:
(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 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,
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);
}
}
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;
}
}
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;
}
gboolean client_focus(ObClient *self)
{
+ gboolean error;
+
/* choose the correct target */
self = client_focus_target(self);
"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();
+
+ error = FALSE;
+ xerror_set_ignore(TRUE);
+
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
+ /* This calls XSync, which will cause the FocusIn to come back to us.
+ That's important for desktop switches, since otherwise we'll have no
+ FocusIn on the queue and end up falling back again. */
+ xerror_set_ignore(FALSE);
+ if (!xerror_occured) error = TRUE;
- /* 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 !error;
}
/*! Present the client to the user.
if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
continue; \
if(cur->iconic) \
- continue; \
- if(cur->layer == c->layer) \
continue;
#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \