r2313: 2002 -> 2003
[rox-filer.git] / ROX-Filer / src / tasklist.c
blobfcf384925776262333d88ba8600c39d1616cedf0
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* tasklist.c - code for tracking windows
24 * Loosly based on code in GNOME's libwnck.
27 #include "config.h"
29 #include <stdlib.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdk.h>
33 #include <gdk/gdkx.h>
35 #include <X11/Xlib.h>
36 #include <X11/Xatom.h>
38 #include "global.h"
40 #include "tasklist.h"
41 #include "wrapped.h"
42 #include "options.h"
43 #include "gui_support.h"
44 #include "main.h"
45 #include "pinboard.h"
46 #include "pixmaps.h"
47 #include "support.h"
49 /* There is one of these for each window controlled by the window
50 * manager (all tasks) in the _NET_CLIENT_LIST property.
52 typedef struct _IconWindow IconWindow;
54 struct _IconWindow {
55 GtkWidget *widget; /* Widget used for icon when iconified */
56 GtkWidget *label;
57 gchar *text;
58 Window xwindow;
59 gboolean iconified;
62 /* If TRUE, only iconfied windows with _NET_WM_STATE_HIDDEN are really icons */
63 static gboolean wm_supports_hidden = FALSE;
65 static GdkAtom xa__NET_SUPPORTED = GDK_NONE;
66 static GdkAtom xa_WM_STATE = GDK_NONE;
67 static GdkAtom xa_WM_NAME = GDK_NONE;
68 static GdkAtom xa_WM_ICON_NAME = GDK_NONE;
69 static GdkAtom xa_UTF8_STRING = GDK_NONE;
70 static GdkAtom xa_TEXT = GDK_NONE;
71 static GdkAtom xa__NET_WM_VISIBLE_NAME = GDK_NONE;
72 static GdkAtom xa__NET_WM_ICON_NAME = GDK_NONE;
73 static GdkAtom xa__NET_CLIENT_LIST = GDK_NONE;
74 static GdkAtom xa__NET_WM_ICON_GEOMETRY = GDK_NONE;
75 static GdkAtom xa__NET_WM_STATE = GDK_NONE;
76 static GdkAtom xa__NET_WM_STATE_HIDDEN = GDK_NONE;
78 /* We have selected destroy and property events on every window in
79 * this table.
81 static GHashTable *known = NULL; /* XID -> IconWindow */
83 /* Static prototypes */
84 static void remove_window(Window win);
85 static void tasklist_update(gboolean to_empty);
86 static GdkFilterReturn window_filter(GdkXEvent *xevent,
87 GdkEvent *event,
88 gpointer data);
89 static guint xid_hash(XID *xid);
90 static gboolean xid_equal(XID *a, XID *b);
91 static void state_changed(IconWindow *win);
92 static void show_icon(IconWindow *win);
93 static void icon_win_free(IconWindow *win);
94 static void update_style(gpointer key, gpointer data, gpointer user_data);
95 static void update_supported(void);
97 /****************************************************************
98 * EXTERNAL INTERFACE *
99 ****************************************************************/
101 void tasklist_set_active(gboolean active)
103 static gboolean need_init = TRUE;
104 static gboolean tasklist_active = FALSE;
106 if (active == tasklist_active)
107 return;
108 tasklist_active = active;
110 if (need_init)
112 GdkWindow *root;
114 root = gdk_get_default_root_window();
116 xa__NET_SUPPORTED = gdk_atom_intern("_NET_SUPPORTED", FALSE);
117 xa_WM_STATE = gdk_atom_intern("WM_STATE", FALSE);
118 xa_WM_ICON_NAME = gdk_atom_intern("WM_ICON_NAME", FALSE);
119 xa_WM_NAME = gdk_atom_intern("WM_NAME", FALSE);
120 xa_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
121 xa_TEXT = gdk_atom_intern("TEXT", FALSE);
122 xa__NET_CLIENT_LIST =
123 gdk_atom_intern("_NET_CLIENT_LIST", FALSE);
124 xa__NET_WM_VISIBLE_NAME =
125 gdk_atom_intern("_NET_WM_VISIBLE_NAME", FALSE);
126 xa__NET_WM_ICON_NAME =
127 gdk_atom_intern("_NET_WM_ICON_NAME", FALSE);
128 xa__NET_WM_ICON_GEOMETRY =
129 gdk_atom_intern("_NET_WM_ICON_GEOMETRY", FALSE);
130 xa__NET_WM_STATE = gdk_atom_intern("_NET_WM_STATE", FALSE);
131 xa__NET_WM_STATE_HIDDEN =
132 gdk_atom_intern("_NET_WM_STATE_HIDDEN", FALSE);
134 known = g_hash_table_new_full((GHashFunc) xid_hash,
135 (GEqualFunc) xid_equal,
136 NULL,
137 (GDestroyNotify) icon_win_free);
138 gdk_window_set_events(root, gdk_window_get_events(root) |
139 GDK_PROPERTY_CHANGE_MASK);
140 need_init = FALSE;
143 if (active)
145 gdk_window_add_filter(NULL, window_filter, NULL);
146 update_supported();
148 else
149 gdk_window_remove_filter(NULL, window_filter, NULL);
151 tasklist_update(!active);
154 /* User has changes the colours in the options box... */
155 void tasklist_style_changed(void)
157 if (known)
158 g_hash_table_foreach(known, update_style, NULL);
161 /****************************************************************
162 * INTERNAL FUNCTIONS *
163 ****************************************************************/
165 static void icon_win_free(IconWindow *win)
167 g_return_if_fail(win->widget == NULL);
168 g_return_if_fail(win->label == NULL);
170 g_free(win->text);
171 g_free(win);
174 /* From gdk */
175 static guint xid_hash(XID *xid)
177 return *xid;
180 /* From gdk */
181 static gboolean xid_equal(XID *a, XID *b)
183 return (*a == *b);
186 static int wincmp(const void *a, const void *b)
188 const Window *aw = a;
189 const Window *bw = b;
191 if (*aw < *bw)
192 return -1;
193 else if (*aw > *bw)
194 return 1;
195 else
196 return 0;
199 /* Read the list of WINDOWs from (xwindow,atom), returning them
200 * in a (sorted) Array of Windows. On error, an empty array is
201 * returned.
202 * Free the array afterwards.
204 static GArray *get_window_list(Window xwindow, GdkAtom atom)
206 GArray *array;
207 Atom type;
208 int format;
209 gulong nitems;
210 gulong bytes_after;
211 Window *data;
212 int err, result;
213 int i;
215 array = g_array_new(FALSE, FALSE, sizeof(Window));
217 gdk_error_trap_push();
218 type = None;
219 result = XGetWindowProperty(gdk_display,
220 xwindow,
221 gdk_x11_atom_to_xatom(atom),
222 0, G_MAXLONG,
223 False, XA_WINDOW, &type, &format, &nitems,
224 &bytes_after, (guchar **)&data);
225 err = gdk_error_trap_pop();
227 if (err != Success || result != Success)
228 return array;
230 if (type == XA_WINDOW)
232 for (i = 0; i < nitems; i++)
233 g_array_append_val(array, data[i]);
235 if (array->len)
236 g_array_sort(array, wincmp);
239 XFree(data);
241 return array;
244 static gchar *get_str(IconWindow *win, GdkAtom atom)
246 Atom rtype;
247 int format;
248 gulong nitems;
249 gulong bytes_after;
250 char *data, *str = NULL;
252 if (XGetWindowProperty(gdk_display, win->xwindow,
253 gdk_x11_atom_to_xatom(atom),
254 0, G_MAXLONG, False,
255 AnyPropertyType,
256 &rtype, &format, &nitems,
257 &bytes_after, (guchar **) &data) == Success && data)
259 if (*data)
260 str = g_strdup(data);
261 XFree(data);
264 return str;
267 static void get_icon_name(IconWindow *win)
269 null_g_free(&win->text);
271 win->text = get_str(win, xa__NET_WM_ICON_NAME);
272 if (!win->text)
273 win->text = get_str(win, xa__NET_WM_VISIBLE_NAME);
274 if (!win->text)
275 win->text = get_str(win, xa_WM_ICON_NAME);
276 if (!win->text)
277 win->text = get_str(win, xa_WM_NAME);
278 if (!win->text)
279 win->text = g_strdup(_("Window"));
282 /* Call from within error_push/pop */
283 static void window_check_status(IconWindow *win)
285 Atom type;
286 int format;
287 gulong nitems;
288 gulong bytes_after;
289 gint32 *data;
290 gboolean iconic = FALSE;
292 if (wm_supports_hidden && XGetWindowProperty(gdk_display, win->xwindow,
293 gdk_x11_atom_to_xatom(xa__NET_WM_STATE),
294 0, G_MAXLONG, False,
295 XA_ATOM,
296 &type, &format, &nitems,
297 &bytes_after, (guchar **) &data) == Success && data)
299 GdkAtom state;
300 int i;
302 for (i = 0; i < nitems; i++)
304 state = gdk_x11_xatom_to_atom((Atom) data[i]);
305 if (state == xa__NET_WM_STATE_HIDDEN)
307 iconic = TRUE;
308 break;
311 XFree(data);
313 else if (XGetWindowProperty(gdk_display, win->xwindow,
314 gdk_x11_atom_to_xatom(xa_WM_STATE),
315 0, 1, False,
316 gdk_x11_atom_to_xatom(xa_WM_STATE),
317 &type, &format, &nitems,
318 &bytes_after, (guchar **) &data) == Success && data)
320 iconic = data[0] == 3;
321 XFree(data);
323 else
324 iconic = FALSE;
326 if (win->iconified == iconic)
327 return;
329 win->iconified = iconic;
331 state_changed(win);
334 /* Called for all events on all windows */
335 static GdkFilterReturn window_filter(GdkXEvent *xevent,
336 GdkEvent *event,
337 gpointer data)
339 XEvent *xev = (XEvent *) xevent;
340 IconWindow *w;
342 if (xev->type == PropertyNotify)
344 GdkAtom atom = gdk_x11_xatom_to_atom(xev->xproperty.atom);
345 Window win = ((XPropertyEvent *) xev)->window;
347 if (atom == xa_WM_STATE || atom == xa__NET_WM_STATE)
349 w = g_hash_table_lookup(known, &win);
351 if (w)
353 gdk_error_trap_push();
354 window_check_status(w);
355 if (gdk_error_trap_pop() != Success)
356 g_hash_table_remove(known, &win);
359 else if (atom == xa__NET_CLIENT_LIST)
360 tasklist_update(FALSE);
361 else if (atom == xa__NET_SUPPORTED)
362 update_supported();
365 return GDK_FILTER_CONTINUE;
368 /* Window has been added to list of managed windows */
369 static void add_window(Window win)
371 IconWindow *w;
373 /* g_print("[ New window %ld ]\n", (long) win); */
375 w = g_hash_table_lookup(known, &win);
377 if (!w)
379 XWindowAttributes attr;
381 gdk_error_trap_push();
383 XGetWindowAttributes(gdk_display, win, &attr);
385 if (gdk_error_trap_pop() != Success)
386 return;
387 gdk_error_trap_push();
389 XSelectInput(gdk_display, win, attr.your_event_mask |
390 PropertyChangeMask);
392 gdk_flush();
394 if (gdk_error_trap_pop() != Success)
395 return;
397 w = g_new(IconWindow, 1);
398 w->widget = NULL;
399 w->label = NULL;
400 w->text = NULL;
401 w->xwindow = win;
402 w->iconified = FALSE;
404 g_hash_table_insert(known, &w->xwindow, w);
407 gdk_error_trap_push();
409 window_check_status(w);
411 #if 0
412 set_iconify_pos(w);
413 #endif
415 if (gdk_error_trap_pop() != Success)
416 g_hash_table_remove(known, &win);
419 /* Window is no longer managed, but hasn't been destroyed yet */
420 static void remove_window(Window win)
422 IconWindow *w;
424 /* g_print("[ Remove window %ld ]\n", (long) win); */
426 w = g_hash_table_lookup(known, &win);
427 if (w)
429 if (w->iconified)
431 w->iconified = FALSE;
432 state_changed(w);
435 g_hash_table_remove(known, &win);
439 /* Make sure the window list is up-to-date. Call once to start, and then
440 * everytime _NET_CLIENT_LIST changes.
441 * If 'to_empty' is set them pretend all windows have disappeared.
443 static void tasklist_update(gboolean to_empty)
445 static GArray *old_mapping = NULL;
446 GArray *mapping = NULL;
447 int new_i, old_i;
449 if (!old_mapping)
451 old_mapping = g_array_new(FALSE, FALSE, sizeof(Window));
454 if (to_empty)
455 mapping = g_array_new(FALSE, FALSE, sizeof(Window));
456 else
457 mapping = get_window_list(gdk_x11_get_default_root_xwindow(),
458 gdk_atom_intern("_NET_CLIENT_LIST", FALSE));
460 new_i = 0;
461 old_i = 0;
462 while (new_i < mapping->len && old_i < old_mapping->len)
464 Window new = g_array_index(mapping, Window, new_i);
465 Window old = g_array_index(old_mapping, Window, old_i);
467 if (new == old)
469 new_i++;
470 old_i++;
472 else if (new < old)
474 add_window(new);
475 new_i++;
477 else
479 remove_window(old);
480 old_i++;
483 while (new_i < mapping->len)
485 add_window(g_array_index(mapping, Window, new_i));
486 new_i++;
488 while (old_i < old_mapping->len)
490 remove_window(g_array_index(old_mapping, Window, old_i));
491 old_i++;
494 g_array_free(old_mapping, TRUE);
495 old_mapping = mapping;
498 /* Called when the user clicks on the button */
499 static void uniconify(IconWindow *win)
501 XClientMessageEvent sev;
503 sev.type = ClientMessage;
504 sev.display = gdk_display;
505 sev.format = 32;
506 sev.window = win->xwindow;
507 sev.message_type = gdk_x11_atom_to_xatom(
508 gdk_atom_intern("_NET_ACTIVE_WINDOW", FALSE));
509 sev.data.l[0] = 0;
511 gdk_error_trap_push();
513 XSendEvent(gdk_display, DefaultRootWindow(gdk_display), False,
514 SubstructureNotifyMask | SubstructureRedirectMask,
515 (XEvent *) &sev);
516 XSync(gdk_display, False);
518 gdk_error_trap_pop();
521 static gint drag_start_x = -1;
522 static gint drag_start_y = -1;
523 static gboolean drag_started = FALSE;
524 static gint drag_off_x = -1;
525 static gint drag_off_y = -1;
527 static void icon_button_press(GtkWidget *widget,
528 GdkEventButton *event,
529 IconWindow *win)
531 if (event->button == 1)
533 drag_start_x = event->x_root;
534 drag_start_y = event->y_root;
535 drag_started = FALSE;
537 drag_off_x = event->x;
538 drag_off_y = event->y;
542 static gboolean icon_motion_notify(GtkWidget *widget,
543 GdkEventMotion *event,
544 IconWindow *win)
546 if (event->state & GDK_BUTTON1_MASK)
548 int dx = event->x_root - drag_start_x;
549 int dy = event->y_root - drag_start_y;
551 if (!drag_started)
553 if (abs(dx) < 5 && abs(dy) < 5)
554 return FALSE;
555 drag_started = TRUE;
558 fixed_move_fast(GTK_FIXED(win->widget->parent),
559 win->widget,
560 event->x_root - drag_off_x,
561 event->y_root - drag_off_y);
564 return FALSE;
567 static void button_released(GtkWidget *widget, IconWindow *win)
569 if (!drag_started)
570 uniconify(win);
573 static GdkColormap* get_cmap(GdkPixmap *pixmap)
575 GdkColormap *cmap;
577 cmap = gdk_drawable_get_colormap(pixmap);
578 if (cmap)
579 g_object_ref(G_OBJECT(cmap));
581 if (cmap == NULL)
583 if (gdk_drawable_get_depth(pixmap) == 1)
585 /* Masks don't need colourmaps */
586 cmap = NULL;
588 else
590 /* Try system cmap */
591 cmap = gdk_colormap_get_system();
592 g_object_ref(G_OBJECT(cmap));
596 /* Be sure we aren't going to blow up due to visual mismatch */
597 if (cmap && (gdk_colormap_get_visual(cmap)->depth !=
598 gdk_drawable_get_depth(pixmap)))
599 cmap = NULL;
601 return cmap;
604 /* Copy a pixmap from the server to a client-side pixbuf */
605 static GdkPixbuf* pixbuf_from_pixmap(Pixmap xpixmap)
607 GdkDrawable *drawable;
608 GdkPixbuf *retval;
609 GdkColormap *cmap;
610 int width, height;
612 retval = NULL;
614 drawable = gdk_xid_table_lookup(xpixmap);
616 if (drawable)
617 g_object_ref(G_OBJECT(drawable));
618 else
619 drawable = gdk_pixmap_foreign_new(xpixmap);
621 cmap = get_cmap(drawable);
623 /* GDK is supposed to do this but doesn't in GTK 2.0.2,
624 * fixed in 2.0.3
626 gdk_drawable_get_size(drawable, &width, &height);
628 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, cmap,
629 0, 0, 0, 0, width, height);
631 if (cmap)
632 g_object_unref(G_OBJECT(cmap));
633 g_object_unref(G_OBJECT(drawable));
635 return retval;
638 /* Creates a new masked pixbuf from a non-masked pixbuf and a mask */
639 static GdkPixbuf* apply_mask(GdkPixbuf *pixbuf, GdkPixbuf *mask)
641 int w, h;
642 int i, j;
643 GdkPixbuf *with_alpha;
644 guchar *src;
645 guchar *dest;
646 int src_stride;
647 int dest_stride;
649 w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
650 h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
652 with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
654 dest = gdk_pixbuf_get_pixels(with_alpha);
655 src = gdk_pixbuf_get_pixels(mask);
657 dest_stride = gdk_pixbuf_get_rowstride(with_alpha);
658 src_stride = gdk_pixbuf_get_rowstride(mask);
660 i = 0;
661 while (i < h)
663 j = 0;
664 while (j < w)
666 guchar *s = src + i * src_stride + j * 3;
667 guchar *d = dest + i * dest_stride + j * 4;
669 /* s[0] == s[1] == s[2], they are 255 if the bit was
670 * set, 0 otherwise
672 if (s[0] == 0)
673 d[3] = 0; /* transparent */
674 else
675 d[3] = 255; /* opaque */
677 ++j;
680 ++i;
683 return with_alpha;
686 /* Return a suitable icon for this window. unref the result.
687 * Never returns NULL.
689 static GdkPixbuf *get_image_for(IconWindow *win)
691 static MaskedPixmap *default_icon = NULL;
692 Pixmap pixmap = None;
693 Pixmap mask = None;
694 XWMHints *hints;
695 GdkPixbuf *retval = NULL;
697 /* Try the pixmap and mask in the old WMHints... */
698 gdk_error_trap_push();
699 hints = XGetWMHints(gdk_display, win->xwindow);
701 if (hints)
703 if (hints->flags & IconPixmapHint)
704 pixmap = hints->icon_pixmap;
705 if (hints->flags & IconMaskHint)
706 mask = hints->icon_mask;
708 XFree(hints);
709 hints = NULL;
712 if (pixmap != None)
714 GdkPixbuf *mask_pb = NULL;
716 retval = pixbuf_from_pixmap(pixmap);
718 if (retval && mask != None)
719 mask_pb = pixbuf_from_pixmap(mask);
721 if (mask_pb)
723 GdkPixbuf *masked;
725 masked = apply_mask(retval, mask_pb);
726 g_object_unref(G_OBJECT(mask_pb));
728 if (masked)
730 g_object_unref(G_OBJECT(retval));
731 retval = masked;
736 gdk_flush();
738 gdk_error_trap_pop();
740 if (!retval)
742 if (!default_icon)
743 default_icon = load_pixmap("iconified");
745 retval = default_icon->pixbuf;
746 g_object_ref(retval);
749 return retval;
752 /* A window has been iconified -- display it on the screen */
753 static void show_icon(IconWindow *win)
755 GdkPixbuf *pixbuf;
756 GtkWidget *vbox;
758 g_return_if_fail(win->widget == NULL);
759 g_return_if_fail(win->label == NULL);
761 win->widget = gtk_button_new();
762 vbox = gtk_vbox_new(FALSE, 0);
763 gtk_container_add(GTK_CONTAINER(win->widget), vbox);
765 pixbuf = get_image_for(win);
767 gtk_box_pack_start(GTK_BOX(vbox), gtk_image_new_from_pixbuf(pixbuf),
768 FALSE, TRUE, 0);
769 g_object_unref(pixbuf);
771 win->label = wrapped_label_new(win->text, 180);
773 update_style(NULL, win, NULL);
775 gtk_box_pack_start(GTK_BOX(vbox), win->label, FALSE, TRUE, 0);
777 gtk_widget_add_events(win->widget, GDK_BUTTON1_MOTION_MASK);
778 g_signal_connect(win->widget, "button-press-event",
779 G_CALLBACK(icon_button_press), win);
780 g_signal_connect(win->widget, "motion-notify-event",
781 G_CALLBACK(icon_motion_notify), win);
782 g_signal_connect(win->widget, "released",
783 G_CALLBACK(button_released), win);
785 gtk_widget_show_all(vbox); /* So the size comes out right */
786 pinboard_add_widget(win->widget);
787 gtk_widget_show(win->widget);
790 /* A window has been destroyed/expanded -- remove its icon */
791 static void hide_icon(IconWindow *win)
793 g_return_if_fail(win->widget != NULL);
795 gtk_widget_destroy(win->widget);
796 win->widget = NULL;
797 win->label = NULL;
800 static void state_changed(IconWindow *win)
802 if (win->iconified)
804 get_icon_name(win);
805 show_icon(win);
807 else
808 hide_icon(win);
811 #if 0
812 /* Set the _NET_WM_ICON_GEOMETRY property, which indicates where this window
813 * will be iconified to. Should be inside a push/pop.
815 static void set_iconify_pos(IconWindow *win)
817 gint32 data[4];
819 data[0] = iconify_next_x;
820 data[1] = iconify_next_y;
821 data[2] = 100;
822 data[3] = 32;
824 XChangeProperty(gdk_display, win->xwindow,
825 gdk_x11_atom_to_xatom(xa__NET_WM_ICON_GEOMETRY),
826 XA_CARDINAL, 32, PropModeReplace, (guchar *) data, 4);
828 #endif
830 static void update_style(gpointer key, gpointer data, gpointer user_data)
832 IconWindow *win = (IconWindow *) data;
834 if (win->widget)
835 widget_modify_font(win->label, pinboard_font);
838 /* Find out what the new window manager can do... */
839 static void update_supported(void)
841 Atom type;
842 int format;
843 gulong nitems;
844 gulong bytes_after;
845 Atom *data;
846 int err, result;
847 int i;
848 gboolean old_supports_hidden = wm_supports_hidden;
850 wm_supports_hidden = FALSE;
852 gdk_error_trap_push();
853 type = None;
854 result = XGetWindowProperty(gdk_display,
855 gdk_x11_get_default_root_xwindow(),
856 gdk_x11_atom_to_xatom(xa__NET_SUPPORTED),
857 0, G_MAXLONG,
858 False, XA_ATOM, &type, &format, &nitems,
859 &bytes_after, (guchar **)&data);
860 err = gdk_error_trap_pop();
862 if (err != Success || result != Success)
863 goto out;
865 for (i = 0; i < nitems; i++)
867 GdkAtom atom = gdk_x11_xatom_to_atom(data[i]);
869 if (atom == xa__NET_WM_STATE_HIDDEN)
870 wm_supports_hidden = TRUE;
873 XFree(data);
874 out:
876 if (wm_supports_hidden != old_supports_hidden)
878 tasklist_update(TRUE);
879 tasklist_update(FALSE);