Converted README to markdown
[rox-filer.git] / ROX-Filer / src / tasklist.c
blobe67fcb58e3f7d7d0452e5e5d7920816364add731
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_DESKTOP_GEOMETRY = GDK_NONE;
77 static GdkAtom xa__NET_DESKTOP_VIEWPORT = 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 (and viewport) is currently displayed */
101 static int cur_desktop = 0;
102 static GdkRectangle cur_viewport;
104 /****************************************************************
105 * EXTERNAL INTERFACE *
106 ****************************************************************/
108 void tasklist_set_active(gboolean active)
110 static gboolean need_init = TRUE;
111 static gboolean tasklist_active = FALSE;
113 if (active == tasklist_active)
115 if (o_pinboard_tasklist_per_workspace.has_changed && active)
116 tasklist_update(FALSE);
117 return;
119 tasklist_active = active;
121 if (need_init)
123 GdkWindow *root;
125 root = gdk_get_default_root_window();
127 xa__NET_SUPPORTED = gdk_atom_intern("_NET_SUPPORTED", FALSE);
128 xa_WM_STATE = gdk_atom_intern("WM_STATE", FALSE);
129 xa_WM_ICON_NAME = gdk_atom_intern("WM_ICON_NAME", FALSE);
130 xa_WM_NAME = gdk_atom_intern("WM_NAME", FALSE);
131 xa_UTF8_STRING = gdk_atom_intern("UTF8_STRING", FALSE);
132 xa_TEXT = gdk_atom_intern("TEXT", FALSE);
133 xa__NET_CLIENT_LIST =
134 gdk_atom_intern("_NET_CLIENT_LIST", FALSE);
135 xa__NET_WM_VISIBLE_NAME =
136 gdk_atom_intern("_NET_WM_VISIBLE_NAME", FALSE);
137 xa__NET_WM_ICON_NAME =
138 gdk_atom_intern("_NET_WM_ICON_NAME", FALSE);
139 xa__NET_WM_ICON_GEOMETRY =
140 gdk_atom_intern("_NET_WM_ICON_GEOMETRY", FALSE);
141 xa__NET_WM_STATE = gdk_atom_intern("_NET_WM_STATE", FALSE);
142 xa__NET_WM_STATE_HIDDEN =
143 gdk_atom_intern("_NET_WM_STATE_HIDDEN", FALSE);
144 xa__NET_DESKTOP_GEOMETRY = gdk_atom_intern("_NET_DESKTOP_GEOMETRY", FALSE);
145 xa__NET_DESKTOP_VIEWPORT = gdk_atom_intern("_NET_DESKTOP_VIEWPORT", FALSE);
147 known = g_hash_table_new_full((GHashFunc) xid_hash,
148 (GEqualFunc) xid_equal,
149 NULL,
150 (GDestroyNotify) icon_win_free);
151 gdk_window_set_events(root, gdk_window_get_events(root) |
152 GDK_PROPERTY_CHANGE_MASK);
153 need_init = FALSE;
156 if (active)
158 update_current_desktop();
159 gdk_window_add_filter(NULL, window_filter, NULL);
160 update_supported();
162 else
163 gdk_window_remove_filter(NULL, window_filter, NULL);
165 tasklist_update(!active);
168 /* User has changes the colours in the options box... */
169 void tasklist_style_changed(void)
171 if (known)
172 g_hash_table_foreach(known, update_style, NULL);
175 /****************************************************************
176 * INTERNAL FUNCTIONS *
177 ****************************************************************/
179 static void icon_win_free(IconWindow *win)
181 g_return_if_fail(win->widget == NULL);
182 g_return_if_fail(win->label == NULL);
184 if (win->timeout_update)
185 g_source_remove(win->timeout_update);
187 g_free(win->text);
188 g_free(win);
191 /* From gdk */
192 static guint xid_hash(XID *xid)
194 return *xid;
197 /* From gdk */
198 static gboolean xid_equal(XID *a, XID *b)
200 return (*a == *b);
203 static int wincmp(const void *a, const void *b)
205 const Window *aw = a;
206 const Window *bw = b;
208 if (*aw < *bw)
209 return -1;
210 else if (*aw > *bw)
211 return 1;
212 else
213 return 0;
216 /* Read the list of WINDOWs from (xwindow,atom), returning them
217 * in a (sorted) Array of Windows. On error, an empty array is
218 * returned.
219 * Free the array afterwards.
221 static GArray *get_window_list(Window xwindow, GdkAtom atom)
223 GArray *array;
224 Atom type;
225 int format;
226 gulong nitems;
227 gulong bytes_after;
228 unsigned char *data;
229 int err, result;
230 int i;
232 array = g_array_new(FALSE, FALSE, sizeof(Window));
234 gdk_error_trap_push();
235 type = None;
236 result = XGetWindowProperty(gdk_display,
237 xwindow,
238 gdk_x11_atom_to_xatom(atom),
239 0, G_MAXLONG,
240 False, XA_WINDOW, &type, &format, &nitems,
241 &bytes_after, &data);
242 err = gdk_error_trap_pop();
244 if (err != Success || result != Success)
245 return array;
247 if (type == XA_WINDOW)
249 for (i = 0; i < nitems; i++)
250 g_array_append_val(array, ((Window *) data)[i]);
252 if (array->len)
253 g_array_sort(array, wincmp);
256 XFree(data);
258 return array;
261 static guchar *get_str(IconWindow *win, GdkAtom atom)
263 Atom rtype;
264 int format;
265 gulong nitems;
266 gulong bytes_after;
267 unsigned char *data, *str = NULL;
268 int err, result;
270 gdk_error_trap_push();
272 result = XGetWindowProperty(gdk_display, win->xwindow,
273 gdk_x11_atom_to_xatom(atom),
274 0, G_MAXLONG, False,
275 AnyPropertyType,
276 &rtype, &format, &nitems,
277 &bytes_after, &data);
279 err = gdk_error_trap_pop();
281 if (err == Success && result == Success && data)
283 if (*data)
284 str = g_strdup(data);
285 XFree(data);
288 return str;
291 static void get_icon_name(IconWindow *win)
293 null_g_free(&win->text);
295 /* Keep this list in sync with window_filter */
297 win->text = get_str(win, xa__NET_WM_ICON_NAME);
298 if (!win->text)
299 win->text = get_str(win, xa__NET_WM_VISIBLE_NAME);
300 if (!win->text)
301 win->text = get_str(win, xa_WM_ICON_NAME);
302 if (!win->text)
303 win->text = get_str(win, xa_WM_NAME);
304 if (!win->text)
305 win->text = g_strdup(_("Window"));
308 static gboolean update_title(gpointer data)
310 IconWindow *win = (IconWindow *) data;
312 if (!win->widget)
313 return FALSE; /* No longer an icon */
315 get_icon_name(win);
316 wrapped_label_set_text(WRAPPED_LABEL(win->label), win->text);
318 win->timeout_update = 0;
320 return FALSE;
323 /* Call from within error_push/pop
324 * See wnck_window_is_in_viewport */
325 static gboolean within_viewport(Window xwindow)
327 int x, y;
328 unsigned int width, height, bw, depth;
329 Window root_window, child;
330 GdkRectangle win_rect;
332 XGetGeometry (gdk_display,
333 xwindow,
334 &root_window,
335 &x, &y, &width, &height, &bw, &depth);
336 XTranslateCoordinates (gdk_display,
337 xwindow,
338 root_window,
339 0, 0,
340 &x, &y, &child);
342 win_rect.x = x + cur_viewport.x;
343 win_rect.y = y + cur_viewport.y;
344 win_rect.width = width;
345 win_rect.height = height;
347 return gdk_rectangle_intersect (&cur_viewport, &win_rect, &win_rect);
350 /* Call from within error_push/pop */
351 static void window_check_status(IconWindow *win)
353 Atom type;
354 int format;
355 gulong nitems;
356 gulong bytes_after;
357 unsigned char *data;
358 Window transient_for;
359 gboolean iconic = FALSE;
361 if (XGetTransientForHint(gdk_display, win->xwindow, &transient_for) && transient_for)
362 iconic = FALSE;
363 else if (wm_supports_hidden && XGetWindowProperty(gdk_display, win->xwindow,
364 gdk_x11_atom_to_xatom(xa__NET_WM_STATE),
365 0, G_MAXLONG, False,
366 XA_ATOM,
367 &type, &format, &nitems,
368 &bytes_after, &data) == Success && data)
370 GdkAtom state;
371 int i;
373 for (i = 0; i < nitems; i++)
375 state = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
376 if (state == xa__NET_WM_STATE_HIDDEN)
378 iconic = TRUE;
379 break;
382 XFree(data);
384 else if (XGetWindowProperty(gdk_display, win->xwindow,
385 gdk_x11_atom_to_xatom(xa_WM_STATE),
386 0, 1, False,
387 gdk_x11_atom_to_xatom(xa_WM_STATE),
388 &type, &format, &nitems,
389 &bytes_after, &data) == Success && data)
391 iconic = ((guint32 *) data)[0] == 3;
392 XFree(data);
394 else
395 iconic = FALSE;
397 /* Iconified windows on another desktops are not shown */
398 if (o_pinboard_tasklist_per_workspace.int_value &&
399 XGetWindowProperty(gdk_display, win->xwindow,
400 gdk_x11_atom_to_xatom(xa__NET_WM_DESKTOP),
401 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
402 &bytes_after, &data) == Success && data)
404 guint32 desk_on = ((guint32 *) data)[0];
406 if ((desk_on != 0xffffffff) && ((desk_on != cur_desktop) ||
407 !within_viewport(win->xwindow)))
408 iconic = FALSE;
409 XFree(data);
412 if (win->iconified == iconic)
413 return;
415 win->iconified = iconic;
417 state_changed(win);
419 gdk_flush();
422 static void update_current_desktop(void)
424 unsigned char *data;
425 Atom type;
426 int format;
427 gulong nitems;
428 gulong bytes_after;
430 if (XGetWindowProperty(gdk_display, gdk_x11_get_default_root_xwindow(),
431 gdk_x11_atom_to_xatom(xa__NET_CURRENT_DESKTOP),
432 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
433 &bytes_after, (unsigned char **)(&data)) == Success && data) {
434 cur_desktop = ((gint32*)data)[0];
435 XFree(data);
438 if (XGetWindowProperty(gdk_display, gdk_x11_get_default_root_xwindow(),
439 gdk_x11_atom_to_xatom(xa__NET_DESKTOP_VIEWPORT),
440 0, G_MAXLONG, False, XA_CARDINAL, &type, &format,
441 &nitems,
442 &bytes_after, (unsigned char **)(&data)) == Success &&
443 data) {
444 cur_viewport.x = ((gint32*)data)[0];
445 cur_viewport.y = ((gint32*)data)[1];
446 cur_viewport.width = screen_width;
447 cur_viewport.height = screen_height;
448 XFree(data);
452 /* Called for all events on all windows */
453 static GdkFilterReturn window_filter(GdkXEvent *xevent,
454 GdkEvent *event,
455 gpointer data)
457 XEvent *xev = (XEvent *) xevent;
458 IconWindow *w;
460 if (xev->type == PropertyNotify)
462 GdkAtom atom = gdk_x11_xatom_to_atom(xev->xproperty.atom);
463 Window win = ((XPropertyEvent *) xev)->window;
465 if (atom == xa_WM_STATE || atom == xa__NET_WM_STATE)
467 w = g_hash_table_lookup(known, &win);
469 if (w)
471 gdk_error_trap_push();
472 window_check_status(w);
473 if (gdk_error_trap_pop() != Success)
474 g_hash_table_remove(known, &win);
477 else if (atom == xa__NET_CURRENT_DESKTOP ||
478 atom == xa__NET_DESKTOP_VIEWPORT ||
479 atom == xa__NET_DESKTOP_GEOMETRY)
481 update_current_desktop();
482 tasklist_update(FALSE);
484 else if (atom == xa__NET_WM_ICON_NAME ||
485 atom == xa__NET_WM_VISIBLE_NAME ||
486 atom == xa_WM_ICON_NAME ||
487 atom == xa_WM_NAME)
489 /* Keep this list in sync with get_icon_name */
490 w = g_hash_table_lookup(known, &win);
492 if (w && w->widget && !w->timeout_update)
493 w->timeout_update = g_timeout_add(100,
494 update_title, w);
496 else if (atom == xa__NET_CLIENT_LIST)
497 tasklist_update(FALSE);
498 else if (atom == xa__NET_SUPPORTED)
499 update_supported();
502 return GDK_FILTER_CONTINUE;
505 /* Window has been added to list of managed windows */
506 static void add_window(Window win)
508 IconWindow *w;
510 /* g_print("[ New window %ld ]\n", (long) win); */
512 w = g_hash_table_lookup(known, &win);
514 if (!w)
516 XWindowAttributes attr;
518 gdk_error_trap_push();
520 XGetWindowAttributes(gdk_display, win, &attr);
522 if (gdk_error_trap_pop() != Success)
523 return;
524 gdk_error_trap_push();
526 XSelectInput(gdk_display, win, attr.your_event_mask |
527 PropertyChangeMask);
529 gdk_flush();
531 if (gdk_error_trap_pop() != Success)
532 return;
534 w = g_new(IconWindow, 1);
535 w->widget = NULL;
536 w->label = NULL;
537 w->text = NULL;
538 w->xwindow = win;
539 w->iconified = FALSE;
540 w->timeout_update = 0;
542 g_hash_table_insert(known, &w->xwindow, w);
545 gdk_error_trap_push();
547 window_check_status(w);
549 #if 0
550 set_iconify_pos(w);
551 #endif
553 if (gdk_error_trap_pop() != Success)
554 g_hash_table_remove(known, &win);
557 /* Window is no longer managed, but hasn't been destroyed yet */
558 static void remove_window(Window win)
560 IconWindow *w;
562 /* g_print("[ Remove window %ld ]\n", (long) win); */
564 w = g_hash_table_lookup(known, &win);
565 if (w)
567 if (w->iconified)
569 w->iconified = FALSE;
570 state_changed(w);
573 g_hash_table_remove(known, &win);
577 /* Make sure the window list is up-to-date. Call once to start, and then
578 * everytime _NET_CLIENT_LIST changes.
579 * If 'to_empty' is set them pretend all windows have disappeared.
581 static void tasklist_update(gboolean to_empty)
583 static GArray *old_mapping = NULL;
584 GArray *mapping = NULL;
585 int new_i, old_i;
587 if (!old_mapping)
589 old_mapping = g_array_new(FALSE, FALSE, sizeof(Window));
592 if (to_empty)
593 mapping = g_array_new(FALSE, FALSE, sizeof(Window));
594 else
595 mapping = get_window_list(gdk_x11_get_default_root_xwindow(),
596 gdk_atom_intern("_NET_CLIENT_LIST", FALSE));
598 new_i = 0;
599 old_i = 0;
600 while (new_i < mapping->len && old_i < old_mapping->len)
602 Window new = g_array_index(mapping, Window, new_i);
603 Window old = g_array_index(old_mapping, Window, old_i);
605 if (new == old)
607 add_window(new);
608 new_i++;
609 old_i++;
611 else if (new < old)
613 add_window(new);
614 new_i++;
616 else
618 remove_window(old);
619 old_i++;
622 while (new_i < mapping->len)
624 add_window(g_array_index(mapping, Window, new_i));
625 new_i++;
627 while (old_i < old_mapping->len)
629 remove_window(g_array_index(old_mapping, Window, old_i));
630 old_i++;
633 g_array_free(old_mapping, TRUE);
634 old_mapping = mapping;
637 /* Called when the user clicks on the button */
638 static void uniconify(IconWindow *win, guint32 timestamp)
640 XClientMessageEvent sev;
642 sev.type = ClientMessage;
643 sev.display = gdk_display;
644 sev.format = 32;
645 sev.window = win->xwindow;
646 sev.message_type = gdk_x11_atom_to_xatom(
647 gdk_atom_intern("_NET_ACTIVE_WINDOW", FALSE));
648 sev.data.l[0] = 2;
649 sev.data.l[1] = timestamp;
650 sev.data.l[2] = 0;
652 gdk_error_trap_push();
654 XSendEvent(gdk_display, DefaultRootWindow(gdk_display), False,
655 SubstructureNotifyMask | SubstructureRedirectMask,
656 (XEvent *) &sev);
657 XSync(gdk_display, False);
659 gdk_error_trap_pop();
662 static gint drag_start_x = -1;
663 static gint drag_start_y = -1;
664 static gboolean drag_started = FALSE;
665 static gint drag_off_x = -1;
666 static gint drag_off_y = -1;
668 static void icon_button_press(GtkWidget *widget,
669 GdkEventButton *event,
670 IconWindow *win)
672 if (event->button == 1)
674 drag_start_x = event->x_root;
675 drag_start_y = event->y_root;
676 drag_started = FALSE;
678 drag_off_x = event->x;
679 drag_off_y = event->y;
683 static gboolean icon_motion_notify(GtkWidget *widget,
684 GdkEventMotion *event,
685 IconWindow *win)
687 if (event->state & GDK_BUTTON1_MASK)
689 int dx = event->x_root - drag_start_x;
690 int dy = event->y_root - drag_start_y;
692 if (!drag_started)
694 if (abs(dx) < 5 && abs(dy) < 5)
695 return FALSE;
696 drag_started = TRUE;
699 fixed_move_fast(GTK_FIXED(win->widget->parent),
700 win->widget,
701 event->x_root - drag_off_x,
702 event->y_root - drag_off_y);
704 pinboard_moved_widget(win->widget, win->text,
705 event->x_root - drag_off_x,
706 event->y_root - drag_off_y);
709 return FALSE;
712 static void button_released(GtkWidget *widget, GdkEventButton *event,
713 IconWindow *win)
715 if (!drag_started && event->button == 1)
716 uniconify(win, event->time);
719 static GdkColormap* get_cmap(GdkPixmap *pixmap)
721 GdkColormap *cmap;
723 cmap = gdk_drawable_get_colormap(pixmap);
724 if (cmap)
725 g_object_ref(G_OBJECT(cmap));
727 if (cmap == NULL)
729 if (gdk_drawable_get_depth(pixmap) == 1)
731 /* Masks don't need colourmaps */
732 cmap = NULL;
734 else
736 /* Try system cmap */
737 cmap = gdk_colormap_get_system();
738 g_object_ref(G_OBJECT(cmap));
742 /* Be sure we aren't going to blow up due to visual mismatch */
743 if (cmap && (gdk_colormap_get_visual(cmap)->depth !=
744 gdk_drawable_get_depth(pixmap)))
745 cmap = NULL;
747 return cmap;
750 /* Copy a pixmap from the server to a client-side pixbuf */
751 static GdkPixbuf* pixbuf_from_pixmap(Pixmap xpixmap)
753 GdkDrawable *drawable;
754 GdkPixbuf *retval;
755 GdkColormap *cmap;
756 int width, height;
758 retval = NULL;
760 drawable = gdk_xid_table_lookup(xpixmap);
762 if (GDK_IS_DRAWABLE(drawable))
763 g_object_ref(G_OBJECT(drawable));
764 else
766 drawable = gdk_pixmap_foreign_new(xpixmap);
767 if (!GDK_IS_DRAWABLE(drawable))
768 return retval;
771 cmap = get_cmap(drawable);
773 /* GDK is supposed to do this but doesn't in GTK 2.0.2,
774 * fixed in 2.0.3
776 gdk_drawable_get_size(drawable, &width, &height);
778 retval = gdk_pixbuf_get_from_drawable(NULL, drawable, cmap,
779 0, 0, 0, 0, width, height);
781 if (cmap)
782 g_object_unref(G_OBJECT(cmap));
783 g_object_unref(G_OBJECT(drawable));
785 return retval;
788 /* Creates a new masked pixbuf from a non-masked pixbuf and a mask */
789 static GdkPixbuf* apply_mask(GdkPixbuf *pixbuf, GdkPixbuf *mask)
791 int w, h;
792 int i, j;
793 GdkPixbuf *with_alpha;
794 guchar *src;
795 guchar *dest;
796 int src_stride;
797 int dest_stride;
799 w = MIN(gdk_pixbuf_get_width(mask), gdk_pixbuf_get_width(pixbuf));
800 h = MIN(gdk_pixbuf_get_height(mask), gdk_pixbuf_get_height(pixbuf));
802 with_alpha = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
804 dest = gdk_pixbuf_get_pixels(with_alpha);
805 src = gdk_pixbuf_get_pixels(mask);
807 dest_stride = gdk_pixbuf_get_rowstride(with_alpha);
808 src_stride = gdk_pixbuf_get_rowstride(mask);
810 i = 0;
811 while (i < h)
813 j = 0;
814 while (j < w)
816 guchar *s = src + i * src_stride + j * 3;
817 guchar *d = dest + i * dest_stride + j * 4;
819 /* s[0] == s[1] == s[2], they are 255 if the bit was
820 * set, 0 otherwise
822 if (s[0] == 0)
823 d[3] = 0; /* transparent */
824 else
825 d[3] = 255; /* opaque */
827 ++j;
830 ++i;
833 return with_alpha;
836 #define BORDER_X 16
837 #define BORDER_Y 8
838 /* Take the icon that the iconified window has given us and modify it to make
839 * it obvious what it is.
841 static GdkPixbuf *apply_window_effect(GdkPixbuf *src)
843 GdkPixbuf *new;
844 int w, h;
846 w = gdk_pixbuf_get_width(src);
847 h = gdk_pixbuf_get_height(src);
849 new = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(src), TRUE,
850 8, w + BORDER_X * 2, h + BORDER_Y * 2);
851 gdk_pixbuf_fill(new, 0x88888888);
853 gdk_pixbuf_composite(src, new,
854 BORDER_X, BORDER_Y, w, h,
855 BORDER_X, BORDER_Y, 1, 1,
856 GDK_INTERP_NEAREST, 255);
858 return new;
861 /* Return a suitable icon for this window. unref the result.
862 * Never returns NULL.
864 static GdkPixbuf *get_image_for(IconWindow *win)
866 static MaskedPixmap *default_icon = NULL;
867 Pixmap pixmap = None;
868 Pixmap mask = None;
869 XWMHints *hints;
870 GdkPixbuf *retval = NULL;
872 /* Try the pixmap and mask in the old WMHints... */
873 gdk_error_trap_push();
874 hints = XGetWMHints(gdk_display, win->xwindow);
876 if (hints)
878 if (hints->flags & IconPixmapHint)
879 pixmap = hints->icon_pixmap;
880 if (hints->flags & IconMaskHint)
881 mask = hints->icon_mask;
883 XFree(hints);
884 hints = NULL;
887 if (pixmap != None)
889 GdkPixbuf *mask_pb = NULL;
891 retval = pixbuf_from_pixmap(pixmap);
893 if (retval && mask != None)
894 mask_pb = pixbuf_from_pixmap(mask);
896 if (mask_pb)
898 GdkPixbuf *masked;
900 masked = apply_mask(retval, mask_pb);
901 g_object_unref(G_OBJECT(mask_pb));
903 if (masked)
905 g_object_unref(G_OBJECT(retval));
906 retval = masked;
911 gdk_flush();
913 gdk_error_trap_pop();
915 if (!retval)
917 if (!default_icon)
918 default_icon = load_pixmap("iconified");
920 retval = default_icon->pixbuf;
921 g_object_ref(retval);
924 /* Apply a special effect to make this look different from normal
925 * pinboard icons.
928 GdkPixbuf *old = retval;
929 GdkPixbuf *small;
930 small = scale_pixbuf(old, ICON_WIDTH, ICON_HEIGHT);
931 g_object_unref(old);
932 retval = apply_window_effect(small);
933 g_object_unref(small);
936 return retval;
939 /* Stop the button from highlighting */
940 static gint icon_expose(GtkWidget *widget, GdkEventExpose *event,
941 IconWindow *win)
943 static GtkWidgetClass *parent_class = NULL;
945 g_return_val_if_fail(win != NULL, TRUE);
946 g_return_val_if_fail(win->label != NULL, TRUE);
948 if (!parent_class)
950 gpointer c = ((GTypeInstance *) widget)->g_class;
951 parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
954 draw_label_shadow((WrappedLabel *) win->label, event->region);
956 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
957 event->region);
958 (parent_class->expose_event)(widget, event);
959 gdk_gc_set_clip_region(win->label->style->fg_gc[win->label->state],
960 NULL);
962 /* Stop the button effect */
963 return TRUE;
966 /* A window has been iconified -- display it on the screen */
967 static void show_icon(IconWindow *win)
969 GdkPixbuf *pixbuf;
970 GtkWidget *vbox;
972 g_return_if_fail(win->widget == NULL);
973 g_return_if_fail(win->label == NULL);
975 win->widget = gtk_button_new();
976 gtk_button_set_relief(GTK_BUTTON(win->widget), GTK_RELIEF_NONE);
977 g_signal_connect(win->widget, "expose-event",
978 G_CALLBACK(icon_expose), win);
979 vbox = gtk_vbox_new(FALSE, 0);
980 gtk_container_add(GTK_CONTAINER(win->widget), vbox);
982 pixbuf = get_image_for(win);
984 gtk_box_pack_start(GTK_BOX(vbox), simple_image_new(pixbuf),
985 FALSE, TRUE, 0);
986 g_object_unref(pixbuf);
988 win->label = wrapped_label_new(win->text, 180);
990 update_style(NULL, win, NULL);
992 gtk_box_pack_start(GTK_BOX(vbox), win->label, FALSE, TRUE, 0);
994 gtk_widget_add_events(win->widget,
995 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
996 g_signal_connect(win->widget, "button-press-event",
997 G_CALLBACK(icon_button_press), win);
998 g_signal_connect(win->widget, "motion-notify-event",
999 G_CALLBACK(icon_motion_notify), win);
1000 g_signal_connect(win->widget, "button-release-event",
1001 G_CALLBACK(button_released), win);
1003 gtk_widget_show_all(vbox); /* So the size comes out right */
1004 pinboard_add_widget(win->widget, win->text);
1005 gtk_widget_show(win->widget);
1008 /* A window has been destroyed/expanded -- remove its icon */
1009 static void hide_icon(IconWindow *win)
1011 g_return_if_fail(win->widget != NULL);
1013 gtk_widget_hide(win->widget); /* Stops flicker - stupid GtkFixed! */
1014 gtk_widget_destroy(win->widget);
1015 win->widget = NULL;
1016 win->label = NULL;
1019 static void state_changed(IconWindow *win)
1021 if (win->iconified)
1023 get_icon_name(win);
1024 show_icon(win);
1026 else
1027 hide_icon(win);
1030 #if 0
1031 /* Set the _NET_WM_ICON_GEOMETRY property, which indicates where this window
1032 * will be iconified to. Should be inside a push/pop.
1034 static void set_iconify_pos(IconWindow *win)
1036 gint32 data[4];
1038 data[0] = iconify_next_x;
1039 data[1] = iconify_next_y;
1040 data[2] = 100;
1041 data[3] = 32;
1043 XChangeProperty(gdk_display, win->xwindow,
1044 gdk_x11_atom_to_xatom(xa__NET_WM_ICON_GEOMETRY),
1045 XA_CARDINAL, 32, PropModeReplace, (guchar *) data, 4);
1047 #endif
1049 static void update_style(gpointer key, gpointer data, gpointer user_data)
1051 IconWindow *win = (IconWindow *) data;
1053 if (!win->widget)
1054 return;
1056 widget_modify_font(win->label, pinboard_font);
1057 gtk_widget_modify_fg(win->label, GTK_STATE_NORMAL, &pin_text_fg_col);
1058 gtk_widget_modify_bg(win->label, GTK_STATE_NORMAL, &pin_text_bg_col);
1059 gtk_widget_modify_fg(win->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
1060 gtk_widget_modify_bg(win->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
1063 /* Find out what the new window manager can do... */
1064 static void update_supported(void)
1066 Atom type;
1067 int format;
1068 gulong nitems;
1069 gulong bytes_after;
1070 unsigned char *data;
1071 int err, result;
1072 int i;
1073 gboolean old_supports_hidden = wm_supports_hidden;
1075 wm_supports_hidden = FALSE;
1077 gdk_error_trap_push();
1078 type = None;
1079 result = XGetWindowProperty(gdk_display,
1080 gdk_x11_get_default_root_xwindow(),
1081 gdk_x11_atom_to_xatom(xa__NET_SUPPORTED),
1082 0, G_MAXLONG,
1083 False, XA_ATOM, &type, &format, &nitems,
1084 &bytes_after, &data);
1085 err = gdk_error_trap_pop();
1087 if (err != Success || result != Success)
1088 goto out;
1090 for (i = 0; i < nitems; i++)
1092 GdkAtom atom = gdk_x11_xatom_to_atom(((Atom *) data)[i]);
1094 if (atom == xa__NET_WM_STATE_HIDDEN)
1095 wm_supports_hidden = TRUE;
1098 XFree(data);
1099 out:
1101 if (wm_supports_hidden != old_supports_hidden)
1103 tasklist_update(TRUE);
1104 tasklist_update(FALSE);