+#include "debug.h"
#include "client.h"
#include "dock.h"
#include "xerror.h"
#include "prop.h"
#include "extensions.h"
#include "frame.h"
+#include "session.h"
#include "event.h"
#include "grab.h"
#include "focus.h"
#include "openbox.h"
#include "group.h"
#include "config.h"
-#include "menu.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "mouse.h"
#include "render/render.h"
#include <glib.h>
static void client_change_allowed_actions(ObClient *self);
static void client_change_state(ObClient *self);
static void client_apply_startup_state(ObClient *self);
+static void client_restore_session_state(ObClient *self);
+static void client_restore_session_stacking(ObClient *self);
void client_startup()
{
XFree(wmhint);
}
- g_message("Managing window: %lx", window);
+ ob_debug("Managing window: %lx\n", window);
/* choose the events we want to receive on the CLIENT window */
attrib_set.event_mask = CLIENT_EVENTMASK;
/* create the ObClient struct, and populate it from the hints on the
window */
- self = g_new(ObClient, 1);
+ self = g_new0(ObClient, 1);
self->obwin.type = Window_Client;
self->window = window;
+
client_get_all(self);
+ client_restore_session_state(self);
+
+ client_change_state(self);
/* remove the client's border (and adjust re gravity) */
client_toggle_border(self, FALSE);
grab_server(FALSE);
- /* add to client list/map */
- client_list = g_list_append(client_list, self);
- g_hash_table_insert(window_map, &self->window, self);
-
/* update the focus lists */
focus_order_add_new(self);
+ stacking_add(CLIENT_AS_WINDOW(self));
+ client_restore_session_stacking(self);
+
/* focus the new window? */
if (ob_state() != OB_STATE_STARTING && config_focus_new &&
- (self->type == OB_CLIENT_TYPE_NORMAL || self->type == OB_CLIENT_TYPE_DIALOG)) {
- gboolean group_foc = FALSE;
-
- if (self->group) {
- GSList *it;
-
- for (it = self->group->members; it; it = it->next) {
- if (client_focused(it->data)) {
- group_foc = TRUE;
- break;
- }
- }
- }
/* note the check against Type_Normal/Dialog, not client_normal(self),
which would also include other types. in this case we want more
strict rules for focus */
- if ((group_foc ||
- (!self->transient_for && (!self->group ||
- !self->group->members->next))) ||
- client_search_focus_tree_full(self) ||
- !focus_client ||
- !client_normal(focus_client)) {
+ (self->type == OB_CLIENT_TYPE_NORMAL ||
+ self->type == OB_CLIENT_TYPE_DIALOG))
+ {
+ activate = TRUE;
+#if 0
+ if (self->desktop != screen_desktop) {
/* activate the window */
- stacking_add(CLIENT_AS_WINDOW(self));
activate = TRUE;
} else {
- /* try to not get in the way */
- stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+ gboolean group_foc = FALSE;
+
+ if (self->group) {
+ GSList *it;
+
+ for (it = self->group->members; it; it = it->next)
+ {
+ if (client_focused(it->data))
+ {
+ group_foc = TRUE;
+ break;
+ }
+ }
+ }
+ if ((group_foc ||
+ (!self->transient_for && (!self->group ||
+ !self->group->members->next))) ||
+ client_search_focus_tree_full(self) ||
+ !focus_client ||
+ !client_normal(focus_client))
+ {
+ /* activate the window */
+ activate = TRUE;
+ }
}
- } else {
- stacking_add(CLIENT_AS_WINDOW(self));
+#endif
}
dispatch_client(Event_Client_New, self, 0, 0);
/* make sure the window is visible */
- client_move_onscreen(self);
-
- screen_update_areas();
+ if (ob_state() == OB_STATE_RUNNING)
+ client_move_onscreen(self, client_normal(self));
client_showhide(self);
- if (activate) client_activate(self);
+ /* use client_focus instead of client_activate cuz client_activate does
+ stuff like switch desktops etc and I'm not interested in all that when
+ a window maps since its not based on an action from the user like
+ clicking a window to activate is. so keep the new window out of the way
+ but do focus it. */
+ if (activate) client_focus(self);
+
+ /* client_activate does this but we aret using it so we have to do it
+ here as well */
+ if (screen_showing_desktop)
+ screen_show_desktop(FALSE);
+
+ /* add to client list/map */
+ client_list = g_list_append(client_list, self);
+ g_hash_table_insert(window_map, &self->window, self);
+
+ /* this has to happen after we're in the client_list */
+ screen_update_areas();
/* update the list hints */
client_set_list();
+ keyboard_grab_for_client(self, TRUE);
+ mouse_grab_for_client(self, TRUE);
+
dispatch_client(Event_Client_Mapped, self, 0, 0);
- g_message("Managed window 0x%lx (%s)", window, self->class);
+ ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
}
void client_unmanage_all()
client_unmanage(client_list->data);
}
-/* called by client_unmanage() to close any menus referencing this client */
-void client_close_menus(gpointer key, gpointer value, gpointer self)
-{
- if (((Menu *)value)->client == (ObClient *)self)
- menu_hide((Menu *)value);
-}
-
void client_unmanage(ObClient *self)
{
guint j;
GSList *it;
- g_message("Unmanaging window: %lx (%s)", self->window, self->class);
+ ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
dispatch_client(Event_Client_Destroy, self, 0, 0);
g_assert(self != NULL);
+ keyboard_grab_for_client(self, FALSE);
+ mouse_grab_for_client(self, FALSE);
+
/* remove the window from our save set */
XChangeSaveSet(ob_display, self->window, SetModeDelete);
if (moveresize_client == self)
moveresize_end(TRUE);
- /* close any windows that are attached to this window */
- g_hash_table_foreach(menu_hash, client_close_menus, self);
-
+ /* menus can be associated with a client, so close any that are since
+ we are disappearing now */
+ menu_frame_hide_all_client(self);
if (focus_client == self) {
XEvent e;
if (ob_state() != OB_STATE_EXITING) {
/* these values should not be persisted across a window
unmapping/mapping */
- prop_erase(self->window, prop_atoms.net_wm_desktop);
- prop_erase(self->window, prop_atoms.net_wm_state);
+ PROP_ERASE(self->window, net_wm_desktop);
+ PROP_ERASE(self->window, net_wm_state);
+ PROP_ERASE(self->window, wm_state);
} else {
/* if we're left in an iconic state, the client wont be mapped. this is
bad, since we will no longer be managing the window on restart */
}
- g_message("Unmanaged window 0x%lx", self->window);
+ ob_debug("Unmanaged window 0x%lx\n", self->window);
/* free all data allocated in the client struct */
g_slist_free(self->transients);
client_set_list();
}
-void client_move_onscreen(ObClient *self)
+static void client_restore_session_state(ObClient *self)
+{
+ GList *it;
+
+ if (!(it = session_state_find(self)))
+ return;
+
+ self->session = it->data;
+
+ RECT_SET(self->area, self->session->x, self->session->y,
+ self->session->w, self->session->h);
+ self->positioned = TRUE;
+ XResizeWindow(ob_display, self->window,
+ self->session->w, self->session->h);
+
+ self->desktop = (self->session->desktop == DESKTOP_ALL ?
+ self->session->desktop :
+ MIN(screen_num_desktops - 1, self->session->desktop));
+ PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+
+ self->shaded = self->session->shaded;
+ self->iconic = self->session->iconic;
+ self->skip_pager = self->session->skip_pager;
+ self->skip_taskbar = self->session->skip_taskbar;
+ self->fullscreen = self->session->fullscreen;
+ self->above = self->session->above;
+ self->below = self->session->below;
+ self->max_horz = self->session->max_horz;
+ self->max_vert = self->session->max_vert;
+}
+
+static void client_restore_session_stacking(ObClient *self)
+{
+ GList *it;
+
+ if (!self->session) return;
+
+ it = g_list_find(session_saved_state, self->session);
+ for (it = g_list_previous(it); it; it = g_list_previous(it)) {
+ GList *cit;
+
+ for (cit = client_list; cit; cit = g_list_next(cit))
+ if (session_state_cmp(it->data, cit->data))
+ break;
+ if (cit) {
+ client_calc_layer(self);
+ stacking_below(CLIENT_AS_WINDOW(self),
+ CLIENT_AS_WINDOW(cit->data));
+ break;
+ }
+ }
+}
+
+void client_move_onscreen(ObClient *self, gboolean rude)
+{
+ int x = self->area.x;
+ int y = self->area.y;
+ if (client_find_onscreen(self, &x, &y,
+ self->frame->area.width,
+ self->frame->area.height, rude)) {
+ client_configure(self, OB_CORNER_TOPLEFT, x, y,
+ self->area.width, self->area.height,
+ TRUE, TRUE);
+ }
+}
+
+gboolean client_find_onscreen(ObClient *self, int *x, int *y, int w, int h,
+ gboolean rude)
{
Rect *a;
- int x = self->frame->area.x, y = self->frame->area.y;
+ int ox = *x, oy = *y;
+
+ frame_client_gravity(self->frame, x, y); /* get where the frame
+ would be */
/* XXX watch for xinerama dead areas */
+
a = screen_area(self->desktop);
- if (x >= a->x + a->width - 1)
- x = a->x + a->width - self->frame->area.width;
- if (y >= a->y + a->height - 1)
- y = a->y + a->height - self->frame->area.height;
- if (x + self->frame->area.width - 1 < a->x)
- x = a->x;
- if (y + self->frame->area.height - 1< a->y)
- y = a->y;
-
- frame_frame_gravity(self->frame, &x, &y); /* get where the client
- should be */
- client_configure(self, OB_CORNER_TOPLEFT, x, y,
- self->area.width, self->area.height,
- TRUE, TRUE);
+ if (!self->strut.right && *x >= a->x + a->width - 1)
+ *x = a->x + a->width - self->frame->area.width;
+ if (!self->strut.bottom && *y >= a->y + a->height - 1)
+ *y = a->y + a->height - self->frame->area.height;
+ if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
+ *x = a->x;
+ if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
+ *y = a->y;
+
+ if (rude) {
+ /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
+ Java can suck it too. */
+
+ /* dont let windows map/move into the strut unless they
+ are bigger than the available area */
+ if (w <= a->width) {
+ if (!self->strut.left && *x < a->x) *x = a->x;
+ if (!self->strut.right && *x + w > a->x + a->width)
+ *x = a->x + a->width - w;
+ }
+ if (h <= a->height) {
+ if (!self->strut.top && *y < a->y) *y = a->y;
+ if (!self->strut.bottom && *y + h > a->y + a->height)
+ *y = a->y + a->height - h;
+ }
+ }
+
+ frame_frame_gravity(self->frame, x, y); /* get where the client
+ should be */
+
+ return ox != *x || oy != *y;
}
static void client_toggle_border(ObClient *self, gboolean show)
static void client_get_all(ObClient *self)
{
- /* update EVERYTHING!! */
-
- self->ignore_unmaps = 0;
-
- /* defaults */
- self->frame = NULL;
- self->title = self->icon_title = NULL;
+ /* non-zero defaults */
self->title_count = 1;
- self->name = self->class = self->role = NULL;
self->wmstate = NormalState;
- self->transient = FALSE;
- self->transients = NULL;
- self->transient_for = NULL;
self->layer = -1;
- self->urgent = FALSE;
- self->positioned = FALSE;
- self->disabled_decorations = 0;
- self->group = NULL;
- self->nicons = 0;
+ self->decorate = TRUE;
client_get_area(self);
client_update_transient_for(self);
client_update_class(self);
client_update_strut(self);
client_update_icons(self);
-
- client_change_state(self);
}
static void client_get_area(ObClient *self)
if (!trdesk)
/* defaults to the current desktop */
self->desktop = screen_desktop;
-
}
if (self->desktop != d) {
/* set the desktop hint, to make sure that it always exists */
void client_update_transient_for(ObClient *self)
{
Window t = None;
- ObClient *c = NULL;
+ ObClient *target = NULL;
if (XGetTransientForHint(ob_display, self->window, &t)) {
self->transient = TRUE;
if (t != self->window) { /* cant be transient to itself! */
- c = g_hash_table_lookup(window_map, &t);
+ target = g_hash_table_lookup(window_map, &t);
/* if this happens then we need to check for it*/
- g_assert(c != self);
- g_assert(!c || WINDOW_IS_CLIENT(c));
+ g_assert(target != self);
+ g_assert(!target || WINDOW_IS_CLIENT(target));
- if (!c && self->group) {
+ if (!target && self->group) {
/* not transient to a client, see if it is transient for a
group */
if (t == self->group->leader ||
t == None ||
t == RootWindow(ob_display, ob_screen)) {
/* window is a transient for its group! */
- c = OB_TRAN_GROUP;
+ target = OB_TRAN_GROUP;
}
}
}
self->transient = FALSE;
/* if anything has changed... */
- if (c != self->transient_for) {
+ if (target != self->transient_for) {
if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
GSList *it;
/* remove from old parents */
- for (it = self->group->members; it; it = it->next)
- if (it->data != self &&
- !((ObClient*)it->data)->transient_for)
- ((ObClient*)it->data)->transients =
- g_slist_remove(((ObClient*)it->data)->transients, self);
+ for (it = self->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if (c != self && !c->transient_for)
+ c->transients = g_slist_remove(c->transients, self);
+ }
} else if (self->transient_for != NULL) { /* transient of window */
/* remove from old parent */
self->transient_for->transients =
g_slist_remove(self->transient_for->transients, self);
}
- self->transient_for = c;
+ self->transient_for = target;
if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
GSList *it;
/* add to new parents */
- for (it = self->group->members; it; it = it->next)
- if (it->data != self &&
- !((ObClient*)it->data)->transient_for)
- ((ObClient*)it->data)->transients =
- g_slist_append(((ObClient*)it->data)->transients, self);
+ for (it = self->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if (c != self && !c->transient_for)
+ c->transients = g_slist_append(c->transients, self);
+ }
/* remove all transients which are in the group, that causes
circlular pointer hell of doom */
- for (it = self->group->members; it; it = it->next) {
+ for (it = self->group->members; it; it = g_slist_next(it)) {
GSList *sit, *next;
for (sit = self->transients; sit; sit = next) {
- next = sit->next;
+ next = g_slist_next(sit);
if (sit->data == it->data)
- self->transients = g_slist_remove(self->transients,
- sit->data);
+ self->transients =
+ g_slist_delete_link(self->transients, sit);
}
}
} else if (self->transient_for != NULL) { /* transient of window */
/* get the hints from the window */
if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
- self->positioned = !!(size.flags & (PPosition|USPosition));
+ self->positioned = !!(size.flags & (PPosition|USPosition));
if (size.flags & PWinGravity) {
self->gravity = size.win_gravity;
void client_setup_decor_and_functions(ObClient *self)
{
/* start with everything (cept fullscreen) */
- self->decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
- Decor_Icon | Decor_AllDesktops | Decor_Iconify | Decor_Maximize |
- Decor_Shade;
- self->functions = OB_CLIENT_FUNC_RESIZE | OB_CLIENT_FUNC_MOVE |
- OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_MAXIMIZE |
- OB_CLIENT_FUNC_SHADE;
+ self->decorations = (OB_FRAME_DECOR_TITLEBAR |
+ OB_FRAME_DECOR_HANDLE |
+ OB_FRAME_DECOR_GRIPS |
+ OB_FRAME_DECOR_BORDER |
+ OB_FRAME_DECOR_ICON |
+ OB_FRAME_DECOR_ALLDESKTOPS |
+ OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE |
+ OB_FRAME_DECOR_SHADE);
+ self->functions = (OB_CLIENT_FUNC_RESIZE |
+ OB_CLIENT_FUNC_MOVE |
+ OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE |
+ OB_CLIENT_FUNC_SHADE);
if (self->delete_window) {
- self->decorations |= Decor_Close;
self->functions |= OB_CLIENT_FUNC_CLOSE;
+ self->decorations |= OB_FRAME_DECOR_CLOSE;
}
if (!(self->min_size.width < self->max_size.width ||
- self->min_size.height < self->max_size.height)) {
- self->decorations &= ~(Decor_Maximize | Decor_Handle);
+ self->min_size.height < self->max_size.height))
self->functions &= ~OB_CLIENT_FUNC_RESIZE;
- }
switch (self->type) {
case OB_CLIENT_TYPE_NORMAL:
case OB_CLIENT_TYPE_DIALOG:
case OB_CLIENT_TYPE_UTILITY:
/* these windows cannot be maximized */
- self->decorations &= ~Decor_Maximize;
self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
break;
case OB_CLIENT_TYPE_MENU:
case OB_CLIENT_TYPE_TOOLBAR:
/* these windows get less functionality */
- self->decorations &= ~(Decor_Iconify | Decor_Handle);
self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
break;
decor and functionality */
if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
- if (! (self->mwmhints.decorations & OB_MWM_DECOR_BORDER))
- self->decorations &= ~Decor_Border;
- if (! (self->mwmhints.decorations & OB_MWM_DECOR_HANDLE))
- self->decorations &= ~Decor_Handle;
- if (! (self->mwmhints.decorations & OB_MWM_DECOR_TITLE))
- self->decorations &= ~Decor_Titlebar;
- if (! (self->mwmhints.decorations & OB_MWM_DECOR_ICONIFY))
- self->decorations &= ~Decor_Iconify;
- if (! (self->mwmhints.decorations & OB_MWM_DECOR_MAXIMIZE))
- self->decorations &= ~Decor_Maximize;
+ if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
+ (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
+ /* if the mwm hints request no handle or title, then all
+ decorations are disabled */
+ self->decorations = 0;
}
}
self->functions &= ~OB_CLIENT_FUNC_RESIZE;
if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
self->functions &= ~OB_CLIENT_FUNC_MOVE;
+ /* dont let mwm hints kill any buttons
if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+ */
/* dont let mwm hints kill the close button
if (! (self->mwmhints.functions & MwmFunc_Close))
- self->functions &= ~Func_Close; */
+ self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
}
}
+ if (!(self->functions & OB_CLIENT_FUNC_SHADE))
+ self->decorations &= ~OB_FRAME_DECOR_SHADE;
+ if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
+ self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
+ if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
+ self->decorations &= ~OB_FRAME_DECOR_GRIPS;
+
/* can't maximize without moving/resizing */
- if (!((self->functions & OB_CLIENT_FUNC_MOVE) &&
- (self->functions & OB_CLIENT_FUNC_RESIZE)))
- self->functions &= ~(OB_CLIENT_FUNC_MAXIMIZE |
- OB_CLIENT_FUNC_FULLSCREEN);
-
- /* finally, user specified disabled decorations are applied to subtract
- decorations */
- if (self->disabled_decorations & Decor_Titlebar)
- self->decorations &= ~Decor_Titlebar;
- if (self->disabled_decorations & Decor_Handle)
- self->decorations &= ~Decor_Handle;
- if (self->disabled_decorations & Decor_Border)
- self->decorations &= ~Decor_Border;
- if (self->disabled_decorations & Decor_Iconify)
- self->decorations &= ~Decor_Iconify;
- if (self->disabled_decorations & Decor_Maximize)
- self->decorations &= ~Decor_Maximize;
- if (self->disabled_decorations & Decor_AllDesktops)
- self->decorations &= ~Decor_AllDesktops;
- if (self->disabled_decorations & Decor_Shade)
- self->decorations &= ~Decor_Shade;
- if (self->disabled_decorations & Decor_Close)
- self->decorations &= ~Decor_Close;
+ if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
+ (self->functions & OB_CLIENT_FUNC_MOVE) &&
+ (self->functions & OB_CLIENT_FUNC_RESIZE))) {
+ self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
+ self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
+ }
+
+ /* kill the handle on fully maxed windows */
+ if (self->max_vert && self->max_horz)
+ self->decorations &= ~OB_FRAME_DECOR_HANDLE;
+
+ /* finally, the user can have requested no decorations, which overrides
+ everything */
+ if (!self->decorate)
+ self->decorations = OB_FRAME_DECOR_BORDER;
/* if we don't have a titlebar, then we cannot shade! */
- if (!(self->decorations & Decor_Titlebar))
+ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
self->functions &= ~OB_CLIENT_FUNC_SHADE;
/* now we need to check against rules for the client's current state */
client_change_allowed_actions(self);
if (self->frame) {
- /* this makes sure that these windows appear on all desktops */
- if (self->type == OB_CLIENT_TYPE_DESKTOP &&
- self->desktop != DESKTOP_ALL)
- client_set_desktop(self, DESKTOP_ALL, FALSE);
-
- /* adjust the client's decorations, etc. */
- client_reconfigure(self);
+ /* adjust the client's decorations, etc. */
+ client_reconfigure(self);
} else {
/* this makes sure that these windows appear on all desktops */
if (self->type == OB_CLIENT_TYPE_DESKTOP &&
else self->shaded = FALSE;
}
if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
- g_message("UNSETTING ICONIC");
if (self->frame) client_iconify(self, FALSE, TRUE);
else self->iconic = FALSE;
}
if (!self->transient_for) {
/* add other transients of the group that are already
set up */
- for (it = self->group->members; it; it = it->next)
- if (it->data != self &&
- ((ObClient*)it->data)->transient_for == OB_TRAN_GROUP)
- self->transients = g_slist_append(self->transients,
- it->data);
+ for (it = self->group->members; it; it = it->next) {
+ ObClient *c = it->data;
+ if (c != self && c->transient_for == OB_TRAN_GROUP)
+ self->transients =
+ g_slist_append(self->transients, c);
+ }
}
}
- /* the WM_HINTS can contain an icon */
- client_update_icons(self);
-
/* because the self->transient flag wont change from this call,
we don't need to update the window's type and such, only its
transient_for, and the transients lists of other windows in
client_update_transient_for(self);
}
+ /* the WM_HINTS can contain an icon */
+ client_update_icons(self);
+
XFree(hints);
}
if (ur != self->urgent) {
self->urgent = ur;
- g_message("Urgent Hint for 0x%lx: %s", self->window,
- ur ? "ON" : "OFF");
+ ob_debug("Urgent Hint for 0x%lx: %s\n", self->window,
+ ur ? "ON" : "OFF");
/* fire the urgent callback if we're mapped, otherwise, wait until
after we're mapped */
if (self->frame)
{
guint num;
guint32 *data;
+ gboolean got = FALSE;
+ StrutPartial strut;
+
+ if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
+ &data, &num)) {
+ if (num == 12) {
+ got = TRUE;
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ data[4], data[5], data[8], data[9],
+ data[6], data[7], data[10], data[11]);
+ }
+ g_free(data);
+ }
- if (!PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
- STRUT_SET(self->strut, 0, 0, 0, 0);
- } else {
- if (num == 4)
- STRUT_SET(self->strut, data[0], data[2], data[1], data[3]);
- else
- STRUT_SET(self->strut, 0, 0, 0, 0);
- g_free(data);
+ if (!got &&
+ PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
+ if (num == 4) {
+ got = TRUE;
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ g_free(data);
}
- /* updating here is pointless while we're being mapped cuz we're not in
- the client list yet */
- if (self->frame)
- screen_update_areas();
+ if (!got)
+ STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (!STRUT_EQUAL(strut, self->strut)) {
+ self->strut = strut;
+
+ /* updating here is pointless while we're being mapped cuz we're not in
+ the client list yet */
+ if (self->frame)
+ screen_update_areas();
+ }
}
void client_update_icons(ObClient *self)
*/
}
-void client_configure(ObClient *self, ObCorner anchor,
- int x, int y, int w, int h,
- gboolean user, gboolean final)
+void client_configure_full(ObClient *self, ObCorner anchor,
+ int x, int y, int w, int h,
+ gboolean user, gboolean final,
+ gboolean force_reply)
{
gboolean moved = FALSE, resized = FALSE;
+ guint fdecor = self->frame->decorations;
+ gboolean fhorz = self->frame->max_horz;
+
+ /* 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, TRUE, TRUE, TRUE);
/* gets the frame's position */
frame_client_gravity(self->frame, &x, &y);
/* set the size and position if maximized */
if (self->max_horz) {
- x = a->x - self->frame->size.left;
- w = a->width;
+ x = a->x;
+ w = a->width - self->frame->size.left - self->frame->size.right;
}
if (self->max_vert) {
y = a->y;
if (!(w == self->area.width && h == self->area.height)) {
int basew, baseh, minw, minh;
+ int mw, mh, aw, ah;
/* base size is substituted with min size if not specified */
if (self->base_size.width || self->base_size.height) {
minh = self->base_size.height;
}
- if (user) {
- /* for interactive resizing. have to move half an increment in each
- direction. */
-
- /* how far we are towards the next size inc */
- int mw = (w - basew) % self->size_inc.width;
- int mh = (h - baseh) % self->size_inc.height;
- /* amount to add */
- int aw = self->size_inc.width / 2;
- int ah = self->size_inc.height / 2;
- /* don't let us move into a new size increment */
- if (mw + aw >= self->size_inc.width)
- aw = self->size_inc.width - mw - 1;
- if (mh + ah >= self->size_inc.height)
- ah = self->size_inc.height - mh - 1;
- w += aw;
- h += ah;
+ /* for interactive resizing. have to move half an increment in each
+ direction. */
+
+ /* how far we are towards the next size inc */
+ mw = (w - basew) % self->size_inc.width;
+ mh = (h - baseh) % self->size_inc.height;
+ /* amount to add */
+ aw = self->size_inc.width / 2;
+ ah = self->size_inc.height / 2;
+ /* don't let us move into a new size increment */
+ if (mw + aw >= self->size_inc.width)
+ aw = self->size_inc.width - mw - 1;
+ if (mh + ah >= self->size_inc.height)
+ ah = self->size_inc.height - mh - 1;
+ w += aw;
+ h += ah;
- /* if this is a user-requested resize, then check against min/max
- sizes */
-
- /* smaller than min size or bigger than max size? */
- if (w > self->max_size.width) w = self->max_size.width;
- if (w < minw) w = minw;
- if (h > self->max_size.height) h = self->max_size.height;
- if (h < minh) h = minh;
- }
+ /* if this is a user-requested resize, then check against min/max
+ sizes */
+
+ /* smaller than min size or bigger than max size? */
+ if (w > self->max_size.width) w = self->max_size.width;
+ if (w < minw) w = minw;
+ if (h > self->max_size.height) h = self->max_size.height;
+ if (h < minh) h = minh;
w -= basew;
h -= baseh;
h /= self->size_inc.height;
/* you cannot resize to nothing */
- if (w < 1) w = 1;
- if (h < 1) h = 1;
+ if (basew + w < 1) w = 1 - basew;
+ if (baseh + h < 1) h = 1 - baseh;
/* store the logical size */
- SIZE_SET(self->logical_size, w, h);
+ SIZE_SET(self->logical_size,
+ self->size_inc.width > 1 ? w : w + basew,
+ self->size_inc.height > 1 ? h : h + baseh);
w *= self->size_inc.width;
h *= self->size_inc.height;
w += basew;
h += baseh;
- if (user) {
- /* adjust the height to match the width for the aspect ratios.
- for this, min size is not substituted for base size ever. */
- w -= self->base_size.width;
- h -= self->base_size.height;
+ /* adjust the height to match the width for the aspect ratios.
+ for this, min size is not substituted for base size ever. */
+ w -= self->base_size.width;
+ h -= self->base_size.height;
- if (self->min_ratio)
- if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
- if (self->max_ratio)
- if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
+ if (self->min_ratio)
+ if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
+ if (self->max_ratio)
+ if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
- w += self->base_size.width;
- h += self->base_size.height;
- }
+ w += self->base_size.width;
+ h += self->base_size.height;
}
switch (anchor) {
/* for app-requested resizes, always resize if 'resized' is true.
for user-requested ones, only resize if final is true, or when
- resizing in opaque mode */
+ resizing in redraw mode */
if ((!user && resized) ||
- (user && (final || (resized && config_opaque_resize))))
+ (user && (final || (resized && config_redraw_resize))))
XResizeWindow(ob_display, self->window, w, h);
/* move/resize the frame to match the request */
if (self->frame) {
- if (self->decorations != self->frame->decorations)
+ if (self->decorations != fdecor || self->max_horz != fhorz)
moved = resized = TRUE;
if (moved || resized)
- frame_adjust_area(self->frame, moved, resized);
+ frame_adjust_area(self->frame, moved, resized, FALSE);
- /* If you send this and the client hasn't changed you end up with buggy
- clients (emacs) freaking out, cuz they send back a configure every
- time they receive this event, which resends them this event... etc.
- */
- if ((!user && moved) || (user && final)) {
+ if (!resized && (force_reply || ((!user && moved) || (user && final))))
+ {
XEvent event;
event.type = ConfigureNotify;
event.xconfigure.display = ob_display;
event.xconfigure.event = self->window;
event.xconfigure.window = self->window;
-
+
/* root window real coords */
- event.xconfigure.x = self->frame->area.x + self->frame->size.left;
- event.xconfigure.y = self->frame->area.y + self->frame->size.top;
-
+ event.xconfigure.x = self->frame->area.x + self->frame->size.left -
+ self->border_width;
+ event.xconfigure.y = self->frame->area.y + self->frame->size.top -
+ self->border_width;
event.xconfigure.width = w;
event.xconfigure.height = h;
event.xconfigure.border_width = 0;
if (self->iconic != iconic) {
- g_message("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
- self->window);
+ ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
+ self->window);
self->iconic = iconic;
client_change_state(self); /* change the state hints on the client */
+ client_setup_decor_and_functions(self);
+
/* figure out where the client should be going */
frame_frame_gravity(self->frame, &x, &y);
client_configure(self, OB_CORNER_TOPLEFT, x, y, w, h, TRUE, TRUE);
self->shaded = shade;
client_change_state(self);
/* resize the frame to just the titlebar */
- frame_adjust_area(self->frame, FALSE, FALSE);
+ frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
}
void client_close(ObClient *self)
if (target != self->desktop) {
- g_message("Setting desktop %u", target+1);
+ ob_debug("Setting desktop %u\n", target+1);
g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
}
#ifdef DEBUG_FOCUS
- g_message("%sively focusing %lx at %d", (self->can_focus ? "act" : "pass"),
- self->window, (int)
- event_lasttime);
+ ob_debug("%sively focusing %lx at %d\n",
+ (self->can_focus ? "act" : "pass"),
+ self->window, (int) event_lasttime);
#endif
/* Cause the FocusIn to come back to us. Important for desktop switches,
{
g_assert(focus_client == self);
#ifdef DEBUG_FOCUS
- g_message("client_unfocus for %lx", self->window);
+ ob_debug("client_unfocus for %lx\n", self->window);
#endif
focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
}
-void client_activate(ObClient *self)
+void client_activate(ObClient *self, gboolean here)
{
if (client_normal(self) && screen_showing_desktop)
screen_show_desktop(FALSE);
if (self->iconic)
- client_iconify(self, FALSE, TRUE);
- else if (!self->frame->visible)
+ client_iconify(self, FALSE, FALSE);
+ if (self->desktop != DESKTOP_ALL &&
+ self->desktop != screen_desktop) {
+ if (here)
+ client_set_desktop(self, screen_desktop, FALSE);
+ else
+ screen_set_desktop(self->desktop);
+ } else if (!self->frame->visible)
/* if its not visible for other reasons, then don't mess
with it */
return;
his_cy = (cur->frame->area.y - my_cy)
+ cur->frame->area.height / 2;
- if(dir > 3) {
+ if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
+ dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
int tx;
/* Rotate the diagonals 45 degrees counterclockwise.
* To do this, multiply the matrix /+h +h\ with the
return self;
}
+
+ObClient *client_search_transient(ObClient *self, ObClient *search)
+{
+ GSList *sit;
+
+ for (sit = self->transients; sit; sit = g_slist_next(sit)) {
+ if (sit->data == search)
+ return search;
+ if (client_search_transient(sit->data, search))
+ return search;
+ }
+ return NULL;
+}
+
+gchar* client_get_sm_client_id(ObClient *self)
+{
+ gchar *id = NULL;
+
+ if (!PROP_GETS(self->window, sm_client_id, locale, &id) && self->group)
+ PROP_GETS(self->group->leader, sm_client_id, locale, &id);
+ return id;
+}
+
+/* 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.
+ */
+int client_directional_edge_search(ObClient *c, ObDirection dir)
+{
+ int dest;
+ int my_edge_start, my_edge_end, my_offset;
+ GList *it;
+ Rect *a;
+
+ if(!client_list)
+ return -1;
+
+ a = screen_area(c->desktop);
+
+ 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;
+
+ dest = a->y; /* default: top of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.x;
+ his_edge_end = cur->frame->area.x + cur->frame->area.width;
+ his_offset = cur->frame->area.y + cur->frame->area.height;
+
+ if(his_offset + 1 > my_offset)
+ continue;
+
+ if(his_offset < dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+ }
+ 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 + c->frame->area.height;
+
+ dest = a->y + a->height; /* default: bottom of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.x;
+ his_edge_end = cur->frame->area.x + cur->frame->area.width;
+ his_offset = cur->frame->area.y;
+
+
+ if(his_offset - 1 < my_offset)
+ continue;
+
+ if(his_offset > dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+ }
+ 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;
+
+ dest = a->x; /* default: leftmost egde of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.y;
+ his_edge_end = cur->frame->area.y + cur->frame->area.height;
+ his_offset = cur->frame->area.x + cur->frame->area.width;
+
+ if(his_offset + 1 > my_offset)
+ continue;
+
+ if(his_offset < dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+
+ }
+ break;
+ 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 + c->frame->area.width;
+
+ dest = a->x + a->width; /* default: rightmost edge of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.y;
+ his_edge_end = cur->frame->area.y + cur->frame->area.height;
+ his_offset = cur->frame->area.x;
+
+ if(his_offset - 1 < my_offset)
+ continue;
+
+ if(his_offset > dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+ }
+ break;
+ case OB_DIRECTION_NORTHEAST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_NORTHWEST:
+ case OB_DIRECTION_SOUTHWEST:
+ /* not implemented */
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return dest;
+}