/* -*- 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"
static ObAppSettings *client_get_settings_state(ObClient *self);
static void client_update_transient_tree(ObClient *self,
ObGroup *oldgroup, ObGroup *newgroup,
+ 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);
static void client_call_notifies(ObClient *self, GSList *list);
+
void client_startup(gboolean reconfig)
{
if (reconfig) return;
}
}
-void client_set_list()
+void client_set_list(void)
{
Window *windows, *win_it;
GList *it;
stacking_set_list();
}
-/*
- void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
- {
- GSList *it;
-
- for (it = self->transients; it; it = g_slist_next(it)) {
- if (!func(it->data, data)) return;
- client_foreach_transient(it->data, func, data);
- }
- }
-
- void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
- {
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- if (!func(self->transient_for, data)) return;
- client_foreach_ancestor(self->transient_for, func, data);
- } else {
- GSList *it;
-
- for (it = self->group->members; it; it = g_slist_next(it))
- if (it->data != self &&
- !((ObClient*)it->data)->transient_for) {
- if (!func(it->data, data)) return;
- client_foreach_ancestor(it->data, func, data);
- }
- }
- }
- }
-*/
-
-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. */
/* focus the new window? */
if (ob_state() != OB_STATE_STARTING &&
(!self->session || self->session->focused) &&
- !self->iconic &&
/* this means focus=true for window is same as config_focus_new=true */
((config_focus_new || (settings && settings->focus == 1)) ||
client_search_focus_tree_full(self)) &&
/* this checks for focus=false for the window */
(!settings || settings->focus != 0) &&
- /* note the check against type Normal/Dialog/Utility,
- not client_normal(self), which would also include other types.
- in this case we want more strict rules for focus */
- (self->type == OB_CLIENT_TYPE_NORMAL ||
- self->type == OB_CLIENT_TYPE_UTILITY ||
- self->type == OB_CLIENT_TYPE_DIALOG))
+ 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
splash screens get "transient" set to TRUE by
the place_client call
*/
- transient ||
- (!(self->positioned & USPosition) &&
- client_normal(self) &&
- !self->session));
+ ob_state() == OB_STATE_RUNNING &&
+ (transient ||
+ (!((self->positioned & USPosition) ||
+ (settings && settings->pos_given)) &&
+ client_normal(self) &&
+ !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
splash screens get "transient" set to TRUE by
the place_client call
*/
- if (transient ||
- (!(self->sized & USSize) &&
- client_normal(self) &&
- !self->session))
+ if (ob_state() == OB_STATE_RUNNING &&
+ (transient ||
+ (!(self->sized & USSize || self->positioned & USPosition) &&
+ client_normal(self) &&
+ !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)))))
{
- /* make a copy to modify */
- Rect a = *screen_area_monitor(self->desktop, client_monitor(self));
+ Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
- /* 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;
+ /* 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;
/* 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);
+ place.width = MIN(place.width, a->width);
+ place.height = MIN(place.height, a->height);
- ob_debug("setting window size to %dx%d\n",
- self->area.width, self->area.height);
- }
- }
+ ob_debug("setting window size to %dx%d\n", place.width, place.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;
+
+ 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");
}
/* If something is focused, and it's not our relative... */
- else if (focus_client && client_search_focus_tree_full(self) == NULL)
+ 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 {
/* 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);
+ {
+ gulong ignore_start;
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
+ client_show(self);
+
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+ }
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);
{
guint j;
GSList *it;
+ gulong ignore_start;
ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
self->window, self->frame->window,
don't generate more events */
XSelectInput(ob_display, self->window, NoEventMask);
+ /* ignore enter events from the unmap so it doesnt mess with the focus */
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
frame_hide(self->frame);
/* flush to send the hide to the server quickly */
XFlush(ob_display);
- /* ignore enter events from the unmap so it doesnt mess with the
- focus */
- event_ignore_all_queued_enters();
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
mouse_grab_for_client(self, FALSE);
/* remove the window from our save set */
XChangeSaveSet(ob_display, self->window, SetModeDelete);
- /* kill the property windows */
- propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
-
/* update the focus lists */
focus_order_remove(self);
if (client_focused(self)) {
client_call_notifies(self, client_destroy_notifies);
/* tell our parent(s) that we're gone */
- if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
- for (it = self->group->members; it; it = g_slist_next(it))
- if (it->data != self)
- ((ObClient*)it->data)->transients =
- g_slist_remove(((ObClient*)it->data)->transients,self);
- } else if (self->transient_for) { /* transient of window */
- self->transient_for->transients =
- g_slist_remove(self->transient_for->transients, self);
- }
+ for (it = self->parents; it; it = g_slist_next(it))
+ ((ObClient*)it->data)->transients =
+ g_slist_remove(((ObClient*)it->data)->transients,self);
/* tell our transients that we're gone */
for (it = self->transients; it; it = g_slist_next(it)) {
- if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
- ((ObClient*)it->data)->transient_for = NULL;
- client_calc_layer(it->data);
- }
+ ((ObClient*)it->data)->parents =
+ g_slist_remove(((ObClient*)it->data)->parents, self);
+ /* we could be keeping our children in a higher layer */
+ client_calc_layer(it->data);
}
/* remove from its group */
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);
- all_a = screen_area(self->desktop);
- mon_a = screen_area_monitor(self->desktop, screen_find_monitor(&desired));
+ frame_rect_to_frame(self->frame, &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 (!screen_physical_area_monitor_contains(i, &desired)) {
+ if (i < screen_num_monitors - 1)
+ continue;
- 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);
+ /* 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);
+
+ 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, w, h);
+ frame_frame_gravity(self->frame, x, y);
return ox != *x || oy != *y;
}
client_update_wmhints(self);
/* this may have already been called from client_update_wmhints */
- if (self->transient_for == NULL)
+ if (!self->parents && !self->transient_for_group)
client_update_transient_for(self);
client_get_startup_id(self);
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 {
- gboolean trdesk = FALSE;
+ GSList *it;
+ gboolean first = TRUE;
+ guint all = screen_num_desktops; /* not a valid value */
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- if (self->transient_for->desktop != DESKTOP_ALL) {
- self->desktop = self->transient_for->desktop;
- trdesk = TRUE;
- }
- } else {
- /* if all the group is on one desktop, then open it on the
- same desktop */
- GSList *it;
- gboolean first = TRUE;
- guint all = screen_num_desktops; /* not a valid value */
-
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
- if (c != self) {
- if (first) {
- all = c->desktop;
- first = FALSE;
- }
- else if (all != c->desktop)
- all = screen_num_desktops; /* make it invalid */
- }
- }
- if (all != screen_num_desktops) {
- self->desktop = all;
- trdesk = TRUE;
- }
+ /* if they are all on one desktop, then open it on the
+ same desktop */
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+
+ if (c->desktop == DESKTOP_ALL) continue;
+
+ if (first) {
+ all = c->desktop;
+ first = FALSE;
}
+ else if (all != c->desktop)
+ all = screen_num_desktops; /* make it invalid */
}
- if (!trdesk) {
- /* try get from the startup-notification protocol */
- if (sn_get_desktop(self->startup_id, &self->desktop)) {
- if (self->desktop >= screen_num_desktops &&
- self->desktop != DESKTOP_ALL)
- self->desktop = screen_num_desktops - 1;
- } else
- /* defaults to the current desktop */
- self->desktop = screen_desktop;
+ if (all != screen_num_desktops) {
+ self->desktop = all;
+
+ ob_debug("client desktop set from parents: 0x%x\n",
+ self->desktop);
+ }
+ /* try get from the startup-notification protocol */
+ else if (sn_get_desktop(self->startup_id, &self->desktop)) {
+ if (self->desktop >= screen_num_desktops &&
+ self->desktop != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ ob_debug("client desktop set from startup-notification: 0x%x\n",
+ self->desktop);
+ }
+ /* defaults to the current desktop */
+ else {
+ self->desktop = screen_desktop;
+ ob_debug("client desktop set to the current desktop: %d\n",
+ self->desktop);
}
}
}
{
guint32 *state;
guint num;
-
+
if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
gulong i;
for (i = 0; i < num; ++i) {
{
Window t = None;
ObClient *target = NULL;
+ gboolean trangroup = FALSE;
if (XGetTransientForHint(ob_display, self->window, &t)) {
if (t != self->window) { /* cant be transient to itself! */
a dockapp, for example */
target = NULL;
}
-
- /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
-
- Setting the transient_for to Root is actually illegal, however
- applications from time have done this to specify transient for
- their group.
-
- Now you can do that by being a TYPE_DIALOG and not setting
- the transient_for hint at all on your window. But people still
- use Root, and Kwin is very strange in this regard.
-
- KWin 3.0 will not consider windows with transient_for set to
- Root as transient for their group *UNLESS* they are also modal.
- In that case, it will make them transient for the group. This
- leads to all sorts of weird behavior from KDE apps which are
- only tested in KWin. I'd like to follow their behavior just to
- make this work right with KDE stuff, but that seems wrong.
- */
- if (!target && self->group) {
- /* not transient to a client, see if it is transient for a
- group */
- if (t == RootWindow(ob_display, ob_screen)) {
- /* window is a transient for its group! */
- target = OB_TRAN_GROUP;
- }
- }
}
- } else if (self->transient && self->group)
- target = OB_TRAN_GROUP;
+
+ /* Setting the transient_for to Root is actually illegal, however
+ applications from time have done this to specify transient for
+ their group */
+ if (!target && self->group && t == RootWindow(ob_display, ob_screen))
+ trangroup = TRUE;
+ } else if (self->group && self->transient)
+ trangroup = TRUE;
client_update_transient_tree(self, self->group, self->group,
- self->transient_for, target);
- self->transient_for = target;
-
+ self->transient_for_group, trangroup,
+ client_direct_parent(self), target);
+ self->transient_for_group = trangroup;
+
}
static void client_update_transient_tree(ObClient *self,
ObGroup *oldgroup, ObGroup *newgroup,
+ gboolean oldgtran, gboolean newgtran,
ObClient* oldparent,
ObClient *newparent)
{
GSList *it, *next;
ObClient *c;
+ g_assert(!oldgtran || oldgroup);
+ g_assert(!newgtran || newgroup);
+ g_assert((!oldgtran && !oldparent) ||
+ (oldgtran && !oldparent) ||
+ (!oldgtran && oldparent));
+ g_assert((!newgtran && !newparent) ||
+ (newgtran && !newparent) ||
+ (!newgtran && newparent));
+
/* * *
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 (oldgroup == newgroup &&
+ oldgtran == newgtran &&
+ oldparent == newparent) return;
- /* 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.
+ /** Remove the client from the transient tree **/
- 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 &&
- newgroup != NULL && newgroup == oldgroup)
- {
- ObClient *look = newparent;
- do {
- self->transients = g_slist_remove(self->transients, look);
- look = look->transient_for;
- } while (look != NULL && look != OB_TRAN_GROUP);
- }
-
-
- /* 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 ||
- (newparent == OB_TRAN_GROUP && oldparent != newparent)) &&
- oldgroup != NULL && oldparent != OB_TRAN_GROUP)
- {
- for (it = self->transients; it; it = next) {
- next = g_slist_next(it);
- c = it->data;
- if (c->group == oldgroup)
- self->transients = g_slist_delete_link(self->transients, it);
- }
+ 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 (oldparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
- oldparent != newparent))
- {
- for (it = oldgroup->members; it; it = g_slist_next(it)) {
- c = it->data;
- if (c != self && (!c->transient_for ||
- c->transient_for != OB_TRAN_GROUP))
- c->transients = g_slist_remove(c->transients, self);
- }
+ 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);
}
- /* 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 != NULL && oldparent != OB_TRAN_GROUP &&
- oldparent != newparent)
- oldparent->transients = g_slist_remove(oldparent->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 (newparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
- oldparent != newparent))
- {
- 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 ||
- c->transient_for != OB_TRAN_GROUP))
+ 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 != NULL && newparent != OB_TRAN_GROUP &&
- newparent != oldparent &&
+ else if (newparent &&
/* don't make ourself its child if it is already our child */
- !client_is_direct_child(self, newparent))
+ !client_is_direct_child(self, newparent) &&
+ client_normal(newparent))
+ {
newparent->transients = g_slist_prepend(newparent->transients, self);
+ 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 &&
- newparent != OB_TRAN_GROUP)
+ 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)) {
c = it->data;
- if (c != self && c->transient_for == OB_TRAN_GROUP &&
+ if (c != self && c->transient_for_group &&
/* Don't make it our child if it is already our parent */
!client_is_direct_child(c, self))
{
self->transients = g_slist_prepend(self->transients, c);
+ c->parents = g_slist_prepend(c->parents, self);
}
}
}
+
+ /** 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);
+
+ ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n "
+ "size inc (%d %d) base size (%d %d)\n",
+ self->min_size.width, self->min_size.height,
+ self->max_size.width, self->max_size.height,
+ self->size_inc.width, self->size_inc.height,
+ self->base_size.width, self->base_size.height);
}
+ else
+ ob_debug("Normal hints: not set\n");
}
void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
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;
/* finally, the user can have requested no decorations, which overrides
everything (but doesnt give it a border if it doesnt have one) */
- if (self->undecorated) {
- if (config_theme_keepborder)
- self->decorations &= OB_FRAME_DECOR_BORDER;
- else
- self->decorations = 0;
- }
+ if (self->undecorated)
+ self->decorations = 0;
/* if we don't have a titlebar, then we cannot shade! */
if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
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;
/* Put ourselves into the new group's transient tree, and remove
ourselves from the old group's */
client_update_transient_tree(self, oldgroup, self->group,
- self->transient_for,
- self->transient_for);
+ self->transient_for_group,
+ self->transient_for_group,
+ client_direct_parent(self),
+ client_direct_parent(self));
/* Lastly, being in a group, or not, can change if the window is
transient for anything.
transient for something, even if transient_for was NULL because
it wasn't in a group before.
- If transient_for was NULL and oldgroup was NULL we can assume
+ If parents was NULL and oldgroup was NULL we can assume
that when we add the new group, it will become transient for
something.
- If transient_for was OB_TRAN_GROUP, then it must have already
+ If transient_for_group is TRUE, then it must have already
had a group. If it is getting a new group, the above call to
client_update_transient_tree has already taken care of
everything ! If it is losing all group status then it will
updated.
*/
if (self->transient &&
- ((self->transient_for == NULL && oldgroup == NULL) ||
- (self->transient_for == OB_TRAN_GROUP && !self->group)))
+ ((self->parents == NULL && oldgroup == NULL) ||
+ (self->transient_for_group && !self->group)))
client_update_transient_for(self);
}
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) {
if ((hints = XGetWMHints(ob_display, self->window))) {
if (hints->flags & IconPixmapHint) {
- self->nicons++;
+ self->nicons = 1;
self->icons = g_new(ObClientIcon, self->nicons);
xerror_set_ignore(TRUE);
if (!RrPixmapToRGBA(ob_rr_inst,
hints->icon_pixmap,
(hints->flags & IconMaskHint ?
hints->icon_mask : None),
- &self->icons[self->nicons-1].width,
- &self->icons[self->nicons-1].height,
- &self->icons[self->nicons-1].data)){
- g_free(&self->icons[self->nicons-1]);
- self->nicons--;
+ &self->icons[0].width,
+ &self->icons[0].height,
+ &self->icons[0].data))
+ {
+ g_free(self->icons);
+ self->nicons = 0;
}
xerror_set_ignore(FALSE);
}
/* 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 void client_change_state(ObClient *self)
{
- gulong netstate[11];
+ gulong netstate[12];
guint num;
num = 0;
ObClient *client_search_focus_tree_full(ObClient *self)
{
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- return client_search_focus_tree_full(self->transient_for);
- } else {
- GSList *it;
-
- for (it = self->group->members; it; it = g_slist_next(it)) {
- if (it->data != self) {
- ObClient *c = it->data;
-
- if (client_focused(c)) return c;
- if ((c = client_search_focus_tree(it->data))) return c;
- }
- }
+ if (self->parents) {
+ GSList *it;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if ((c = client_search_focus_tree_full(it->data))) return c;
}
- }
- /* this function checks the whole tree, the client_search_focus_tree
- does not, so we need to check this window */
- if (client_focused(self))
- return self;
- return client_search_focus_tree(self);
+ return NULL;
+ }
+ else {
+ /* this function checks the whole tree, the client_search_focus_tree
+ does not, so we need to check this window */
+ if (client_focused(self))
+ return self;
+ return client_search_focus_tree(self);
+ }
}
-gboolean client_has_parent(ObClient *self)
+ObClient *client_search_focus_group_full(ObClient *self)
{
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- if (client_normal(self->transient_for))
- return TRUE;
- }
- else if (self->group) {
- GSList *it;
+ GSList *it;
- for (it = self->group->members; it; it = g_slist_next(it)) {
- if (it->data != self && client_normal(it->data))
- return TRUE;
- }
+ if (self->group) {
+ for (it = self->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+
+ if (client_focused(c)) return c;
+ if ((c = client_search_focus_tree(it->data))) return c;
}
- }
- return FALSE;
+ } else
+ if (client_focused(self)) return self;
+ return NULL;
+}
+
+gboolean client_has_parent(ObClient *self)
+{
+ return self->parents != NULL;
}
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;
}
event_cancel_all_key_grabs();
}
+ /* We don't need to ignore enter events here.
+ The window can hide/iconify in 3 different ways:
+ 1 - through an x message. in this case we ignore all enter events
+ caused by responding to the x message (unless underMouse)
+ 2 - by a keyboard action. in this case we ignore all enter events
+ caused by the action
+ 3 - by a mouse action. in this case they are doing stuff with the
+ mouse and focus _should_ move.
+
+ Also in action_end, we simulate an enter event that can't be ignored
+ so trying to ignore them is futile in case 3 anyways
+ */
+
frame_hide(self->frame);
hide = TRUE;
*/
client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
ob_debug("placed window 0x%x at %d, %d with size %d x %d\n",
- self->window, self->area.x, self->area.y,
- self->area.width, self->area.height);
- oldarea = self->area; /* save the area */
- RECT_SET(self->area, x, y, w, h); /* put where it should be for the premax stuff */
+ self->window, x, y, w, h);
+ /* save the area, and make it where it should be for the premax stuff */
+ oldarea = self->area;
+ RECT_SET(self->area, x, y, w, h);
/* apply the states. these are in a carefully crafted order.. */
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);
gint *logicalw, gint *logicalh,
gboolean user)
{
- Rect desired_area = {*x, *y, *w, *h};
+ Rect desired = {*x, *y, *w, *h};
+ frame_rect_to_frame(self->frame, &desired);
/* make the frame recalculate its dimentions n shit without changing
anything visible for real, this way the constraints below can work with
the updated frame dimensions. */
frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+ /* gets the frame's position */
+ frame_client_gravity(self->frame, x, y);
+
+ /* these positions are frame positions, not client positions */
+
+ /* set the size and position if fullscreen */
+ if (self->fullscreen) {
+ Rect *a;
+ guint i;
+
+ i = screen_find_monitor(&desired);
+ a = screen_physical_area_monitor(i);
+
+ *x = a->x;
+ *y = a->y;
+ *w = a->width;
+ *h = a->height;
+
+ 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(self->desktop, i,
+ (self->max_horz && self->max_vert ? NULL : &desired));
+
+ /* set the size and position if maximized */
+ if (self->max_horz) {
+ *x = a->x;
+ *w = a->width - self->frame->size.left - self->frame->size.right;
+ }
+ if (self->max_vert) {
+ *y = a->y;
+ *h = a->height - self->frame->size.top - self->frame->size.bottom;
+ }
+
+ user = FALSE; /* ignore if the client can't be moved/resized when it
+ is maximizing */
+
+ g_free(a);
+ }
+
+ /* gets the client's position */
+ 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)) {
gint basew, baseh, minw, minh;
+ gint incw, inch;
+ gfloat minratio, maxratio;
+
+ incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
+ inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
+ minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+ 0 : self->min_ratio;
+ maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
+ 0 : self->max_ratio;
/* base size is substituted with min size if not specified */
if (self->base_size.width || self->base_size.height) {
*h -= baseh;
/* keep to the increments */
- *w /= self->size_inc.width;
- *h /= self->size_inc.height;
+ *w /= incw;
+ *h /= inch;
/* you cannot resize to nothing */
if (basew + *w < 1) *w = 1 - basew;
if (baseh + *h < 1) *h = 1 - baseh;
-
+
/* save the logical size */
- *logicalw = self->size_inc.width > 1 ? *w : *w + basew;
- *logicalh = self->size_inc.height > 1 ? *h : *h + baseh;
+ *logicalw = incw > 1 ? *w : *w + basew;
+ *logicalh = inch > 1 ? *h : *h + baseh;
- *w *= self->size_inc.width;
- *h *= self->size_inc.height;
+ *w *= incw;
+ *h *= inch;
*w += basew;
*h += baseh;
*w -= self->base_size.width;
*h -= self->base_size.height;
- if (!self->fullscreen) {
- if (self->min_ratio)
- if (*h * self->min_ratio > *w) {
- *h = (gint)(*w / self->min_ratio);
+ if (minratio)
+ if (*h * minratio > *w) {
+ *h = (gint)(*w / minratio);
- /* you cannot resize to nothing */
- if (*h < 1) {
- *h = 1;
- *w = (gint)(*h * self->min_ratio);
- }
+ /* you cannot resize to nothing */
+ if (*h < 1) {
+ *h = 1;
+ *w = (gint)(*h * minratio);
}
- if (self->max_ratio)
- if (*h * self->max_ratio < *w) {
- *h = (gint)(*w / self->max_ratio);
-
- /* you cannot resize to nothing */
- if (*h < 1) {
- *h = 1;
- *w = (gint)(*h * self->min_ratio);
- }
+ }
+ if (maxratio)
+ if (*h * maxratio < *w) {
+ *h = (gint)(*w / maxratio);
+
+ /* you cannot resize to nothing */
+ if (*h < 1) {
+ *h = 1;
+ *w = (gint)(*h * minratio);
}
- }
+ }
*w += self->base_size.width;
*h += self->base_size.height;
}
- /* gets the frame's position */
- frame_client_gravity(self->frame, x, y, *w, *h);
-
- /* these positions are frame positions, not client positions */
-
- /* set the size and position if fullscreen */
- if (self->fullscreen) {
- Rect *a;
- guint i;
-
- i = screen_find_monitor(&desired_area);
- a = screen_physical_area_monitor(i);
-
- *x = a->x;
- *y = a->y;
- *w = a->width;
- *h = a->height;
-
- user = FALSE; /* ignore if the client can't be moved/resized when it
- is fullscreening */
- } else if (self->max_horz || self->max_vert) {
- Rect *a;
- guint i;
-
- i = screen_find_monitor(&desired_area);
- a = screen_area_monitor(self->desktop, i);
-
- /* set the size and position if maximized */
- if (self->max_horz) {
- *x = a->x;
- *w = a->width - self->frame->size.left - self->frame->size.right;
- }
- if (self->max_vert) {
- *y = a->y;
- *h = a->height - self->frame->size.top - self->frame->size.bottom;
- }
-
- user = FALSE; /* ignore if the client can't be moved/resized when it
- is maximizing */
- }
-
- /* gets the client's position */
- frame_frame_gravity(self->frame, x, y, *w, *h);
-
/* these override the above states! if you cant move you can't move! */
if (user) {
if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
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);
{
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);
+ self = client_search_top_direct_parent(self);
client_iconify_recursive(self, iconic, curdesk, hide_animation);
}
}
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));
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)
+ gboolean donthide, gboolean dontraise)
{
- self = client_search_top_normal_parent(self);
- client_set_desktop_recursive(self, target, donthide);
+ self = client_search_top_direct_parent(self);
+ client_set_desktop_recursive(self, target, donthide, dontraise);
}
gboolean client_is_direct_child(ObClient *parent, ObClient *child)
{
- while (child != parent &&
- child->transient_for && child->transient_for != OB_TRAN_GROUP)
- child = child->transient_for;
+ while (child != parent && (child = client_direct_parent(child)));
return child == parent;
}
{
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;
self = client_focus_target(self);
if (!client_can_focus(self)) {
- if (!self->frame->visible) {
- /* update the focus lists */
- focus_order_to_top(self);
- }
ob_debug_type(OB_DEBUG_FOCUS,
"Client %s can't be focused\n", self->title);
return FALSE;
"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 request came from the user, or if nothing is focused, then grant
- the request.
- if the currently focused app doesn't set a user_time, then it can't
- benefit from any focus stealing prevention.
- */
- if (user || !focus_client || !last_time)
- 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_curtime && 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 (!self->nicons) {
ObClientIcon *parent = NULL;
+ GSList *it;
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP)
- parent = client_icon_recursive(self->transient_for, w, h);
- else {
- GSList *it;
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
- if (c != self && !c->transient_for) {
- if ((parent = client_icon_recursive(c, w, h)))
- break;
- }
- }
- }
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ 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))
{
return screen_find_monitor(&self->frame->area);
}
-ObClient *client_search_top_normal_parent(ObClient *self)
+ObClient *client_direct_parent(ObClient *self)
{
- while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
- client_normal(self->transient_for))
- self = self->transient_for;
+ if (!self->parents) return NULL;
+ if (self->transient_for_group) return NULL;
+ return self->parents->data;
+}
+
+ObClient *client_search_top_direct_parent(ObClient *self)
+{
+ ObClient *p;
+ while ((p = client_direct_parent(self))) self = p;
return self;
}
gboolean bylayer,
ObStackingLayer layer)
{
- GSList *ret = NULL;
-
- /* move up the direct transient chain as far as possible */
- while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
- (!bylayer || self->transient_for->layer == layer) &&
- client_normal(self->transient_for))
- self = self->transient_for;
-
- if (!self->transient_for)
- ret = g_slist_prepend(ret, self);
- else {
- GSList *it;
-
- g_assert(self->group);
+ GSList *ret;
+ ObClient *p;
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
-
- if (!c->transient_for && client_normal(c) &&
- (!bylayer || c->layer == layer))
- {
- ret = g_slist_prepend(ret, c);
- }
- }
+ /* move up the direct transient chain as far as possible */
+ while ((p = client_direct_parent(self)) &&
+ (!bylayer || p->layer == layer))
+ self = p;
- if (ret == NULL) /* no group parents */
- ret = g_slist_prepend(ret, self);
- }
+ if (!self->parents)
+ ret = g_slist_prepend(NULL, self);
+ else
+ ret = g_slist_copy(self->parents);
return ret;
}
ObClient *client_search_focus_parent(ObClient *self)
{
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- if (client_focused(self->transient_for))
- return self->transient_for;
- } else {
- GSList *it;
-
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
+ GSList *it;
- /* checking transient_for prevents infinate loops! */
- if (c != self && !c->transient_for)
- if (client_focused(c))
- return c;
- }
- }
- }
+ for (it = self->parents; it; it = g_slist_next(it))
+ if (client_focused(it->data)) return it->data;
return NULL;
}
ObClient *client_search_parent(ObClient *self, ObClient *search)
{
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- if (self->transient_for == search)
- return search;
- } else {
- GSList *it;
-
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
+ GSList *it;
- /* checking transient_for prevents infinate loops! */
- if (c != self && !c->transient_for)
- if (c == search)
- return search;
- }
- }
- }
+ for (it = self->parents; it; it = g_slist_next(it))
+ if (it->data == search) return search;
return NULL;
}
return NULL;
}
-#define WANT_EDGE(cur, c) \
- if(cur == c) \
- continue; \
- if(!client_normal(cur)) \
- continue; \
- if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
- 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);
- monitor = screen_area_monitor(c->desktop, client_monitor(c));
+ 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;
+ 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;
+ default:
+ g_assert_not_reached();
+ }
- HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
- }
+ 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;
- 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 */
}
- return dest;
+ 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;
if (WINDOW_IS_CLIENT(it->data)) {
ObClient *c = WINDOW_AS_CLIENT(it->data);
if (c->frame->visible &&
+ /* check the desktop, this is done during desktop
+ switching and windows are shown/hidden status is not
+ reliable */
+ (c->desktop == screen_desktop ||
+ c->desktop == DESKTOP_ALL) &&
/* ignore all animating windows */
!frame_iconify_animating(c->frame) &&
RECT_CONTAINS(c->frame->area, x, y))