When uniconifying, set "requestor's currently active window" to zero to be on the...
[rox-filer.git] / ROX-Filer / src / tasklist.c
blob92d2d56654d57425944f8b4fe405a915a5a24727
1 /*
2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* tasklist.c - code for tracking windows
22 * Loosly based on code in GNOME's libwnck.
25 #include "config.h"
27 #include <stdlib.h>
29 #include <gtk/gtk.h>
30 #include <gdk/gdk.h>
31 #include <gdk/gdkx.h>
33 #include <X11/Xlib.h>
34 #include <X11/Xatom.h>
36 #include "global.h"
38 #include "tasklist.h"
39 #include "wrapped.h"
40 #include "options.h"
41 #include "gui_support.h"
42 #include "main.h"
43 #include "pinboard.h"
44 #include "pixmaps.h"
45 #include "support.h"
47 /* There is one of these for each window controlled by the window
48 * manager (all tasks) in the _NET_CLIENT_LIST property.
50 typedef struct _IconWindow IconWindow;
52 struct _IconWindow {
53 GtkWidget *widget; /* Widget used for icon when iconified */
54 GtkWidget *label;
55 gchar *text;
56 Window xwindow;
57 gboolean iconified;
58 gint timeout_update; /* Non-zero => timeout callback in use */
61 /* If TRUE, only iconfied windows with _NET_WM_STATE_HIDDEN are really icons */
62 static gboolean wm_supports_hidden = FALSE;
64 static GdkAtom xa__NET_SUPPORTED = GDK_NONE;
65 static GdkAtom xa_WM_STATE = GDK_NONE;
66 static GdkAtom xa_WM_NAME = GDK_NONE;
67 static GdkAtom xa_WM_ICON_NAME = GDK_NONE;
68 static GdkAtom xa_UTF8_STRING = GDK_NONE;
69 static GdkAtom xa_TEXT = GDK_NONE;
70 static GdkAtom xa__NET_WM_VISIBLE_NAME = GDK_NONE;
71 static GdkAtom xa__NET_WM_ICON_NAME = GDK_NONE;
72 static GdkAtom xa__NET_CLIENT_LIST = GDK_NONE;
73 static GdkAtom xa__NET_WM_ICON_GEOMETRY = GDK_NONE;
74 static GdkAtom xa__NET_WM_STATE = GDK_NONE;
75 static GdkAtom xa__NET_WM_STATE_HIDDEN = GDK_NONE;
76 static GdkAtom xa__NET_WM_DESKTOP = GDK_NONE;
77 static GdkAtom xa__NET_CURRENT_DESKTOP = GDK_NONE;
79 /* We have selected destroy and property events on every window in
80 * this table.
82 static GHashTable *known = NULL; /* XID -> IconWindow */
84 /* Static prototypes */
85 static void remove_window(Window win);
86 static void tasklist_update(gboolean to_empty);
87 static GdkFilterReturn window_filter(GdkXEvent *xevent,
88 GdkEvent *event,
89 gpointer data);
90 static guint xid_hash(XID *xid);
91 static gboolean xid_equal(XID *a, XID *b);
92 static void state_changed(IconWindow *win);
93 static void show_icon(IconWindow *win);
94 static void icon_win_free(IconWindow *win);
95 static void update_style(gpointer key, gpointer data, gpointer user_data);
96 static void update_supported(void);
97 static gboolean update_title(gpointer data);
98 static void update_current_desktop(void);
100 /* remember what desktop number is currently displayed */
101 static int cur_desktop = 0;
103 /****************************************************************
104 * EXTERNAL INTERFACE *
105 ****************************************************************/
107 void tasklist_set_active(gboolean active)
109 static gboolean need_init = TRUE;
110 static gboolean tasklist_active = FALSE;
112 if (active == tasklist_active)
114 if (o_pinboard_tasklist_per_workspace.has_changed && active)
115 tasklist_update(FALSE);
116 return;
118 tasklist_active = active;
120 if (active)
121 update_current_desktop();
123 if (need_init)
125 GdkWindow *root;
127 root = gdk_get_default_root_window();
129 xa__NET_SUPPORTED = gdk_atom_intern("_NET_SUPPORTED", FALSE);
130 xa_WM_STATE = gdk_atom_intern("WM_STATE", FALSE);
131 xa_WM_ICON_NAME = gdk_atom_intern("WM_ICON_NAME", FALSE);
132 xa_WM_NAME = gdk_atom_intern("WM_NAME", FALSE);
133 xa_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
134 xa_TEXT = gdk_atom_intern("TEXT", FALSE);
135 xa__NET_CLIENT_LIST =
136 gdk_atom_intern("_NET_CLIENT_LIST", FALSE);
137 xa__NET_WM_VISIBLE_NAME =
138 gdk_atom_intern("_NET_WM_VISIBLE_NAME", FALSE);
139 xa__NET_WM_ICON_NAME =
140 gdk_atom_intern("_NET_WM_ICON_NAME", FALSE);
141 xa__NET_WM_ICON_GEOMETRY =
142 gdk_atom_intern("_NET_WM_ICON_GEOMETRY", FALSE);
143 xa__NET_WM_STATE = gdk_atom_intern("_NET_WM_STATE", FALSE);
144 xa__NET_WM_STATE_HIDDEN =
145 gdk_atom_intern("_NET_WM_STATE_HIDDEN", FALSE);
146 xa__NET_WM_DESKTOP = gdk_atom_intern("_NET_WM_DESKTOP", FALSE);
147 xa__NET_CURRENT_DESKTOP = gdk_atom_intern("_NET_CURRENT_DESKTOP", FALSE);
149 known = g_hash_table_new_full((GHashFunc) xid_hash,
150 (GEqualFunc) xid_equal,
151 NULL,
152 (GDestroyNotify) icon_win_free);
153 gdk_window_set_events(root, gdk_window_get_events(root) |
154 GDK_PROPERTY_CHANGE_MASK);
155 need_init = FALSE;
158 if (active)
160 gdk_window_add_filter(NULL, window_filter, NULL);
161 update_supported();
163 else
164 gdk_window_remove_filter(NULL, window_filter, NULL);
166 tasklist_update(!active);
169 /* User has changes the colours in the options box... */
170 void tasklist_style_changed(void)
172 if (known)
173 g_hash_table_foreach(known, update_style, NULL);
176 /****************************************************************
177 * INTERNAL FUNCTIONS *
178 ****************************************************************/
180 static void icon_win_free(IconWindow *win)
182 g_return_if_fail(win->widget == NULL);
183 g_return_if_fail(win->label == NULL);
185 if (win->timeout_update)
186 g_source_remove(win->timeout_update);
188 g_free(win->text);
189 g_free(win);
192 /* From gdk */
193 static guint xid_hash(XID *xid)
195 return *xid;
198 /* From gdk */
199 static gboolean xid_equal(XID *a, XID *b)
201 return (*a == *b);
204 static int wincmp(const void *a, const void *b)
206 const Window *aw = a;
207 const Window *bw = b;
209 if (*aw < *bw)
210 return -1;
211 else if (*aw > *bw)
212 return 1;
213 else
214 return 0;
217 /* Read the list of WINDOWs from (xwindow,atom), returning them
218 * in a (sorted) Array of Windows. On error, an empty array is
219 * returned.
220 * Free the array afterwards.
222 static GArray *get_window_list(Window xwindow, GdkAtom atom)
224 GArray *array;
225 Atom type;
226 int format;
227 gulong nitems;
228 gulong bytes_after;
229 unsigned char *data;
230 int err, result;
231 int i;
233 array = g_array_new(FALSE, FALSE, sizeof(Window));
235 gdk_error_trap_push();
236 type = None;
237 result = XGetWindowProperty(gdk_display,
238 xwindow,
239 gdk_x11_atom_to_xatom(atom),
240 0, G_MAXLONG,
241 False, XA_WINDOW, &type, &format, &nitems,
242 &bytes_after, &data);
243 err = gdk_error_trap_pop();
245 if (err != Success || result != Success)
246 return array;
248 if (type == XA_WINDOW)
250 for (i = 0; i < nitems; i++)
251 g_array_append_val(array, ((Window *) data)[i]);
253 if (array->len)
254 g_array_sort(array, wincmp);
257 XFree(data);
259 return array;
262 static guchar *get_str(IconWindow *win, GdkAtom atom)
264 Atom rtype;
265 int format;
266 gulong nitems;
267 gulong bytes_after;
268 unsigned char *data, *str = NULL;
269 int err, result;
271 gdk_error_trap_push();
273 result = XGetWindowProperty(gdk_display, win->xwindow,
274 gdk_x11_atom_to_xatom(atom),
275 0, G_MAXLONG, False,
276 AnyPropertyType,
277 &rtype, &format, &nitems,
278 &bytes_after, &data);
280 err = gdk_error_trap_pop();
282 if (err == Success && result == Success && data)
284 if (*data)
285 str = g_strdup(data);
286 XFree(data);
289 return str;
292 static void get_icon_name(IconWindow *win)
294 null_g_free(&win->text);
296 /* Keep this list in sync with window_filter */
298 win->text = get_str(win, xa__NET_WM_ICON_NAME);
299 if (!win->text)
300 win->text = get_str(win, xa__NET_WM_VISIBLE_NAME);
301 if (!win->text)
302 win->text = get_str(win, xa_WM_ICON_NAME);
303 if (!win->text)
304 win->text = get_str(win, xa_WM_NAME);
305 if (!win->text)
306 win->text = g_strdup(_("Window"));
309 static gboolean update_title(gpointer data)
311 IconWindow *win = (IconWindow *) data;
313 if (!win->widget)
314 return FALSE; /* No longer an icon */
316 get_icon_name(win);
317 wrapped_label_set_text(WRAPPED_LABEL(win->label), win->text);
319 win->timeout_update = 0;
321 return FALSE;
324 /* Call from within error_push/pop */
325 static void window_check_status(IconWindow *win)
327 Atom type;
328 int format;
329 gulong nitems;
330 gulong bytes_after;
331 unsigned char *data;
332 Window transient_for;
333 gboolean iconic = FALSE;
335 if (XGetTransientForHint(gdk_display, win->xwindow, &transient_for) && transient_for)
336 iconic = FALSE;
337 else if (wm_supports_hidden && XGetWindowProperty(gdk_display, win->xwindow,
338 gdk_x11_atom_to_xatom(xa__NET_WM_STATE),
339 0, G_MAXLONG, False,
340 XA_ATOM,
341 &type, &format, &nitems,
342 &bytes_after, &data) == Success && data)
344 GdkAtom state;
345 int i;
347 for (i = 0; i < nitems; i++)
349 state = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
350 if (state == xa__NET_WM_STATE_HIDDEN)
352 iconic = TRUE;
353 break;
356 XFree(data);
358 else if (XGetWindowProperty(gdk_display, win->xwindow,
359 gdk_x11_atom_to_xatom(xa_WM_STATE),
360 0, 1, False,
361 gdk_x11_atom_to_xatom(xa_WM_STATE),
362 &type, &format, &nitems,
363 &bytes_after, &data) == Success && data)
365 iconic = ((guint32 *) data)[0] == 3;
366 XFree(data);
368 else
369 iconic = FALSE;
371 /* Iconified windows on another desktops are not shown */
372 if (o_pinboard_tasklist_per_workspace.int_value &&
373 XGetWindowProperty(gdk_display, win->xwindow,
374 gdk_x11_atom_to_xatom(xa__NET_WM_DESKTOP),
375 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
376 &bytes_after, &data) == Success && data)
378 guint32 desk_on = ((guint32 *) data)[0];
380 if ((desk_on != 0xffffffff) && (desk_on != cur_desktop))
381 iconic = FALSE;
382 XFree(data);
385 if (win->iconified == iconic)
386 return;
388 win->iconified = iconic;
390 state_changed(win);
392 gdk_flush();
395 static void update_current_desktop(void)
397 unsigned char *data;
398 Atom type;
399 int format;
400 gulong nitems;
401 gulong bytes_after;
403 if (XGetWindowProperty(gdk_display, gdk_x11_get_default_root_xwindow(),
404 gdk_x11_atom_to_xatom(xa__NET_CURRENT_DESKTOP),
405 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
406 &bytes_after, (unsigned char **)(&data)) == Success && data) {
407 cur_desktop = ((gint32*)data)[0];
408 XFree(data);
412 /* Called for all events on all windows */
413 static GdkFilterReturn window_filter(GdkXEvent *xevent,
414 GdkEvent *event,
415 gpointer data)
417 XEvent *xev = (XEvent *) xevent;
418 IconWindow *w;
420 if (xev->type == PropertyNotify)
422 GdkAtom atom = gdk_x11_xatom_to_atom(xev->xproperty.atom);
423 Window win = ((XPropertyEvent *) xev)->window;
425 if (atom == xa_WM_STATE || atom == xa__NET_WM_STATE)
427 w = g_hash_table_lookup(known, &win);
429 if (w)
431 gdk_error_trap_push();
432 window_check_status(w);
433 if (gdk_error_trap_pop() != Success)
434 g_hash_table_remove(known, &win);
437 else if (atom == xa__NET_CURRENT_DESKTOP)
439 update_current_desktop();
440 tasklist_update(FALSE);
442 else if (atom == xa__NET_WM_ICON_NAME ||
443 atom == xa__NET_WM_VISIBLE_NAME ||
444 atom == xa_WM_ICON_NAME ||
445 atom == xa_WM_NAME)
447 /* Keep this list in sync with get_icon_name */
448 w = g_hash_table_lookup(known, &win);
450 if (w && w->widget && !w->timeout_update)
451 w->timeout_update = g_timeout_add(100,
452 update_title, w);
454 else if (atom == xa__NET_CLIENT_LIST)
455 tasklist_update(FALSE);
456 else if (atom == xa__NET_SUPPORTED)
457 update_supported();
460 return GDK_FILTER_CONTINUE;
463 /* Window has been added to list of managed windows */
464 static void add_window(Window win)
466 IconWindow *w;
468 /* g_print("[ New window %ld ]\n", (long) win); */
470 w = g_hash_table_lookup(known, &win);
472 if (!w)
474 XWindowAttributes attr;
476 gdk_error_trap_push();
478 XGetWindowAttributes(gdk_display, win, &attr);
480 if (gdk_error_trap_pop() != Success)
481 return;
482 gdk_error_trap_push();
484 XSelectInput(gdk_display, win, attr.your_event_mask |
485 PropertyChangeMask);
487 gdk_flush();
489 if (gdk_error_trap_pop() != Success)
490 return;
492 w = g_new(IconWindow, 1);
493 w->widget = NULL;
494 w->label = NULL;
495 w->text = NULL;
496 w->xwindow = win;
497 w->iconified = FALSE;
498 w->timeout_update = 0;
500 g_hash_table_insert(known, &w->xwindow, w);
503 gdk_error_trap_push();
505 window_check_status(w);
507 #if 0
508 set_iconify_pos(w);
509 #endif
511 if (gdk_error_trap_pop() != Success)
512 g_hash_table_remove(known, &win);
515 /* Window is no longer managed, but hasn't been destroyed yet */
516 static void remove_window(Window win)
518 IconWindow *w;
520 /* g_print("[ Remove window %ld ]\n", (long) win); */
522 w = g_hash_table_lookup(known, &win);
523 if (w)
525 if (w->iconified)
527 w->iconified = FALSE;
528 state_changed(w);
531 g_hash_table_remove(known, &win);
535 /* Make sure the window list is up-to-date. Call once to start, and then
536 * everytime _NET_CLIENT_LIST changes.
537 * If 'to_empty' is set them pretend all windows have disappeared.
539 static void tasklist_update(gboolean to_empty)
541 static GArray *old_mapping = NULL;
542 GArray *mapping = NULL;
543 int new_i, old_i;
545 if (!old_mapping)
547 old_mapping = g_array_new(FALSE, FALSE, sizeof(Window));
550 if (to_empty)
551 mapping = g_array_new(FALSE, FALSE, sizeof(Window));
552 else
553 mapping = get_window_list(gdk_x11_get_default_root_xwindow(),
554 gdk_atom_intern("_NET_CLIENT_LIST", FALSE));
556 new_i = 0;
557 old_i = 0;
558 while (new_i < mapping->len && old_i < old_mapping->len)
560 Window new = g_array_index(mapping, Window, new_i);
561 Window old = g_array_index(old_mapping, Window, old_i);
563 if (new == old)
565 add_window(new);
566 new_i++;
567 old_i++;
569 else if (new < old)
571 add_window(new);
572 new_i++;
574 else
576 remove_window(old);
577 old_i++;
580 while (new_i < mapping->len)
582 add_window(g_array_index(mapping, Window, new_i));
583 new_i++;
585 while (old_i < old_mapping->len)
587 remove_window(g_array_index(old_mapping, Window, old_i));
588 old_i++;
591 g_array_free(old_mapping, TRUE);
592 old_mapping = mapping;
595 /* Called when the user clicks on the button */
596 static void uniconify(IconWindow *win, guint32 timestamp)
598 XClientMessageEvent sev;
600 sev.type = ClientMessage;
601 sev.display = gdk_display;
602 sev.format = 32;
603 sev.window = win->xwindow;
604 sev.message_type = gdk_x11_atom_to_xatom(
605 gdk_atom_intern("_NET_ACTIVE_WINDOW", FALSE));
606 sev.data.l[0] = 2;
607 sev.data.l[1] = timestamp;
608 sev.data.l[2] = 0;
610 gdk_error_trap_push();
612 XSendEvent(gdk_display, DefaultRootWindow(gdk_display), False,
613 SubstructureNotifyMask | SubstructureRedirectMask,
614 (XEvent *) &sev);
615 XSync(gdk_display, False);
617 gdk_error_trap_pop();
620 static gint drag_start_x = -1;
621 static gint drag_start_y = -1;
622 static gboolean drag_started = FALSE;
623 static gint drag_off_x = -1;
624 static gint drag_off_y = -1;
626 static void icon_button_press(GtkWidget *widget,
627 GdkEventButton *event,
628 IconWindow *win)
630 if (event->button == 1)
632 drag_start_x = event->x_root;
633 drag_start_y = event->y_root;
634 drag_started = FALSE;
636 drag_off_x = event->x;
637 drag_off_y = event->y;
641 static gboolean icon_motion_notify(GtkWidget *widget,
642 GdkEventMotion *event,
643 IconWindow *win)
645 if (event->state & GDK_BUTTON1_MASK)
647 int dx = event->x_root - drag_start_x;
648 int dy = event->y_root - drag_start_y;
650 if (!drag_started)
652 if (abs(dx) < 5 && abs(dy) < 5)
653 return FALSE;
654 drag_started = TRUE;
657 fixed_move_fast(GTK_FIXED(win->widget->parent),
658 win->widget,
659 event->x_root - drag_off_x,
660 event->y_root - drag_off_y);
662 pinboard_moved_widget(win->widget, win->text,
663 event->x_root - drag_off_x,
664 event->y_root - drag_off_y);
667 return FALSE;
670 static void button_released(GtkWidget *widget, GdkEventButton *event,
671 IconWindow *win)
673 if (!drag_started && event->button == 1)
674 uniconify(win, event->time);
677 static GdkColormap* get_cmap(GdkPixmap *pixmap)
679 GdkColormap *cmap;
681 cmap = gdk_drawable_get_colormap(pixmap);
682 if (cmap)
683 g_object_ref(G_OBJECT(cmap));
685 if (cmap == NULL)
687 if (gdk_drawable_get_depth(pixmap) == 1)
689 /* Masks don't need colourmaps */
690 cmap = NULL;
692 else
694 /* Try system cmap */
695 cmap = gdk_colormap_get_system();
696 g_object_ref(G_OBJECT(cmap));
700 /* Be sure we aren't going to blow up due to visual mismatch */
701 if (cmap && (gdk_colormap_get_visual(cmap)->depth !=
702 gdk_drawable_get_depth(pixmap)))
703 cmap = NULL;
705 return cmap;
708 /* Copy a pixmap from the server to a client-side pixbuf */
709 static GdkPixbuf* pixbuf_from_pixmap(Pixmap xpixmap)
711 GdkDrawable *drawable;
712 GdkPixbuf *retval;
713 GdkColormap *cmap;
714 int width, height;
716 retval = NULL;
718 drawable = gdk_xid_table_lookup(xpixmap);
720 if (GDK_IS_DRAWABLE(drawable))
721 g_object_ref(G_OBJECT(drawable));
722 else
724 drawable = gdk_pixmap_foreign_new(xpixmap);
725 if (!GDK_IS_DRAWABLE(drawable))
726 return retval;
729 cmap = get_cmap(drawable);
731 /* GDK is supposed to do this but doesn't in GTK 2.0.2,
732 * fixed in 2.0.3
734 gdk_drawable_get_size(drawable, &width, &height);
736 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, cmap,
737 0, 0, 0, 0, width, height);
739 if (cmap)
740 g_object_unref(G_OBJECT(cmap));
741 g_object_unref(G_OBJECT(drawable));
743 return retval;
746 /* Creates a new masked pixbuf from a non-masked pixbuf and a mask */
747 static GdkPixbuf* apply_mask(GdkPixbuf *pixbuf, GdkPixbuf *mask)
749 int w, h;
750 int i, j;
751 GdkPixbuf *with_alpha;
752 guchar *src;
753 guchar *dest;
754 int src_stride;
755 int dest_stride;
757 w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
758 h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
760 with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
762 dest = gdk_pixbuf_get_pixels(with_alpha);
763 src = gdk_pixbuf_get_pixels(mask);
765 dest_stride = gdk_pixbuf_get_rowstride(with_alpha);
766 src_stride = gdk_pixbuf_get_rowstride(mask);
768 i = 0;
769 while (i < h)
771 j = 0;
772 while (j < w)
774 guchar *s = src + i * src_stride + j * 3;
775 guchar *d = dest + i * dest_stride + j * 4;
777 /* s[0] == s[1] == s[2], they are 255 if the bit was
778 * set, 0 otherwise
780 if (s[0] == 0)
781 d[3] = 0; /* transparent */
782 else
783 d[3] = 255; /* opaque */
785 ++j;
788 ++i;
791 return with_alpha;
794 #define BORDER_X 16
795 #define BORDER_Y 8
796 /* Take the icon that the iconified window has given us and modify it to make
797 * it obvious what it is.
799 static GdkPixbuf *apply_window_effect(GdkPixbuf *src)
801 GdkPixbuf *new;
802 int w, h;
804 w = gdk_pixbuf_get_width(src);
805 h = gdk_pixbuf_get_height(src);
807 new = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), TRUE,
808 8, w + BORDER_X * 2, h + BORDER_Y * 2);
809 gdk_pixbuf_fill(new, 0x88888888);
811 gdk_pixbuf_composite(src, new,
812 BORDER_X, BORDER_Y, w, h,
813 BORDER_X, BORDER_Y, 1, 1,
814 GDK_INTERP_NEAREST, 255);
816 return new;
819 /* Return a suitable icon for this window. unref the result.
820 * Never returns NULL.
822 static GdkPixbuf *get_image_for(IconWindow *win)
824 static MaskedPixmap *default_icon = NULL;
825 Pixmap pixmap = None;
826 Pixmap mask = None;
827 XWMHints *hints;
828 GdkPixbuf *retval = NULL;
830 /* Try the pixmap and mask in the old WMHints... */
831 gdk_error_trap_push();
832 hints = XGetWMHints(gdk_display, win->xwindow);
834 if (hints)
836 if (hints->flags & IconPixmapHint)
837 pixmap = hints->icon_pixmap;
838 if (hints->flags & IconMaskHint)
839 mask = hints->icon_mask;
841 XFree(hints);
842 hints = NULL;
845 if (pixmap != None)
847 GdkPixbuf *mask_pb = NULL;
849 retval = pixbuf_from_pixmap(pixmap);
851 if (retval && mask != None)
852 mask_pb = pixbuf_from_pixmap(mask);
854 if (mask_pb)
856 GdkPixbuf *masked;
858 masked = apply_mask(retval, mask_pb);
859 g_object_unref(G_OBJECT(mask_pb));
861 if (masked)
863 g_object_unref(G_OBJECT(retval));
864 retval = masked;
869 gdk_flush();
871 gdk_error_trap_pop();
873 if (!retval)
875 if (!default_icon)
876 default_icon = load_pixmap("iconified");
878 retval = default_icon->pixbuf;
879 g_object_ref(retval);
882 /* Apply a special effect to make this look different from normal
883 * pinboard icons.
886 GdkPixbuf *old = retval;
887 GdkPixbuf *small;
888 small = scale_pixbuf(old, ICON_WIDTH, ICON_HEIGHT);
889 g_object_unref(old);
890 retval = apply_window_effect(small);
891 g_object_unref(small);
894 return retval;
897 /* Stop the button from highlighting */
898 static gint icon_expose(GtkWidget *widget, GdkEventExpose *event,
899 IconWindow *win)
901 static GtkWidgetClass *parent_class = NULL;
903 g_return_val_if_fail(win != NULL, TRUE);
904 g_return_val_if_fail(win->label != NULL, TRUE);
906 if (!parent_class)
908 gpointer c = ((GTypeInstance *) widget)->g_class;
909 parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
912 draw_label_shadow((WrappedLabel *) win->label, event->region);
914 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
915 event->region);
916 (parent_class->expose_event)(widget, event);
917 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
918 NULL);
920 /* Stop the button effect */
921 return TRUE;
924 /* A window has been iconified -- display it on the screen */
925 static void show_icon(IconWindow *win)
927 GdkPixbuf *pixbuf;
928 GtkWidget *vbox;
930 g_return_if_fail(win->widget == NULL);
931 g_return_if_fail(win->label == NULL);
933 win->widget = gtk_button_new();
934 gtk_button_set_relief(GTK_BUTTON(win->widget), GTK_RELIEF_NONE);
935 g_signal_connect(win->widget, "expose-event",
936 G_CALLBACK(icon_expose), win);
937 vbox = gtk_vbox_new(FALSE, 0);
938 gtk_container_add(GTK_CONTAINER(win->widget), vbox);
940 pixbuf = get_image_for(win);
942 gtk_box_pack_start(GTK_BOX(vbox), simple_image_new(pixbuf),
943 FALSE, TRUE, 0);
944 g_object_unref(pixbuf);
946 win->label = wrapped_label_new(win->text, 180);
948 update_style(NULL, win, NULL);
950 gtk_box_pack_start(GTK_BOX(vbox), win->label, FALSE, TRUE, 0);
952 gtk_widget_add_events(win->widget,
953 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
954 g_signal_connect(win->widget, "button-press-event",
955 G_CALLBACK(icon_button_press), win);
956 g_signal_connect(win->widget, "motion-notify-event",
957 G_CALLBACK(icon_motion_notify), win);
958 g_signal_connect(win->widget, "button-release-event",
959 G_CALLBACK(button_released), win);
961 gtk_widget_show_all(vbox); /* So the size comes out right */
962 pinboard_add_widget(win->widget, win->text);
963 gtk_widget_show(win->widget);
966 /* A window has been destroyed/expanded -- remove its icon */
967 static void hide_icon(IconWindow *win)
969 g_return_if_fail(win->widget != NULL);
971 gtk_widget_hide(win->widget); /* Stops flicker - stupid GtkFixed! */
972 gtk_widget_destroy(win->widget);
973 win->widget = NULL;
974 win->label = NULL;
977 static void state_changed(IconWindow *win)
979 if (win->iconified)
981 get_icon_name(win);
982 show_icon(win);
984 else
985 hide_icon(win);
988 #if 0
989 /* Set the _NET_WM_ICON_GEOMETRY property, which indicates where this window
990 * will be iconified to. Should be inside a push/pop.
992 static void set_iconify_pos(IconWindow *win)
994 gint32 data[4];
996 data[0] = iconify_next_x;
997 data[1] = iconify_next_y;
998 data[2] = 100;
999 data[3] = 32;
1001 XChangeProperty(gdk_display, win->xwindow,
1002 gdk_x11_atom_to_xatom(xa__NET_WM_ICON_GEOMETRY),
1003 XA_CARDINAL, 32, PropModeReplace, (guchar *) data, 4);
1005 #endif
1007 static void update_style(gpointer key, gpointer data, gpointer user_data)
1009 IconWindow *win = (IconWindow *) data;
1011 if (!win->widget)
1012 return;
1014 widget_modify_font(win->label, pinboard_font);
1015 gtk_widget_modify_fg(win->label, GTK_STATE_NORMAL, &pin_text_fg_col);
1016 gtk_widget_modify_bg(win->label, GTK_STATE_NORMAL, &pin_text_bg_col);
1017 gtk_widget_modify_fg(win->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
1018 gtk_widget_modify_bg(win->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
1021 /* Find out what the new window manager can do... */
1022 static void update_supported(void)
1024 Atom type;
1025 int format;
1026 gulong nitems;
1027 gulong bytes_after;
1028 unsigned char *data;
1029 int err, result;
1030 int i;
1031 gboolean old_supports_hidden = wm_supports_hidden;
1033 wm_supports_hidden = FALSE;
1035 gdk_error_trap_push();
1036 type = None;
1037 result = XGetWindowProperty(gdk_display,
1038 gdk_x11_get_default_root_xwindow(),
1039 gdk_x11_atom_to_xatom(xa__NET_SUPPORTED),
1040 0, G_MAXLONG,
1041 False, XA_ATOM, &type, &format, &nitems,
1042 &bytes_after, &data);
1043 err = gdk_error_trap_pop();
1045 if (err != Success || result != Success)
1046 goto out;
1048 for (i = 0; i < nitems; i++)
1050 GdkAtom atom = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
1052 if (atom == xa__NET_WM_STATE_HIDDEN)
1053 wm_supports_hidden = TRUE;
1056 XFree(data);
1057 out:
1059 if (wm_supports_hidden != old_supports_hidden)
1061 tasklist_update(TRUE);
1062 tasklist_update(FALSE);