/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
-
+
client.c for the Openbox window manager
Copyright (c) 2006 Mikael Magnusson
Copyright (c) 2003-2007 Dana Jansens
#include "event.h"
#include "grab.h"
#include "focus.h"
-#include "propwin.h"
#include "stacking.h"
#include "openbox.h"
#include "group.h"
gboolean oldgtran, gboolean newgtran,
ObClient* oldparent,
ObClient *newparent);
-static void client_present(ObClient *self, gboolean here, gboolean raise);
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade);
static GSList *client_search_all_top_parents_internal(ObClient *self,
gboolean bylayer,
ObStackingLayer layer);
}
}
-void client_set_list()
+void client_set_list(void)
{
Window *windows, *win_it;
GList *it;
stacking_set_list();
}
-void client_manage_all()
+void client_manage_all(void)
{
guint i, j, nchild;
Window w, *children;
XWMHints *wmhint;
gboolean activate = FALSE;
ObAppSettings *settings;
- gint placex, placey, placew, placeh;
gboolean transient = FALSE;
+ Rect place, *monitor;
+ Time launch_time, map_time;
grab_server(TRUE);
grab_server(FALSE);
return; /* don't manage it */
}
-
+
/* is the window a docking app */
if ((wmhint = XGetWMHints(ob_display, window))) {
if ((wmhint->flags & StateHint) &&
ob_debug("Managing window: 0x%lx\n", window);
+ map_time = event_get_server_time();
+
/* choose the events we want to receive on the CLIENT window */
attrib_set.event_mask = CLIENT_EVENTMASK;
attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
self->wmstate = WithdrawnState; /* make sure it gets updated first time */
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);
+ ob_debug("Window type: %d\n", self->type);
+ ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
+
/* specify that if we exit, the window should not be destroyed and
should be reparented back to root automatically */
XChangeSaveSet(ob_display, window, SetModeInsert);
/* now we have all of the window's information so we can set this up */
client_setup_decor_and_functions(self, FALSE);
- {
- Time t = sn_app_started(self->startup_id, self->class);
- if (t) self->user_time = t;
- }
+ /* tell startup notification that this app started */
+ launch_time = sn_app_started(self->startup_id, self->class);
/* do this after we have a frame.. it uses the frame to help determine the
WM_STATE to apply. */
client_search_focus_tree_full(self)) &&
/* this checks for focus=false for the window */
(!settings || settings->focus != 0) &&
- focus_valid_target(self, FALSE, TRUE, FALSE, FALSE))
+ focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
{
activate = TRUE;
}
frame_adjust_client_area(self->frame);
/* where the frame was placed is where the window was originally */
- placex = self->area.x;
- placey = self->area.y;
- placew = self->area.width;
- placeh = self->area.height;
+ place = self->area;
+ monitor = screen_physical_area_monitor(screen_find_monitor(&place));
/* figure out placement for the window if the window is new */
if (ob_state() == OB_STATE_RUNNING) {
(self->positioned == USPosition ? "user specified" :
(self->positioned == (PPosition | USPosition) ?
"program + user specified" :
- "BADNESS !?")))), placex, placey);
+ "BADNESS !?")))), place.x, place.y);
ob_debug("Sized: %s @ %d %d\n",
(!self->sized ? "no" :
(self->sized == USSize ? "user specified" :
(self->sized == (PSize | USSize) ?
"program + user specified" :
- "BADNESS !?")))), placew, placeh);
+ "BADNESS !?")))), place.width, place.height);
/* splash screens are also returned as TRUE for transient,
and so will be forced on screen below */
- transient = place_client(self, &placex, &placey, settings);
+ transient = place_client(self, &place.x, &place.y, settings);
/* make sure the window is visible. */
- client_find_onscreen(self, &placex, &placey, placew, placeh,
+ client_find_onscreen(self, &place.x, &place.y,
+ place.width, place.height,
/* non-normal clients has less rules, and
windows that are being restored from a
session do also. we can assume you want
*/
ob_state() == OB_STATE_RUNNING &&
(transient ||
- (!(self->positioned & USPosition) &&
+ (!((self->positioned & USPosition) ||
+ (settings && settings->pos_given)) &&
client_normal(self) &&
- !self->session)));
+ !self->session &&
+ /* don't move oldschool fullscreen windows to
+ fit inside the struts (fixes Acroread, which
+ makes its fullscreen window fit the screen
+ but it is not USSize'd or USPosition'd) */
+ !(self->decorations == 0 &&
+ RECT_EQUAL(place, *monitor)))));
}
/* if the window isn't user-sized, then make it fit inside
*/
if (ob_state() == OB_STATE_RUNNING &&
(transient ||
- (!(self->sized & USSize) &&
+ (!(self->sized & USSize || self->positioned & USPosition) &&
client_normal(self) &&
- !self->session)))
+ !self->session &&
+ /* don't shrink oldschool fullscreen windows to fit inside the
+ struts (fixes Acroread, which makes its fullscreen window
+ fit the screen but it is not USSize'd or USPosition'd) */
+ !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
{
- Rect placer;
+ Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
- RECT_SET(placer, placex, placey, placew, placeh);
- frame_rect_to_frame(self->frame, &placer);
+ /* get the size of the frame */
+ place.width += self->frame->size.left + self->frame->size.right;
+ place.height += self->frame->size.top + self->frame->size.bottom;
- Rect *a = screen_area_monitor(self->desktop, client_monitor(self),
- &placer);
+ /* fit the window inside the area */
+ place.width = MIN(place.width, a->width);
+ place.height = MIN(place.height, a->height);
- /* shrink by the frame's area */
- a->width -= self->frame->size.left + self->frame->size.right;
- a->height -= self->frame->size.top + self->frame->size.bottom;
+ ob_debug("setting window size to %dx%d\n", place.width, place.height);
- /* fit the window inside the area */
- if (placew > a->width || self->area.height > a->height) {
- placew = MIN(self->area.width, a->width);
- placeh = MIN(self->area.height, a->height);
+ /* get the size of the client back */
+ place.width -= self->frame->size.left + self->frame->size.right;
+ place.height -= self->frame->size.top + self->frame->size.bottom;
- ob_debug("setting window size to %dx%d\n",
- self->area.width, self->area.height);
- }
g_free(a);
}
-
ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
"some restrictions may apply\n",
- self->window, placex, placey, placew, placeh);
+ self->window, place.x, place.y, place.width, place.height);
if (self->session)
ob_debug(" but session requested %d, %d %d x %d instead, "
"overriding\n",
this also places the window
*/
- client_apply_startup_state(self, placex, placey, placew, placeh);
+ client_apply_startup_state(self, place.x, place.y,
+ place.width, place.height);
+
+ g_free(monitor);
+ monitor = NULL;
if (activate) {
- guint32 last_time = focus_client ?
- focus_client->user_time : CurrentTime;
+ gboolean raise = FALSE;
/* This is focus stealing prevention */
ob_debug_type(OB_DEBUG_FOCUS,
- "Want to focus new window 0x%x with time %u "
- "(last time %u)\n",
- self->window, self->user_time, last_time);
+ "Want to focus new window 0x%x at time %u "
+ "launched at %u (last user interaction time %u)\n",
+ self->window, map_time, launch_time,
+ event_last_user_time);
+
+ if (menu_frame_visible || moveresize_in_progress) {
+ activate = FALSE;
+ raise = TRUE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because the user is inside "
+ "an Openbox menu or is move/resizing a window and "
+ "we don't want to interrupt them\n");
+ }
/* if it's on another desktop */
- if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
- && /* the timestamp is from before you changed desktops */
- self->user_time && screen_desktop_user_time &&
- !event_time_after(self->user_time, screen_desktop_user_time))
+ else if (!(self->desktop == screen_desktop ||
+ self->desktop == DESKTOP_ALL) &&
+ /* the timestamp is from before you changed desktops */
+ launch_time && screen_desktop_user_time &&
+ !event_time_after(launch_time, screen_desktop_user_time))
{
activate = FALSE;
+ raise = TRUE;
ob_debug_type(OB_DEBUG_FOCUS,
"Not focusing the window because its on another "
"desktop\n");
else if (focus_client && client_search_focus_tree_full(self) == NULL &&
client_search_focus_group_full(self) == NULL)
{
- /* If time stamp is old, don't steal focus */
- if (self->user_time && last_time &&
- !event_time_after(self->user_time, last_time))
+ /* If the user is working in another window right now, then don't
+ steal focus */
+ if (event_last_user_time && launch_time &&
+ event_time_after(event_last_user_time, launch_time) &&
+ event_last_user_time != launch_time &&
+ event_time_after(event_last_user_time,
+ map_time - OB_EVENT_USER_TIME_DELAY))
{
activate = FALSE;
ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because the time is "
- "too old\n");
+ "Not focusing the window because the user is "
+ "working in another window\n");
}
- /* If its a transient (and parents aren't focused) and the time
- is ambiguous (either the current focus target doesn't have
- a timestamp, or they are the same (we probably inherited it
- from them) */
- else if (client_has_parent(self) &&
- (!last_time || self->user_time == last_time))
- {
+ /* If its a transient (and its parents aren't focused) */
+ else if (client_has_parent(self)) {
activate = FALSE;
ob_debug_type(OB_DEBUG_FOCUS,
"Not focusing the window because it is a "
- "transient, and the time is very ambiguous\n");
+ "transient, and its relatives aren't focused\n");
}
/* Don't steal focus from globally active clients.
I stole this idea from KWin. It seems nice.
anyway */
else if (client_focus_target(self) != self) {
activate = FALSE;
+ raise = TRUE;
ob_debug_type(OB_DEBUG_FOCUS,
"Not focusing the window because another window "
"would get the focus anyway\n");
}
+ else if (!(self->desktop == screen_desktop ||
+ self->desktop == DESKTOP_ALL))
+ {
+ activate = FALSE;
+ raise = TRUE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because it is on "
+ "another desktop and no relatives are focused ");
+ }
}
if (!activate) {
ob_debug_type(OB_DEBUG_FOCUS,
- "Focus stealing prevention activated for %s with "
- "time %u (last time %u)\n",
- self->title, self->user_time, last_time);
+ "Focus stealing prevention activated for %s at "
+ "time %u (last user interactioon time %u)\n",
+ self->title, map_time, event_last_user_time);
/* if the client isn't focused, then hilite it so the user
knows it is there */
client_hilite(self, TRUE);
+ /* we may want to raise it even tho we're not activating it */
+ if (raise && !client_restore_session_stacking(self))
+ stacking_raise(CLIENT_AS_WINDOW(self));
}
}
else {
if (activate) {
gboolean stacked = client_restore_session_stacking(self);
- client_present(self, FALSE, !stacked);
+ client_present(self, FALSE, !stacked, TRUE);
}
/* add to client list/map */
frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
ob_debug("gave extents left %d right %d top %d bottom %d\n",
- self->frame->size.left, self->frame->size.right,
+ self->frame->size.left, self->frame->size.right,
self->frame->size.top, self->frame->size.bottom);
/* free the ObAppSettings shallow copy */
return self;
}
-void client_unmanage_all()
+void client_unmanage_all(void)
{
while (client_list != NULL)
client_unmanage(client_list->data);
/* 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)) {
gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
gboolean rude)
{
- Rect *mon_a, *all_a;
gint ox = *x, oy = *y;
gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
gint fw, fh;
Rect desired;
+ guint i;
RECT_SET(desired, *x, *y, w, h);
frame_rect_to_frame(self->frame, &desired);
- all_a = screen_area(self->desktop, &desired);
- mon_a = screen_area_monitor(self->desktop, screen_find_monitor(&desired),
- &desired);
-
/* get where the frame would be */
- frame_client_gravity(self->frame, x, y, w, h);
+ frame_client_gravity(self->frame, x, y);
/* get the requested size of the window with decorations */
fw = self->frame->size.left + w + self->frame->size.right;
fh = self->frame->size.top + h + self->frame->size.bottom;
- /* This makes sure windows aren't entirely outside of the screen so you
- can't see them at all.
- It makes sure 10% of the window is on the screen at least. At don't let
- it move itself off the top of the screen, which would hide the titlebar
- on you. (The user can still do this if they want too, it's only limiting
- the application.
-
- XXX watch for xinerama dead areas...
- */
- if (client_normal(self)) {
- if (!self->strut.right && *x + fw/10 >= all_a->x + all_a->width - 1)
- *x = all_a->x + all_a->width - fw/10;
- if (!self->strut.bottom && *y + fh/10 >= all_a->y + all_a->height - 1)
- *y = all_a->y + all_a->height - fh/10;
- if (!self->strut.left && *x + fw*9/10 - 1 < all_a->x)
- *x = all_a->x - fw*9/10;
- if (!self->strut.top && *y + fh*9/10 - 1 < all_a->y)
- *y = all_a->y - fw*9/10;
- }
-
- /* If rudeness wasn't requested, then figure out of the client is currently
- entirely on the screen. If it is, and the position isn't changing by
- request, and it is enlarging, then be rude even though it wasn't
- requested */
+ /* If rudeness wasn't requested, then still be rude in a given direction
+ if the client is not moving, only resizing in that direction */
if (!rude) {
Point oldtl, oldtr, oldbl, oldbr;
Point newtl, newtr, newbl, newbr;
rudeb = TRUE;
}
- /* This here doesn't let windows even a pixel outside the struts/screen.
- * When called from client_manage, programs placing themselves are
- * forced completely onscreen, while things like
- * xterm -geometry resolution-width/2 will work fine. Trying to
- * place it completely offscreen will be handled in the above code.
- * Sorry for this confused comment, i am tired. */
- if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x;
- if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width)
- *x = mon_a->x + MAX(0, mon_a->width - fw);
+ for (i = 0; i < screen_num_monitors; ++i) {
+ Rect *a;
- if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y;
- if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height)
- *y = mon_a->y + MAX(0, mon_a->height - fh);
+ if (!screen_physical_area_monitor_contains(i, &desired)) {
+ if (i < screen_num_monitors - 1)
+ continue;
- /* get where the client should be */
- frame_frame_gravity(self->frame, x, y, w, h);
+ /* the window is not inside any monitor! so just use the first
+ one */
+ a = screen_area(self->desktop, 0, NULL);
+ } else
+ a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
+
+ /* This makes sure windows aren't entirely outside of the screen so you
+ can't see them at all.
+ It makes sure 10% of the window is on the screen at least. At don't
+ let it move itself off the top of the screen, which would hide the
+ titlebar on you. (The user can still do this if they want too, it's
+ only limiting the application.
+ */
+ if (client_normal(self)) {
+ if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
+ *x = a->x + a->width - fw/10;
+ if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
+ *y = a->y + a->height - fh/10;
+ if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
+ *x = a->x - fw*9/10;
+ if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
+ *y = a->y - fh*9/10;
+ }
+
+ /* This here doesn't let windows even a pixel outside the
+ struts/screen. When called from client_manage, programs placing
+ themselves are forced completely onscreen, while things like
+ xterm -geometry resolution-width/2 will work fine. Trying to
+ place it completely offscreen will be handled in the above code.
+ Sorry for this confused comment, i am tired. */
+ if (rudel && !self->strut.left && *x < a->x) *x = a->x;
+ if (ruder && !self->strut.right && *x + fw > a->x + a->width)
+ *x = a->x + MAX(0, a->width - fw);
- g_free(all_a);
- g_free(mon_a);
+ if (rudet && !self->strut.top && *y < a->y) *y = a->y;
+ if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
+ *y = a->y + MAX(0, a->height - fh);
+
+ g_free(a);
+ }
+
+ /* get where the client should be */
+ frame_frame_gravity(self->frame, x, y);
return ox != *x || oy != *y;
}
if (self->type == OB_CLIENT_TYPE_DESKTOP)
self->desktop = DESKTOP_ALL;
}
-
+
#ifdef SYNC
client_update_sync_request_counter(self);
#endif
client_get_colormap(self);
client_update_strut(self);
client_update_icons(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);
}
{
XWindowAttributes wattrib;
Status ret;
-
+
ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
g_assert(ret != BadWindow);
self->desktop = screen_num_desktops - 1;
else
self->desktop = d;
- ob_debug("client requested desktop 0x%x\n", self->desktop);
+ ob_debug("client requested desktop 0x%x\n", self->desktop);
} else {
GSList *it;
gboolean first = TRUE;
{
guint32 *state;
guint num;
-
+
if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
gulong i;
for (i = 0; i < num; ++i) {
self->transient_for_group, trangroup,
client_direct_parent(self), target);
self->transient_for_group = trangroup;
-
+
}
static void client_update_transient_tree(ObClient *self,
oldgtran == newgtran &&
oldparent == newparent) return;
- /** Remove the client from the transient tree wherever it has changed **/
+ /** Remove the client from the transient tree **/
- /* If the window is becoming a direct transient for a window in its group
- 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 (!oldgtran && oldparent != newparent && newparent != NULL &&
- newgroup != NULL && newgroup == oldgroup &&
- client_normal(newparent))
- {
- ObClient *look = client_search_top_direct_parent(newparent);
- self->transients = g_slist_remove(self->transients, look);
- look->parents = g_slist_remove(look->parents, self);
- }
-
-
- /* If the group changed, or if we are just becoming transient for the
- group, then we need to remove any old group transient windows
- from our children. But if we were already transient for the group, then
- other group transients are not our children. */
- if ((oldgroup != newgroup || (newgtran && oldgtran != newgtran)) &&
- oldgroup != NULL && !oldgtran)
- {
- for (it = self->transients; it; it = next) {
- next = g_slist_next(it);
- c = it->data;
- if (c->group == oldgroup && client_normal(self)) {
- self->transients = g_slist_delete_link(self->transients, it);
- c->parents = g_slist_remove(c->parents, self);
- }
- }
+ for (it = self->transients; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->transients = g_slist_delete_link(self->transients, it);
+ c->parents = g_slist_remove(c->parents, self);
}
-
- /* If we used to be transient for a group and now we are not, or we're
- transient for a new group, then we need to remove ourselves from all
- our ex-parents */
- if (oldgtran && (oldgroup != newgroup || oldgtran != newgtran))
- {
- for (it = self->parents; it; it = next) {
- next = g_slist_next(it);
- c = it->data;
- if (!c->transient_for_group && client_normal(c)) {
- c->transients = g_slist_remove(c->transients, self);
- self->parents = g_slist_delete_link(self->parents, it);
- }
- }
- }
- /* If we used to be transient for a single window and we are no longer
- transient for it, then we need to remove ourself from its children */
- else if (oldparent && oldparent != newparent &&
- client_normal(oldparent))
- {
- oldparent->transients = g_slist_remove(oldparent->transients, self);
- self->parents = g_slist_remove(self->parents, oldparent);
+ for (it = self->parents; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->parents = g_slist_delete_link(self->parents, it);
+ c->transients = g_slist_remove(c->transients, self);
}
- /** Re-add the client to the transient tree wherever it has changed **/
+ /** Re-add the client to the transient tree **/
- /* If we're now transient for a group and we weren't transient for it
- before then we need to add ourselves to all our new parents */
- if (newgtran && (oldgroup != newgroup || oldgtran != newgtran))
- {
- for (it = oldgroup->members; it; it = g_slist_next(it)) {
+ /* If we're transient for a group then we need to add ourselves to all our
+ parents */
+ if (newgtran) {
+ for (it = newgroup->members; it; it = g_slist_next(it)) {
c = it->data;
- if (c != self && !c->transient_for_group && client_normal(c))
+ if (c != self &&
+ !client_search_top_direct_parent(c)->transient_for_group &&
+ client_normal(c))
{
c->transients = g_slist_prepend(c->transients, self);
self->parents = g_slist_prepend(self->parents, c);
}
}
}
- /* If we are now transient for a single window which we weren't before,
- we need to add ourselves to its children
+
+ /* If we are now transient for a single window we need to add ourselves to
+ its children
WARNING: Cyclical transient ness is possible if two windows are
transient for eachother.
*/
- else if (newparent && newparent != oldparent &&
+ else if (newparent &&
/* don't make ourself its child if it is already our child */
!client_is_direct_child(self, newparent) &&
client_normal(newparent))
self->parents = g_slist_prepend(self->parents, newparent);
}
- /* If the group changed then we need to add any new group transient
- windows to our children. But if we're transient for the group, then
- other group transients are not our children.
+ /* Add any group transient windows to our children. But if we're transient
+ for the group, then other group transients are not our children.
WARNING: Cyclical transient-ness is possible. For e.g. if:
A is transient for the group
C is transient for B
A can't be transient for C or we have a cycle
*/
- if (oldgroup != newgroup && newgroup != NULL && !newgtran &&
+ if (!newgtran && newgroup &&
+ (!newparent ||
+ !client_search_top_direct_parent(newparent)->transient_for_group) &&
client_normal(self))
{
for (it = newgroup->members; it; it = g_slist_next(it)) {
}
}
}
+
+ /** If we change our group transient-ness, our children change their
+ effect group transient-ness, which affects how they relate to other
+ group windows **/
+
+ for (it = self->transients; it; it = g_slist_next(it)) {
+ c = it->data;
+ if (!c->transient_for_group)
+ client_update_transient_tree(c, c->group, c->group,
+ c->transient_for_group,
+ c->transient_for_group,
+ client_direct_parent(c),
+ client_direct_parent(c));
+ }
}
static void client_get_mwm_hints(ObClient *self)
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 */
for (i = 0; i < num; ++i) {
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
notified whenever we want it to receive focus */
self->focus_notify = TRUE;
#ifdef SYNC
- else if (proto[i] == prop_atoms.net_wm_sync_request)
+ else if (proto[i] == prop_atoms.net_wm_sync_request)
/* if this protocol is requested, then resizing the
window will be synchronized between the frame and the
client */
if (size.flags & PMinSize)
SIZE_SET(self->min_size, size.min_width, size.min_height);
-
+
if (size.flags & PMaxSize)
SIZE_SET(self->max_size, size.max_width, size.max_height);
-
+
if (size.flags & PBaseSize)
SIZE_SET(self->base_size, size.base_width, size.base_height);
-
+
if (size.flags & PResizeInc && size.width_inc && size.height_inc)
SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
switch (self->type) {
case OB_CLIENT_TYPE_NORMAL:
/* normal windows retain all of the possible decorations and
- functionality, and are the only windows that you can fullscreen */
+ functionality, and can be fullscreen */
self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
break;
case OB_CLIENT_TYPE_DIALOG:
+ /* sometimes apps make dialog windows fullscreen for some reason (for
+ e.g. kpdf does this..) */
+ self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
+ break;
+
case OB_CLIENT_TYPE_UTILITY:
/* these windows don't have anything added or removed by default */
break;
client_change_allowed_actions(self);
if (reconfig)
- client_reconfigure(self);
+ /* force reconfigure to make sure decorations are updated */
+ client_reconfigure(self, TRUE);
}
static void client_change_allowed_actions(ObClient *self)
}
}
-void client_reconfigure(ObClient *self)
-{
- client_configure(self, self->area.x, self->area.y,
- self->area.width, self->area.height,
- FALSE, TRUE);
-}
-
void client_update_wmhints(ObClient *self)
{
XWMHints *hints;
/* assume a window takes input if it doesnt specify */
self->can_focus = TRUE;
-
+
if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
gboolean ur;
gchar *visible = NULL;
g_free(self->title);
-
+
/* try netwm */
if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
/* try old x stuff */
if (!(PROP_GETS(self->window, wm_name, locale, &data)
|| PROP_GETS(self->window, wm_name, utf8, &data))) {
if (self->transient) {
- /*
- GNOME alert windows are not given titles:
- http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
- */
+ /*
+ GNOME alert windows are not given titles:
+ http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+ */
data = g_strdup("");
} else
data = g_strdup("Unnamed Window");
if (!got &&
PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
if (num == 4) {
- const Rect *a;
+ Rect *a;
got = TRUE;
/* use the screen's width/height */
- a = screen_physical_area();
+ a = screen_physical_area_all_monitors();
STRUT_PARTIAL_SET(strut,
data[0], data[2], data[1], data[3],
a->x, a->x + a->width - 1,
a->y, a->y + a->height - 1,
a->x, a->x + a->width - 1);
+ g_free(a);
}
g_free(data);
}
}
self->icons = g_new(ObClientIcon, self->nicons);
-
+
/* store the icons */
i = 0;
for (j = 0; j < self->nicons; ++j) {
/* set the default icon onto the window
in theory, this could be a race, but if a window doesn't set an icon
or removes it entirely, it's not very likely it is going to set one
- right away afterwards */
- if (self->nicons == 0) {
+ right away afterwards
+
+ if it has parents, then one of them will have an icon already
+ */
+ if (self->nicons == 0 && !self->parents) {
RrPixel32 *icon = ob_rr_theme->def_win_icon;
gulong *data;
frame_adjust_icon(self->frame);
}
-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 (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
- assume noone is setting times older than the last one, cuz that
- would be pretty stupid anyways
- */
- self->user_time = 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);
- }
-}
-
void client_update_icon_geometry(ObClient *self)
{
guint num;
static ObStackingLayer calc_layer(ObClient *self)
{
ObStackingLayer l;
+ Rect *monitor;
+
+ monitor = screen_physical_area_monitor(client_monitor(self));
if (self->type == OB_CLIENT_TYPE_DESKTOP)
l = OB_STACKING_LAYER_DESKTOP;
*/
(self->decorations == 0 &&
!(self->max_horz && self->max_vert) &&
- RECT_EQUAL(self->area,
- *screen_physical_area_monitor
- (client_monitor(self))))) &&
+ RECT_EQUAL(self->area, *monitor))) &&
(client_focused(self) || client_search_focus_tree(self)))
l = OB_STACKING_LAYER_FULLSCREEN;
else if (self->above) l = OB_STACKING_LAYER_ABOVE;
else if (self->below) l = OB_STACKING_LAYER_BELOW;
else l = OB_STACKING_LAYER_NORMAL;
+ g_free(monitor);
+
return l;
}
return FALSE;
if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
return TRUE;
-
+
return FALSE;
}
client_shade(self, TRUE);
if (demands_attention)
client_hilite(self, TRUE);
-
+
if (max_vert && max_horz)
client_maximize(self, TRUE, 0);
else if (max_vert)
else if (max_horz)
client_maximize(self, TRUE, 1);
- /* if the window hasn't been configured yet, then do so now */
- if (!fullscreen && !max_vert && !max_horz) {
- self->area = oldarea;
- client_configure(self, x, y, w, h, FALSE, TRUE);
- }
+ /* if the window hasn't been configured yet, then do so now, in fact the
+ x,y,w,h may _not_ be the same as the area rect, which can end up
+ meaning that the client isn't properly moved/resized by the fullscreen
+ function
+ pho can cause this because it maps at size of the screen but not 0,0
+ so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
+ then fullscreen'ing makes it go to 0,0 which it thinks it already is at
+ cuz thats where the pre-fullscreen will be. however the actual area is
+ not, so this needs to be called even if we have fullscreened/maxed
+ */
+ self->area = oldarea;
+ client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
/* set the desktop hint, to make sure that it always exists */
PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
/* gets the frame's position */
- frame_client_gravity(self->frame, x, y, *w, *h);
+ frame_client_gravity(self->frame, x, y);
/* these positions are frame positions, not client positions */
user = FALSE; /* ignore if the client can't be moved/resized when it
is fullscreening */
+
+ g_free(a);
} else if (self->max_horz || self->max_vert) {
Rect *a;
guint i;
+ /* use all possible struts when maximizing to the full screen */
i = screen_find_monitor(&desired);
- a = screen_area_monitor(self->desktop, i, &desired);
+ a = screen_area(self->desktop, i,
+ (self->max_horz && self->max_vert ? NULL : &desired));
/* set the size and position if maximized */
if (self->max_horz) {
}
/* gets the client's position */
- frame_frame_gravity(self->frame, x, y, *w, *h);
+ frame_frame_gravity(self->frame, x, y);
/* work within the prefered sizes given by the window */
if (!(*w == self->area.width && *h == self->area.height)) {
/* you cannot resize to nothing */
if (basew + *w < 1) *w = 1 - basew;
if (baseh + *h < 1) *h = 1 - baseh;
-
+
/* save the logical size */
*logicalw = incw > 1 ? *w : *w + basew;
*logicalh = inch > 1 ? *h : *h + baseh;
void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
- gboolean user, gboolean final)
+ gboolean user, gboolean final, gboolean force_reply)
{
gint oldw, oldh;
gboolean send_resize_client;
- gboolean moved = FALSE, resized = FALSE;
+ gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
gboolean fmoved, fresized;
guint fdecor = self->frame->decorations;
gboolean fhorz = self->frame->max_horz;
}
/* adjust the frame */
- if (fmoved || fresized)
+ if (fmoved || fresized) {
+ gulong ignore_start;
+ if (!user)
+ ignore_start = event_start_ignore_all_enters();
+
frame_adjust_area(self->frame, fmoved, fresized, FALSE);
+ if (!user)
+ event_end_ignore_all_enters(ignore_start);
+ }
+
+ if (!user || final) {
+ gint oldrx = self->root_pos.x;
+ gint oldry = self->root_pos.y;
+ /* we have reset the client to 0 border width, so don't include
+ it in these coords */
+ POINT_SET(self->root_pos,
+ self->frame->area.x + self->frame->size.left -
+ self->border_width,
+ self->frame->area.y + self->frame->size.top -
+ self->border_width);
+ if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
+ rootmoved = TRUE;
+ }
+
/* This is kinda tricky and should not be changed.. let me explain!
When user = FALSE, then the request is coming from the application
itself, and we are more strict about when to send a synthetic
ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
- in this case.
+ in this case (if force_reply is true)
When user = TRUE, then the request is coming from "us", like when we
- maximize a window or sometihng. In this case we are more lenient. We
+ maximize a window or something. In this case we are more lenient. We
used to follow the same rules as above, but _Java_ Swing can't handle
this. So just to appease Swing, when user = TRUE, we always send
a synthetic ConfigureNotify to give the window its root coordinates.
*/
- if ((!user && !resized) || (user && final))
+ if ((!user && !resized && (rootmoved || force_reply)) ||
+ (user && final && rootmoved))
{
XEvent event;
- /* we have reset the client to 0 border width, so don't include
- it in these coords */
- POINT_SET(self->root_pos,
- self->frame->area.x + self->frame->size.left -
- self->border_width,
- self->frame->area.y + self->frame->size.top -
- self->border_width);
-
event.type = ConfigureNotify;
event.xconfigure.display = ob_display;
event.xconfigure.event = self->window;
RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
}
+ ob_debug("Window %s going fullscreen (%d)\n",
+ self->title, self->fullscreen);
+
client_setup_decor_and_functions(self, FALSE);
client_move_resize(self, x, y, w, h);
if (curdesk && self->desktop != screen_desktop &&
self->desktop != DESKTOP_ALL)
- client_set_desktop(self, screen_desktop, FALSE);
+ client_set_desktop(self, screen_desktop, FALSE, FALSE);
/* this puts it after the current focused window */
focus_order_remove(self);
void client_maximize(ObClient *self, gboolean max, gint dir)
{
gint x, y, w, h;
-
+
g_assert(dir == 0 || dir == 1 || dir == 2);
if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
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);
+ frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
}
void client_close(ObClient *self)
close, we just kill it */
if (!self->delete_window)
client_kill(self);
-
+
/*
XXX: itd be cool to do timeouts and shit here for killing the client's
process off
void client_set_desktop_recursive(ObClient *self,
guint target,
- gboolean donthide)
+ gboolean donthide,
+ gboolean dontraise)
{
guint old;
GSList *it;
frame_adjust_state(self->frame);
/* 'move' the window to the new desktop */
if (!donthide)
- client_showhide(self);
+ client_hide(self);
+ client_show(self);
/* raise if it was not already on the desktop */
- if (old != DESKTOP_ALL)
+ if (old != DESKTOP_ALL && !dontraise)
stacking_raise(CLIENT_AS_WINDOW(self));
- /* the new desktop's geometry may be different, so we may need to
- resize, for example if we are maximized */
- client_reconfigure(self);
if (STRUT_EXISTS(self->strut))
screen_update_areas();
+ else
+ /* the new desktop's geometry may be different, so we may need to
+ resize, for example if we are maximized */
+ client_reconfigure(self, FALSE);
}
/* move all transients */
for (it = self->transients; it; it = g_slist_next(it))
if (it->data != self)
if (client_is_direct_child(self, it->data))
- client_set_desktop_recursive(it->data, target, donthide);
+ client_set_desktop_recursive(it->data, target,
+ donthide, dontraise);
}
-void client_set_desktop(ObClient *self, guint target, gboolean donthide)
+void client_set_desktop(ObClient *self, guint target,
+ gboolean donthide, gboolean dontraise)
{
self = client_search_top_direct_parent(self);
- client_set_desktop_recursive(self, target, donthide);
+ client_set_desktop_recursive(self, target, donthide, dontraise);
}
gboolean client_is_direct_child(ObClient *parent, ObClient *child)
{
GSList *it;
ObClient *ret;
-
+
for (it = self->transients; it; it = g_slist_next(it)) {
ObClient *c = it->data;
if ((ret = client_search_modal_child(c))) return ret;
gboolean client_validate(ObClient *self)
{
- XEvent e;
+ XEvent e;
XSync(ob_display, FALSE); /* get all events on the server */
void client_set_wm_state(ObClient *self, glong state)
{
if (state == self->wmstate) return; /* no change */
-
+
switch (state) {
case IconicState:
client_iconify(self, TRUE, TRUE, FALSE);
action == prop_atoms.net_wm_state_remove ||
action == prop_atoms.net_wm_state_toggle))
/* an invalid action was passed to the client message, ignore it */
- return;
+ return;
for (i = 0; i < 2; ++i) {
Atom state = i == 0 ? data1 : data2;
-
+
if (!state) continue;
/* if toggling, then pick whether we're adding or removing */
action = undecorated ? prop_atoms.net_wm_state_remove :
prop_atoms.net_wm_state_add;
}
-
+
if (action == prop_atoms.net_wm_state_add) {
if (state == prop_atoms.net_wm_state_modal) {
modal = TRUE;
"Focusing client \"%s\" (0x%x) at time %u\n",
self->title, self->window, event_curtime);
+ /* if using focus_delay, stop the timer now so that focus doesn't
+ go moving on us */
+ event_halt_focus_delay();
+
/* 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 !
return !xerror_occured;
}
-/*! Present the client to the user.
- @param raise If the client should be raised or not. You should only set
- raise to false if you don't care if the window is completely
- hidden.
-*/
-static void client_present(ObClient *self, gboolean here, gboolean raise)
+static void client_present(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade)
{
- /* if using focus_delay, stop the timer now so that focus doesn't
- go moving on us */
- event_halt_focus_delay();
-
if (client_normal(self) && screen_showing_desktop)
screen_show_desktop(FALSE, self);
if (self->iconic)
self->desktop != screen_desktop)
{
if (here)
- client_set_desktop(self, screen_desktop, FALSE);
+ client_set_desktop(self, screen_desktop, FALSE, TRUE);
else
screen_set_desktop(self->desktop, FALSE);
} else if (!self->frame->visible)
/* if its not visible for other reasons, then don't mess
with it */
return;
- if (self->shaded)
+ if (self->shaded && unshade)
client_shade(self, FALSE);
if (raise)
stacking_raise(CLIENT_AS_WINDOW(self));
client_focus(self);
}
-void client_activate(ObClient *self, gboolean here, gboolean user)
+void client_activate(ObClient *self, gboolean here, gboolean raise,
+ gboolean unshade, gboolean user)
{
- guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
- gboolean allow = FALSE;
-
- /* if the currently focused app doesn't set a user_time, then it can't
- benefit from any focus stealing prevention.
-
- if the timestamp is missing in the request then let it go through
- even if it is source=app, because EVERY APPLICATION DOES THIS because
- GTK IS VERY BUGGY AND HARDCODES source=application... WHY!?
- */
- if (!last_time || !event_curtime)
- allow = TRUE;
- /* otherwise, if they didn't give a time stamp or if it is too old, they
- don't get focus */
- else
- allow = event_time_after(event_curtime, last_time);
-
- ob_debug_type(OB_DEBUG_FOCUS,
- "Want to activate window 0x%x with time %u (last time %u), "
- "source=%s allowing? %d\n",
- self->window, event_curtime, last_time,
- (user ? "user" : "application"), allow);
-
- if (allow)
- client_present(self, here, TRUE);
- else
- /* don't focus it but tell the user it wants attention */
- client_hilite(self, TRUE);
+ client_present(self, here, raise, unshade);
}
static void client_bring_windows_recursive(ObClient *self,
if (iconic && self->iconic)
client_iconify(self, FALSE, TRUE, FALSE);
else
- client_set_desktop(self, desktop, FALSE);
+ client_set_desktop(self, desktop, FALSE, FALSE);
}
}
if ((parent = client_icon_recursive(c, w, h)))
break;
}
-
+
return parent;
}
void client_set_undecorated(ObClient *self, gboolean undecorated)
{
if (self->undecorated != undecorated &&
- /* don't let it undecorate if the function is missing, but let
+ /* don't let it undecorate if the function is missing, but let
it redecorate */
(self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
{
if (!self->parents) return NULL;
if (self->transient_for_group) return NULL;
return self->parents->data;
-}
+}
ObClient *client_search_top_direct_parent(ObClient *self)
{
{
GSList *ret;
ObClient *p;
-
+
/* move up the direct transient chain as far as possible */
while ((p = client_direct_parent(self)) &&
(!bylayer || p->layer == layer))
return NULL;
}
-#define WANT_EDGE(cur, c) \
- if (cur == c) \
- continue; \
- if (c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && \
- cur->desktop != screen_desktop) \
- continue; \
- if (cur->iconic) \
- continue;
+static void detect_edge(Rect area, ObDirection dir,
+ gint my_head, gint my_size,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge)
+{
+ gint edge_start, edge_size, head, tail;
+ gboolean skip_head = FALSE, skip_tail = FALSE;
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_SOUTH:
+ edge_start = area.x;
+ edge_size = area.width;
+ break;
+ case OB_DIRECTION_EAST:
+ case OB_DIRECTION_WEST:
+ edge_start = area.y;
+ edge_size = area.height;
+ break;
+ default:
+ g_assert_not_reached();
+ }
-#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
- if ((his_edge_start >= my_edge_start && \
- his_edge_start <= my_edge_end) || \
- (my_edge_start >= his_edge_start && \
- my_edge_start <= his_edge_end)) \
- dest = his_offset;
-
-/* finds the nearest edge in the given direction from the current client
- * note to self: the edge is the -frame- edge (the actual one), not the
- * client edge.
- */
-gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
-{
- gint dest, monitor_dest;
- gint my_edge_start, my_edge_end, my_offset;
- GList *it;
- Rect *a, *monitor;
-
- if(!client_list)
- return -1;
+ /* do we collide with this window? */
+ if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
+ edge_start, edge_size))
+ return;
- a = screen_area(c->desktop, &c->frame->area);
- monitor = screen_area_monitor(c->desktop, client_monitor(c),
- &c->frame->area);
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ head = RECT_BOTTOM(area);
+ tail = RECT_TOP(area);
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_TOP(area);
+ tail = RECT_BOTTOM(area);
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_RIGHT(area);
+ tail = RECT_LEFT(area);
+ break;
+ case OB_DIRECTION_EAST:
+ head = RECT_LEFT(area);
+ tail = RECT_RIGHT(area);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ case OB_DIRECTION_WEST:
+ /* check if our window is past the head of this window */
+ if (my_head <= head + 1)
+ skip_head = TRUE;
+ /* check if our window's tail is past the tail of this window */
+ if (my_head + my_size - 1 <= tail)
+ skip_tail = TRUE;
+ /* check if the head of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a tail, not a head) */
+ if (head + (*near_edge ? 0 : my_size) < *dest)
+ skip_head = TRUE;
+ /* check if the tail of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a head, not a tail) */
+ if (tail - (!*near_edge ? 0 : my_size) < *dest)
+ skip_tail = TRUE;
+ break;
+ case OB_DIRECTION_SOUTH:
+ case OB_DIRECTION_EAST:
+ /* check if our window is past the head of this window */
+ if (my_head >= head - 1)
+ skip_head = TRUE;
+ /* check if our window's tail is past the tail of this window */
+ if (my_head - my_size + 1 >= tail)
+ skip_tail = TRUE;
+ /* check if the head of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a tail, not a head) */
+ if (head - (*near_edge ? 0 : my_size) > *dest)
+ skip_head = TRUE;
+ /* check if the tail of this window is closer than the previously
+ chosen edge (take into account that the previously chosen
+ edge might have been a head, not a tail) */
+ if (tail + (!*near_edge ? 0 : my_size) > *dest)
+ skip_tail = TRUE;
+ break;
+ default:
+ g_assert_not_reached();
+ }
- switch(dir) {
- case OB_DIRECTION_NORTH:
- my_edge_start = c->frame->area.x;
- my_edge_end = c->frame->area.x + c->frame->area.width;
- my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
-
- /* default: top of screen */
- dest = a->y + (hang ? c->frame->area.height : 0);
- monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
- /* if the monitor edge comes before the screen edge, */
- /* use that as the destination instead. (For xinerama) */
- if (monitor_dest != dest && my_offset > monitor_dest)
- dest = monitor_dest;
-
- for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
- gint his_edge_start, his_edge_end, his_offset;
- ObClient *cur = it->data;
-
- WANT_EDGE(cur, c)
-
- his_edge_start = cur->frame->area.x;
- his_edge_end = cur->frame->area.x + cur->frame->area.width;
- his_offset = cur->frame->area.y +
- (hang ? 0 : cur->frame->area.height);
-
- if(his_offset + 1 > my_offset)
- continue;
+ ob_debug("my head %d size %d\n", my_head, my_size);
+ ob_debug("head %d tail %d deest %d\n", head, tail, *dest);
+ if (!skip_head) {
+ ob_debug("using near edge %d\n", head);
+ *dest = head;
+ *near_edge = TRUE;
+ }
+ else if (!skip_tail) {
+ ob_debug("using far edge %d\n", tail);
+ *dest = tail;
+ *near_edge = FALSE;
+ }
+}
- if(his_offset < dest)
- continue;
+void client_find_edge_directional(ObClient *self, ObDirection dir,
+ gint my_head, gint my_size,
+ gint my_edge_start, gint my_edge_size,
+ gint *dest, gboolean *near_edge)
+{
+ GList *it;
+ Rect *a, *mon;
+ Rect dock_area;
+ gint edge;
- HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
- }
+ a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
+ &self->frame->area);
+ mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
+ &self->frame->area);
+
+ switch (dir) {
+ case OB_DIRECTION_NORTH:
+ if (my_head >= RECT_TOP(*mon) + 1)
+ edge = RECT_TOP(*mon) - 1;
+ else
+ edge = RECT_TOP(*a) - 1;
break;
case OB_DIRECTION_SOUTH:
- my_edge_start = c->frame->area.x;
- my_edge_end = c->frame->area.x + c->frame->area.width;
- my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
+ if (my_head <= RECT_BOTTOM(*mon) - 1)
+ edge = RECT_BOTTOM(*mon) + 1;
+ else
+ edge = RECT_BOTTOM(*a) + 1;
+ break;
+ case OB_DIRECTION_EAST:
+ if (my_head <= RECT_RIGHT(*mon) - 1)
+ edge = RECT_RIGHT(*mon) + 1;
+ else
+ edge = RECT_RIGHT(*a) + 1;
+ break;
+ case OB_DIRECTION_WEST:
+ if (my_head >= RECT_LEFT(*mon) + 1)
+ edge = RECT_LEFT(*mon) - 1;
+ else
+ edge = RECT_LEFT(*a) - 1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ /* default to the far edge, then narrow it down */
+ *dest = edge;
+ *near_edge = TRUE;
- /* default: bottom of screen */
- dest = a->y + a->height - (hang ? c->frame->area.height : 0);
- monitor_dest = monitor->y + monitor->height -
- (hang ? c->frame->area.height : 0);
- /* if the monitor edge comes before the screen edge, */
- /* use that as the destination instead. (For xinerama) */
- if (monitor_dest != dest && my_offset < monitor_dest)
- dest = monitor_dest;
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *cur = it->data;
- for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
- gint his_edge_start, his_edge_end, his_offset;
- ObClient *cur = it->data;
+ /* skip windows to not bump into */
+ if (cur == self)
+ continue;
+ if (cur->iconic)
+ continue;
+ if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+ cur->desktop != screen_desktop)
+ continue;
- WANT_EDGE(cur, c)
+ ob_debug("trying window %s\n", cur->title);
- his_edge_start = cur->frame->area.x;
- his_edge_end = cur->frame->area.x + cur->frame->area.width;
- his_offset = cur->frame->area.y +
- (hang ? cur->frame->area.height : 0);
+ detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ }
+ dock_get_area(&dock_area);
+ detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ g_free(a);
+ g_free(mon);
+}
+void client_find_move_directional(ObClient *self, ObDirection dir,
+ gint *x, gint *y)
+{
+ gint head, size;
+ gint e, e_start, e_size;
+ gboolean near;
- if(his_offset - 1 < my_offset)
- continue;
-
- if(his_offset > dest)
- continue;
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ head = RECT_RIGHT(self->frame->area);
+ size = self->frame->area.width;
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_LEFT(self->frame->area);
+ size = self->frame->area.width;
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ break;
+ case OB_DIRECTION_NORTH:
+ head = RECT_TOP(self->frame->area);
+ size = self->frame->area.height;
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_BOTTOM(self->frame->area);
+ size = self->frame->area.height;
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ break;
+ default:
+ g_assert_not_reached();
+ }
- HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
- }
+ client_find_edge_directional(self, dir, head, size,
+ e_start, e_size, &e, &near);
+ *x = self->frame->area.x;
+ *y = self->frame->area.y;
+ switch (dir) {
+ case OB_DIRECTION_EAST:
+ if (near) e -= self->frame->area.width;
+ else e++;
+ *x = e;
break;
case OB_DIRECTION_WEST:
- my_edge_start = c->frame->area.y;
- my_edge_end = c->frame->area.y + c->frame->area.height;
- my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
-
- /* default: leftmost egde of screen */
- dest = a->x + (hang ? c->frame->area.width : 0);
- monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
- /* if the monitor edge comes before the screen edge, */
- /* use that as the destination instead. (For xinerama) */
- if (monitor_dest != dest && my_offset > monitor_dest)
- dest = monitor_dest;
-
- for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
- gint his_edge_start, his_edge_end, his_offset;
- ObClient *cur = it->data;
-
- WANT_EDGE(cur, c)
-
- his_edge_start = cur->frame->area.y;
- his_edge_end = cur->frame->area.y + cur->frame->area.height;
- his_offset = cur->frame->area.x +
- (hang ? 0 : cur->frame->area.width);
-
- if(his_offset + 1 > my_offset)
- continue;
+ if (near) e++;
+ else e -= self->frame->area.width;
+ *x = e;
+ break;
+ case OB_DIRECTION_NORTH:
+ if (near) e++;
+ else e -= self->frame->area.height;
+ *y = e;
+ break;
+ case OB_DIRECTION_SOUTH:
+ if (near) e -= self->frame->area.height;
+ else e++;
+ *y = e;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ frame_frame_gravity(self->frame, x, y);
+}
- if(his_offset < dest)
- continue;
+void client_find_resize_directional(ObClient *self, ObDirection side,
+ gboolean grow,
+ gint *x, gint *y, gint *w, gint *h)
+{
+ gint head;
+ gint e, e_start, e_size, delta;
+ gboolean near;
+ ObDirection dir;
- HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
- }
- break;
+ switch (side) {
case OB_DIRECTION_EAST:
- my_edge_start = c->frame->area.y;
- my_edge_end = c->frame->area.y + c->frame->area.height;
- my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
-
- /* default: rightmost edge of screen */
- dest = a->x + a->width - (hang ? c->frame->area.width : 0);
- monitor_dest = monitor->x + monitor->width -
- (hang ? c->frame->area.width : 0);
- /* if the monitor edge comes before the screen edge, */
- /* use that as the destination instead. (For xinerama) */
- if (monitor_dest != dest && my_offset < monitor_dest)
- dest = monitor_dest;
-
- for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
- gint his_edge_start, his_edge_end, his_offset;
- ObClient *cur = it->data;
-
- WANT_EDGE(cur, c)
-
- his_edge_start = cur->frame->area.y;
- his_edge_end = cur->frame->area.y + cur->frame->area.height;
- his_offset = cur->frame->area.x +
- (hang ? cur->frame->area.width : 0);
-
- if(his_offset - 1 < my_offset)
- continue;
-
- if(his_offset > dest)
- continue;
-
- HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
- }
+ head = RECT_RIGHT(self->frame->area) +
+ (self->size_inc.width - 1) * (grow ? 1 : -1);
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
+ break;
+ case OB_DIRECTION_WEST:
+ head = RECT_LEFT(self->frame->area) -
+ (self->size_inc.width - 1) * (grow ? 1 : -1);
+ e_start = RECT_TOP(self->frame->area);
+ e_size = self->frame->area.height;
+ dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
+ break;
+ case OB_DIRECTION_NORTH:
+ head = RECT_TOP(self->frame->area) -
+ (self->size_inc.height - 1) * (grow ? 1 : -1);
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
+ break;
+ case OB_DIRECTION_SOUTH:
+ head = RECT_BOTTOM(self->frame->area) +
+ (self->size_inc.height - 1) * (grow ? 1 : -1);
+ e_start = RECT_LEFT(self->frame->area);
+ e_size = self->frame->area.width;
+ dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
break;
- case OB_DIRECTION_NORTHEAST:
- case OB_DIRECTION_SOUTHEAST:
- case OB_DIRECTION_NORTHWEST:
- case OB_DIRECTION_SOUTHWEST:
- /* not implemented */
default:
g_assert_not_reached();
- dest = 0; /* suppress warning */
}
- g_free(a);
- g_free(monitor);
- return dest;
+ ob_debug("head %d dir %d\n", head, dir);
+ client_find_edge_directional(self, dir, head, 1,
+ e_start, e_size, &e, &near);
+ ob_debug("edge %d\n", e);
+ *x = self->frame->area.x;
+ *y = self->frame->area.y;
+ *w = self->frame->area.width;
+ *h = self->frame->area.height;
+ switch (side) {
+ case OB_DIRECTION_EAST:
+ if (grow == near) --e;
+ delta = e - RECT_RIGHT(self->frame->area);
+ *w += delta;
+ break;
+ case OB_DIRECTION_WEST:
+ if (grow == near) ++e;
+ delta = RECT_LEFT(self->frame->area) - e;
+ *x -= delta;
+ *w += delta;
+ break;
+ case OB_DIRECTION_NORTH:
+ if (grow == near) ++e;
+ delta = RECT_TOP(self->frame->area) - e;
+ *y -= delta;
+ *h += delta;
+ break;
+ case OB_DIRECTION_SOUTH:
+ if (grow == near) --e;
+ delta = e - RECT_BOTTOM(self->frame->area);
+ *h += delta;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ frame_frame_gravity(self->frame, x, y);
+ *w -= self->frame->size.left + self->frame->size.right;
+ *h -= self->frame->size.top + self->frame->size.bottom;
}
-ObClient* client_under_pointer()
+ObClient* client_under_pointer(void)
{
gint x, y;
GList *it;