+ gint x = self->area.x;
+ gint y = self->area.y;
+ if (client_find_onscreen(self, &x, &y,
+ self->area.width,
+ self->area.height, rude)) {
+ client_move(self, x, y);
+ }
+}
+
+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;
+
+ RECT_SET(desired, *x, *y, w, h);
+ all_a = screen_area(self->desktop);
+ mon_a = screen_area_monitor(self->desktop, screen_find_monitor(&desired));
+
+ /* get where the frame would be */
+ frame_client_gravity(self->frame, x, y, w, h);
+
+ /* 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 (!rude) {
+ Point oldtl, oldtr, oldbl, oldbr;
+ Point newtl, newtr, newbl, newbr;
+ gboolean stationary_l, stationary_r, stationary_t, stationary_b;
+
+ POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
+ POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
+ self->frame->area.y + self->frame->area.height - 1);
+ POINT_SET(oldtr, oldbr.x, oldtl.y);
+ POINT_SET(oldbl, oldtl.x, oldbr.y);
+
+ POINT_SET(newtl, *x, *y);
+ POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
+ POINT_SET(newtr, newbr.x, newtl.y);
+ POINT_SET(newbl, newtl.x, newbr.y);
+
+ /* is it moving or just resizing from some corner? */
+ stationary_l = oldtl.x == newtl.x;
+ stationary_r = oldtr.x == newtr.x;
+ stationary_t = oldtl.y == newtl.y;
+ stationary_b = oldbl.y == newbl.y;
+
+ /* if left edge is growing and didnt move right edge */
+ if (stationary_r && newtl.x < oldtl.x)
+ rudel = TRUE;
+ /* if right edge is growing and didnt move left edge */
+ if (stationary_l && newtr.x > oldtr.x)
+ ruder = TRUE;
+ /* if top edge is growing and didnt move bottom edge */
+ if (stationary_b && newtl.y < oldtl.y)
+ rudet = TRUE;
+ /* if bottom edge is growing and didnt move top edge */
+ if (stationary_t && newbl.y > oldbl.y)
+ 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);
+
+ 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);
+
+ /* get where the client should be */
+ frame_frame_gravity(self->frame, x, y, w, h);
+
+ return ox != *x || oy != *y;
+}
+
+static void client_get_all(ObClient *self, gboolean real)
+{
+ /* this is needed for the frame to set itself up */
+ client_get_area(self);
+
+ /* these things can change the decor and functions of the window */
+
+ client_get_mwm_hints(self);
+ /* this can change the mwmhints for special cases */
+ client_get_type_and_transientness(self);
+ client_get_state(self);
+ client_update_normal_hints(self);
+
+ /* get the session related properties, these can change decorations
+ from per-app settings */
+ client_get_session_ids(self);
+
+ /* now we got everything that can affect the decorations */
+ if (!real)
+ return;
+
+ /* get this early so we have it for debugging */
+ client_update_title(self);
+
+ client_update_protocols(self);
+
+ client_update_wmhints(self);
+ /* this may have already been called from client_update_wmhints */
+ if (!self->parents && !self->transient_for_group)
+ client_update_transient_for(self);
+
+ client_get_startup_id(self);
+ client_get_desktop(self);/* uses transient data/group/startup id if a
+ desktop is not specified */
+ client_get_shaped(self);
+
+ {
+ /* a couple type-based defaults for new windows */
+
+ /* this makes sure that these windows appear on all desktops */
+ 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);
+}
+
+static void client_get_startup_id(ObClient *self)
+{
+ if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
+ if (self->group)
+ PROP_GETS(self->group->leader,
+ net_startup_id, utf8, &self->startup_id);
+}
+
+static void client_get_area(ObClient *self)
+{
+ XWindowAttributes wattrib;
+ Status ret;
+
+ ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
+ g_assert(ret != BadWindow);
+
+ RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
+ POINT_SET(self->root_pos, wattrib.x, wattrib.y);
+ self->border_width = wattrib.border_width;
+
+ ob_debug("client area: %d %d %d %d bw %d\n", wattrib.x, wattrib.y,
+ wattrib.width, wattrib.height, wattrib.border_width);
+}
+
+static void client_get_desktop(ObClient *self)
+{
+ guint32 d = screen_num_desktops; /* an always-invalid value */
+
+ if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
+ if (d >= screen_num_desktops && d != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ else
+ self->desktop = d;
+ ob_debug("client requested desktop 0x%x\n", self->desktop);
+ } else {
+ GSList *it;
+ gboolean first = TRUE;
+ guint all = screen_num_desktops; /* not a valid value */
+
+ /* 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 (all != screen_num_desktops) {
+ self->desktop = all;
+ }
+ /* 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;
+ }
+ /* defaults to the current desktop */
+ else
+ self->desktop = screen_desktop;
+ }
+}
+
+static void client_get_state(ObClient *self)
+{
+ guint32 *state;
+ guint num;
+
+ if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
+ gulong i;
+ for (i = 0; i < num; ++i) {
+ if (state[i] == prop_atoms.net_wm_state_modal)
+ self->modal = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_shaded)
+ self->shaded = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_hidden)
+ self->iconic = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
+ self->skip_taskbar = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_skip_pager)
+ self->skip_pager = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_fullscreen)
+ self->fullscreen = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
+ self->max_vert = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
+ self->max_horz = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_above)
+ self->above = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_below)
+ self->below = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_demands_attention)
+ self->demands_attention = TRUE;
+ else if (state[i] == prop_atoms.ob_wm_state_undecorated)
+ self->undecorated = TRUE;
+ }
+
+ g_free(state);