+ 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
+ transient. */
+ if (self->transient)
+ self->type = OB_CLIENT_TYPE_DIALOG;
+ else
+ self->type = OB_CLIENT_TYPE_NORMAL;
+ }
+
+ /* then, based on our type, we can update our transientness.. */
+ if (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_TOOLBAR ||
+ self->type == OB_CLIENT_TYPE_MENU ||
+ self->type == OB_CLIENT_TYPE_UTILITY)
+ {
+ self->transient = TRUE;
+ }
+}
+
+void client_update_protocols(ObClient *self)
+{
+ guint32 *proto;
+ guint num_return, i;
+
+ self->focus_notify = FALSE;
+ self->delete_window = FALSE;
+
+ if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
+ for (i = 0; i < num_return; ++i) {
+ if (proto[i] == prop_atoms.wm_delete_window)
+ /* this means we can request the window to close */
+ self->delete_window = TRUE;
+ else if (proto[i] == prop_atoms.wm_take_focus)
+ /* if this protocol is requested, then the window will be
+ notified whenever we want it to receive focus */
+ self->focus_notify = TRUE;
+#ifdef SYNC
+ 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 */
+ self->sync_request = TRUE;
+#endif
+ }
+ g_free(proto);
+ }
+}
+
+#ifdef SYNC
+void client_update_sync_request_counter(ObClient *self)
+{
+ guint32 i;
+
+ if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) {
+ self->sync_counter = i;
+ } else
+ self->sync_counter = None;
+}
+#endif
+
+void client_get_colormap(ObClient *self)
+{
+ XWindowAttributes wa;
+
+ if (XGetWindowAttributes(ob_display, self->window, &wa))
+ client_update_colormap(self, wa.colormap);
+}
+
+void client_update_colormap(ObClient *self, Colormap colormap)
+{
+ if (colormap == self->colormap) return;
+
+ ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap);
+
+ if (client_focused(self)) {
+ screen_install_colormap(self, FALSE); /* uninstall old one */
+ self->colormap = colormap;
+ screen_install_colormap(self, FALSE); /* install new one */
+ } else
+ self->colormap = colormap;
+}
+
+void client_update_normal_hints(ObClient *self)
+{
+ XSizeHints size;
+ glong ret;
+
+ /* defaults */
+ self->min_ratio = 0.0f;
+ self->max_ratio = 0.0f;
+ SIZE_SET(self->size_inc, 1, 1);
+ SIZE_SET(self->base_size, 0, 0);
+ SIZE_SET(self->min_size, 0, 0);
+ SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
+
+ /* get the hints from the window */
+ if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
+ /* normal windows can't request placement! har har
+ if (!client_normal(self))
+ */
+ self->positioned = (size.flags & (PPosition|USPosition));
+ self->sized = (size.flags & (PSize|USSize));
+
+ if (size.flags & PWinGravity)
+ self->gravity = size.win_gravity;
+
+ if (size.flags & PAspect) {
+ if (size.min_aspect.y)
+ self->min_ratio =
+ (gfloat) size.min_aspect.x / size.min_aspect.y;
+ if (size.max_aspect.y)
+ self->max_ratio =
+ (gfloat) size.max_aspect.x / size.max_aspect.y;
+ }
+
+ 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)
+{
+ /* start with everything (cept fullscreen) */
+ 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 |
+ OB_FRAME_DECOR_CLOSE);
+ self->functions =
+ (OB_CLIENT_FUNC_RESIZE |
+ OB_CLIENT_FUNC_MOVE |
+ OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE |
+ OB_CLIENT_FUNC_SHADE |
+ OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_BELOW |
+ OB_CLIENT_FUNC_ABOVE |
+ OB_CLIENT_FUNC_UNDECORATE);
+
+ if (!(self->min_size.width < self->max_size.width ||
+ self->min_size.height < self->max_size.height))
+ self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ /* normal windows retain all of the possible decorations and
+ 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;
+
+ case OB_CLIENT_TYPE_MENU:
+ case OB_CLIENT_TYPE_TOOLBAR:
+ /* these windows can't iconify or maximize */
+ self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE);
+ self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE);
+ break;
+
+ case OB_CLIENT_TYPE_SPLASH:
+ /* these don't get get any decorations, and the only thing you can
+ do with them is move them */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_MOVE;
+ break;
+
+ case OB_CLIENT_TYPE_DESKTOP:
+ /* these windows are not manipulated by the window manager */
+ self->decorations = 0;
+ self->functions = 0;
+ break;
+
+ case OB_CLIENT_TYPE_DOCK:
+ /* these windows are not manipulated by the window manager, but they
+ can set below layer which has a special meaning */
+ self->decorations = 0;
+ self->functions = OB_CLIENT_FUNC_BELOW;
+ break;
+ }
+
+ /* Mwm Hints are applied subtractively to what has already been chosen for
+ 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_HANDLE) ||
+ (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
+ {
+ /* if the mwm hints request no handle or title, then all
+ decorations are disabled, but keep the border if that's
+ specified */
+ if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
+ self->decorations = OB_FRAME_DECOR_BORDER;
+ else
+ self->decorations = 0;
+ }
+ }
+ }
+
+ if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
+ if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
+ 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 &= ~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 | OB_FRAME_DECOR_HANDLE);
+
+ /* can't maximize without moving/resizing */
+ 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;
+ }
+
+ if (self->max_horz && self->max_vert) {
+ /* you can't resize fully maximized windows */
+ self->functions &= ~OB_CLIENT_FUNC_RESIZE;
+ /* kill the handle on fully maxed windows */
+ self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
+ }
+
+ /* If there are no decorations to remove, don't allow the user to try
+ toggle the state */
+ if (self->decorations == 0)
+ self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
+
+ /* 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)
+ self->decorations = 0;
+
+ /* if we don't have a titlebar, then we cannot shade! */
+ 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 */
+ if (self->fullscreen) {
+ self->functions &= (OB_CLIENT_FUNC_CLOSE |
+ OB_CLIENT_FUNC_FULLSCREEN |
+ OB_CLIENT_FUNC_ICONIFY);
+ self->decorations = 0;
+ }
+
+ client_change_allowed_actions(self);
+
+ if (reconfig)
+ /* force reconfigure to make sure decorations are updated */
+ client_reconfigure(self, TRUE);
+}
+
+static void client_change_allowed_actions(ObClient *self)
+{
+ gulong actions[12];
+ gint num = 0;
+
+ /* desktop windows are kept on all desktops */
+ if (self->type != OB_CLIENT_TYPE_DESKTOP)
+ actions[num++] = prop_atoms.net_wm_action_change_desktop;
+
+ if (self->functions & OB_CLIENT_FUNC_SHADE)
+ actions[num++] = prop_atoms.net_wm_action_shade;
+ if (self->functions & OB_CLIENT_FUNC_CLOSE)
+ actions[num++] = prop_atoms.net_wm_action_close;
+ if (self->functions & OB_CLIENT_FUNC_MOVE)
+ actions[num++] = prop_atoms.net_wm_action_move;
+ if (self->functions & OB_CLIENT_FUNC_ICONIFY)
+ actions[num++] = prop_atoms.net_wm_action_minimize;
+ if (self->functions & OB_CLIENT_FUNC_RESIZE)
+ actions[num++] = prop_atoms.net_wm_action_resize;
+ if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
+ actions[num++] = prop_atoms.net_wm_action_fullscreen;
+ if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
+ actions[num++] = prop_atoms.net_wm_action_maximize_horz;
+ actions[num++] = prop_atoms.net_wm_action_maximize_vert;
+ }
+ if (self->functions & OB_CLIENT_FUNC_ABOVE)
+ actions[num++] = prop_atoms.net_wm_action_above;
+ if (self->functions & OB_CLIENT_FUNC_BELOW)
+ actions[num++] = prop_atoms.net_wm_action_below;
+ if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
+ actions[num++] = prop_atoms.ob_wm_action_undecorate;
+
+ PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
+
+ /* make sure the window isn't breaking any rules now */
+
+ if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
+ if (self->frame) client_shade(self, FALSE);
+ else self->shaded = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
+ if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
+ else self->iconic = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
+ if (self->frame) client_fullscreen(self, FALSE);
+ else self->fullscreen = FALSE;
+ }
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
+ self->max_vert)) {
+ if (self->frame) client_maximize(self, FALSE, 0);
+ else self->max_vert = self->max_horz = FALSE;
+ }
+}
+
+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;
+
+ if (hints->flags & InputHint)
+ self->can_focus = hints->input;
+
+ /* only do this when first managing the window *AND* when we aren't
+ starting up! */
+ if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
+ if (hints->flags & StateHint)
+ self->iconic = hints->initial_state == IconicState;
+
+ ur = self->urgent;
+ self->urgent = (hints->flags & XUrgencyHint);
+ if (self->urgent && !ur)
+ client_hilite(self, TRUE);
+ else if (!self->urgent && ur && self->demands_attention)
+ client_hilite(self, FALSE);
+
+ if (!(hints->flags & WindowGroupHint))
+ hints->window_group = None;
+
+ /* did the group state change? */
+ if (hints->window_group !=
+ (self->group ? self->group->leader : None))
+ {
+ ObGroup *oldgroup = self->group;
+
+ /* remove from the old group if there was one */
+ if (self->group != NULL) {
+ group_remove(self->group, self);
+ self->group = NULL;
+ }
+
+ /* add ourself to the group if we have one */
+ if (hints->window_group != None) {
+ self->group = group_add(hints->window_group, self);
+ }
+
+ /* 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_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.
+
+ The logic for this is:
+ self->transient = TRUE always if the window wants to be
+ transient for something, even if transient_for was NULL because
+ it wasn't in a group before.
+
+ 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_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
+ no longer be transient for anything and that needs to be
+ updated.
+ */
+ if (self->transient &&
+ ((self->parents == NULL && oldgroup == NULL) ||
+ (self->transient_for_group && !self->group)))
+ client_update_transient_for(self);
+ }
+
+ /* the WM_HINTS can contain an icon */
+ if (hints->flags & IconPixmapHint)
+ client_update_icons(self);
+
+ XFree(hints);
+ }
+}
+
+void client_update_title(ObClient *self)
+{
+ gchar *data = NULL;
+ 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
+ */
+ data = g_strdup("");
+ } else
+ data = g_strdup("Unnamed Window");
+ }
+ }
+
+ if (self->client_machine) {
+ visible = g_strdup_printf("%s (%s)", data, self->client_machine);
+ g_free(data);
+ } else
+ visible = data;
+
+ PROP_SETS(self->window, net_wm_visible_name, visible);
+ self->title = visible;
+