+ /* 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);
+ }
+ }
+}
+
+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);
+ }
+}
+
+static void client_get_shaped(ObClient *self)
+{
+ self->shaped = FALSE;
+#ifdef SHAPE
+ if (extensions_shape) {
+ gint foo;
+ guint ufoo;
+ gint s;
+
+ XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
+
+ XShapeQueryExtents(ob_display, self->window, &s, &foo,
+ &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
+ &ufoo);
+ self->shaped = (s != 0);
+ }
+#endif
+}
+
+void client_update_transient_for(ObClient *self)
+{
+ 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! */
+ target = g_hash_table_lookup(window_map, &t);
+ /* if this happens then we need to check for it*/
+ g_assert(target != self);
+ if (target && !WINDOW_IS_CLIENT(target)) {
+ /* this can happen when a dialog is a child of
+ a dockapp, for example */
+ target = NULL;
+ }
+ }
+
+ /* 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_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 &&
+ oldgtran == newgtran &&
+ oldparent == newparent) return;
+
+ /** Remove the client from the transient tree **/
+
+ 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);
+ }
+ for (it = self->parents; it; it = next) {
+ next = g_slist_next(it);
+ c = it->data;
+ self->parents = g_slist_delete_link(self->parents, it);
+ c->transients = g_slist_remove(c->transients, self);
+ }
+
+ /** Re-add the client to the transient tree **/
+
+ /* 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 &&
+ !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 we need to add ourselves to
+ its children
+
+ WARNING: Cyclical transient ness is possible if two windows are
+ transient for eachother.
+ */
+ else if (newparent &&
+ /* don't make ourself its child if it is already our child */
+ !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);
+ }
+
+ /* 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
+ B is transient for A
+ C is transient for B
+ A can't be transient for C or we have a cycle
+ */
+ 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_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);