4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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)
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
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.
34 #include <X11/Xatom.h>
40 #include "gui_support.h"
45 /* There is one of these for each window controlled by the window
46 * manager (all tasks) in the _NET_CLIENT_LIST property.
48 typedef struct _IconWindow IconWindow
;
51 GtkWidget
*widget
; /* Widget used for icon when iconified */
58 static GdkAtom xa_WM_STATE
= GDK_NONE
;
59 static GdkAtom xa_WM_NAME
= GDK_NONE
;
60 static GdkAtom xa_WM_ICON_NAME
= GDK_NONE
;
61 static GdkAtom xa_UTF8_STRING
= GDK_NONE
;
62 static GdkAtom xa_TEXT
= GDK_NONE
;
63 static GdkAtom xa__NET_WM_VISIBLE_NAME
= GDK_NONE
;
64 static GdkAtom xa__NET_WM_ICON_NAME
= GDK_NONE
;
65 static GdkAtom xa__NET_CLIENT_LIST
= GDK_NONE
;
66 static GdkAtom xa__NET_WM_ICON_GEOMETRY
= GDK_NONE
;
68 /* We have selected destroy and property events on every window in
71 static GHashTable
*known
= NULL
; /* XID -> IconWindow */
73 /* Top-left corner of next icon to be created */
74 static int iconify_next_x
= 0;
75 static int iconify_next_y
= 0;
77 /* Static prototypes */
78 static void remove_window(Window win
);
79 static void tasklist_update(gboolean to_empty
);
80 static GdkFilterReturn
window_filter(GdkXEvent
*xevent
,
83 static guint
xid_hash(XID
*xid
);
84 static gboolean
xid_equal(XID
*a
, XID
*b
);
85 static void state_changed(IconWindow
*win
);
86 static void show_icon(IconWindow
*win
);
87 static void icon_win_free(IconWindow
*win
);
88 static void set_iconify_pos(IconWindow
*win
);
89 static void update_style(gpointer key
, gpointer data
, gpointer user_data
);
91 /****************************************************************
92 * EXTERNAL INTERFACE *
93 ****************************************************************/
95 void tasklist_set_active(gboolean active
)
97 static gboolean need_init
= TRUE
;
98 static gboolean tasklist_active
= FALSE
;
100 if (active
== tasklist_active
)
102 tasklist_active
= active
;
108 root
= gdk_get_default_root_window();
110 xa_WM_STATE
= gdk_atom_intern("WM_STATE", FALSE
);
111 xa_WM_ICON_NAME
= gdk_atom_intern("WM_ICON_NAME", FALSE
);
112 xa_WM_NAME
= gdk_atom_intern("WM_NAME", FALSE
);
113 xa_UTF8_STRING
= gdk_atom_intern("UTF8_STRING", FALSE
);
114 xa_TEXT
= gdk_atom_intern("TEXT", FALSE
);
115 xa__NET_CLIENT_LIST
=
116 gdk_atom_intern("_NET_CLIENT_LIST", FALSE
);
117 xa__NET_WM_VISIBLE_NAME
=
118 gdk_atom_intern("_NET_WM_VISIBLE_NAME", FALSE
);
119 xa__NET_WM_ICON_NAME
=
120 gdk_atom_intern("_NET_WM_ICON_NAME", FALSE
);
121 xa__NET_WM_ICON_GEOMETRY
=
122 gdk_atom_intern("_NET_WM_ICON_GEOMETRY", FALSE
);
124 known
= g_hash_table_new_full((GHashFunc
) xid_hash
,
125 (GEqualFunc
) xid_equal
,
127 (GDestroyNotify
) icon_win_free
);
128 gdk_window_set_events(root
, gdk_window_get_events(root
) |
129 GDK_PROPERTY_CHANGE_MASK
);
134 gdk_window_add_filter(NULL
, window_filter
, NULL
);
136 gdk_window_remove_filter(NULL
, window_filter
, NULL
);
138 tasklist_update(!active
);
141 /* User has changes the colours in the options box... */
142 void tasklist_style_changed(void)
144 g_hash_table_foreach(known
, update_style
, NULL
);
147 /****************************************************************
148 * INTERNAL FUNCTIONS *
149 ****************************************************************/
151 static void icon_win_free(IconWindow
*win
)
153 g_return_if_fail(win
->widget
== NULL
);
154 g_return_if_fail(win
->label
== NULL
);
162 static guint
xid_hash(XID
*xid
)
168 static gboolean
xid_equal(XID
*a
, XID
*b
)
173 static int wincmp(const void *a
, const void *b
)
175 const Window
*aw
= a
;
176 const Window
*bw
= b
;
186 /* Read the list of WINDOWs from (xwindow,atom), returning them
187 * in a (sorted) Array of Windows. On error, an empty array is
189 * Free the array afterwards.
191 static GArray
*get_window_list(Window xwindow
, GdkAtom atom
)
202 array
= g_array_new(FALSE
, FALSE
, sizeof(Window
));
204 gdk_error_trap_push();
206 result
= XGetWindowProperty(gdk_display
,
208 gdk_x11_atom_to_xatom(atom
),
210 False
, XA_WINDOW
, &type
, &format
, &nitems
,
211 &bytes_after
, (guchar
**)&data
);
212 err
= gdk_error_trap_pop();
214 if (err
!= Success
|| result
!= Success
)
217 if (type
== XA_WINDOW
)
219 for (i
= 0; i
< nitems
; i
++)
220 g_array_append_val(array
, data
[i
]);
223 g_array_sort(array
, wincmp
);
231 static gchar
*get_str(IconWindow
*win
, GdkAtom atom
)
237 char *data
, *str
= NULL
;
239 if (XGetWindowProperty(gdk_display
, win
->xwindow
,
240 gdk_x11_atom_to_xatom(atom
),
243 &rtype
, &format
, &nitems
,
244 &bytes_after
, (guchar
**) &data
) == Success
&& data
)
247 str
= g_strdup(data
);
254 static void get_icon_name(IconWindow
*win
)
262 win
->text
= get_str(win
, xa__NET_WM_ICON_NAME
);
264 win
->text
= get_str(win
, xa__NET_WM_VISIBLE_NAME
);
266 win
->text
= get_str(win
, xa_WM_ICON_NAME
);
268 win
->text
= get_str(win
, xa_WM_NAME
);
270 win
->text
= g_strdup(_("Window"));
273 /* Call from within error_push/pop */
274 static void window_check_status(IconWindow
*win
)
283 if (XGetWindowProperty(gdk_display
, win
->xwindow
,
284 gdk_x11_atom_to_xatom(xa_WM_STATE
),
286 gdk_x11_atom_to_xatom(xa_WM_STATE
),
287 &type
, &format
, &nitems
,
288 &bytes_after
, (guchar
**) &data
) == Success
&& data
)
290 iconic
= data
[0] == 3;
296 if (win
->iconified
== iconic
)
299 win
->iconified
= iconic
;
304 /* Called for all events on all windows */
305 static GdkFilterReturn
window_filter(GdkXEvent
*xevent
,
309 XEvent
*xev
= (XEvent
*) xevent
;
312 if (xev
->type
== PropertyNotify
)
314 GdkAtom atom
= gdk_x11_xatom_to_atom(xev
->xproperty
.atom
);
315 Window win
= ((XPropertyEvent
*) xev
)->window
;
317 if (atom
== xa_WM_STATE
)
319 w
= g_hash_table_lookup(known
, &win
);
323 gdk_error_trap_push();
324 window_check_status(w
);
325 if (gdk_error_trap_pop() != Success
)
326 g_hash_table_remove(known
, &win
);
330 if (atom
== xa__NET_CLIENT_LIST
)
331 tasklist_update(FALSE
);
334 return GDK_FILTER_CONTINUE
;
337 /* Window has been added to list of managed windows */
338 static void add_window(Window win
)
342 /* g_print("[ New window %ld ]\n", (long) win); */
344 w
= g_hash_table_lookup(known
, &win
);
348 XWindowAttributes attr
;
350 gdk_error_trap_push();
352 XGetWindowAttributes(gdk_display
, win
, &attr
);
354 XSelectInput(gdk_display
, win
, attr
.your_event_mask
|
357 if (gdk_error_trap_pop() != Success
)
360 w
= g_new(IconWindow
, 1);
365 w
->iconified
= FALSE
;
367 g_hash_table_insert(known
, &w
->xwindow
, w
);
370 gdk_error_trap_push();
372 window_check_status(w
);
376 if (gdk_error_trap_pop() != Success
)
377 g_hash_table_remove(known
, &win
);
380 /* Window is no longer managed, but hasn't been destroyed yet */
381 static void remove_window(Window win
)
385 /* g_print("[ Remove window %ld ]\n", (long) win); */
387 w
= g_hash_table_lookup(known
, &win
);
392 w
->iconified
= FALSE
;
396 g_hash_table_remove(known
, &win
);
400 /* Make sure the window list is up-to-date. Call once to start, and then
401 * everytime _NET_CLIENT_LIST changes.
402 * If 'to_empty' is set them pretend all windows have disappeared.
404 static void tasklist_update(gboolean to_empty
)
406 static GArray
*old_mapping
= NULL
;
407 GArray
*mapping
= NULL
;
412 old_mapping
= g_array_new(FALSE
, FALSE
, sizeof(Window
));
416 mapping
= g_array_new(FALSE
, FALSE
, sizeof(Window
));
418 mapping
= get_window_list(gdk_x11_get_default_root_xwindow(),
419 gdk_atom_intern("_NET_CLIENT_LIST", FALSE
));
423 while (new_i
< mapping
->len
&& old_i
< old_mapping
->len
)
425 Window
new = g_array_index(mapping
, Window
, new_i
);
426 Window old
= g_array_index(old_mapping
, Window
, old_i
);
444 while (new_i
< mapping
->len
)
446 add_window(g_array_index(mapping
, Window
, new_i
));
449 while (old_i
< old_mapping
->len
)
451 remove_window(g_array_index(old_mapping
, Window
, old_i
));
455 g_array_free(old_mapping
, TRUE
);
456 old_mapping
= mapping
;
459 /* Called when the user clicks on the button */
460 static void uniconify(IconWindow
*win
)
462 XClientMessageEvent sev
;
464 sev
.type
= ClientMessage
;
465 sev
.display
= gdk_display
;
467 sev
.window
= win
->xwindow
;
468 sev
.message_type
= gdk_x11_atom_to_xatom(
469 gdk_atom_intern("_NET_ACTIVE_WINDOW", FALSE
));
472 gdk_error_trap_push();
474 XSendEvent(gdk_display
, DefaultRootWindow(gdk_display
), False
,
475 SubstructureNotifyMask
| SubstructureRedirectMask
,
477 XSync (gdk_display
, False
);
479 gdk_error_trap_pop();
482 static gint drag_start_x
= -1;
483 static gint drag_start_y
= -1;
484 static gboolean drag_started
= FALSE
;
485 static gint drag_off_x
= -1;
486 static gint drag_off_y
= -1;
488 static void icon_button_press(GtkWidget
*widget
,
489 GdkEventButton
*event
,
492 if (event
->button
== 1)
494 drag_start_x
= event
->x_root
;
495 drag_start_y
= event
->y_root
;
496 drag_started
= FALSE
;
498 drag_off_x
= event
->x
;
499 drag_off_y
= event
->y
;
503 static gboolean
icon_motion_notify(GtkWidget
*widget
,
504 GdkEventMotion
*event
,
507 if (event
->state
& GDK_BUTTON1_MASK
)
509 int dx
= event
->x_root
- drag_start_x
;
510 int dy
= event
->y_root
- drag_start_y
;
514 if (abs(dx
) < 5 && abs(dy
) < 5)
519 fixed_move_fast(GTK_FIXED(win
->widget
->parent
),
521 event
->x_root
- drag_off_x
,
522 event
->y_root
- drag_off_y
);
528 static void button_released(GtkWidget
*widget
, IconWindow
*win
)
534 /* A window has been iconified -- display it on the screen */
535 static void show_icon(IconWindow
*win
)
537 static MaskedPixmap
*icon
= NULL
;
541 g_return_if_fail(win
->widget
== NULL
);
542 g_return_if_fail(win
->label
== NULL
);
545 icon
= load_pixmap("images/iconified.png");
547 win
->widget
= gtk_button_new();
548 vbox
= gtk_vbox_new(FALSE
, 0);
549 gtk_container_add(GTK_CONTAINER(win
->widget
), vbox
);
551 gtk_box_pack_start(GTK_BOX(vbox
),
552 gtk_image_new_from_pixbuf(icon
->pixbuf
),
555 gtk_button_set_relief(GTK_BUTTON(win
->widget
), GTK_RELIEF_NONE
);
557 win
->label
= gtk_label_new(win
->text
);
559 update_style(NULL
, win
, NULL
);
561 gtk_box_pack_start(GTK_BOX(vbox
), win
->label
, FALSE
, TRUE
, 0);
563 gtk_widget_add_events(win
->widget
, GDK_BUTTON1_MOTION_MASK
);
564 g_signal_connect(win
->widget
, "button-press-event",
565 G_CALLBACK(icon_button_press
), win
);
566 g_signal_connect(win
->widget
, "motion-notify-event",
567 G_CALLBACK(icon_motion_notify
), win
);
568 g_signal_connect(win
->widget
, "released",
569 G_CALLBACK(button_released
), win
);
571 pinboard_add_widget(win
->widget
, iconify_next_x
, iconify_next_y
);
572 gtk_widget_show_all(win
->widget
);
573 gtk_widget_size_request(win
->widget
, &req
);
575 iconify_next_y
+= req
.height
;
576 if (iconify_next_y
+ req
.height
> screen_height
)
580 /* A window has been destroyed/expanded -- remove its icon */
581 static void hide_icon(IconWindow
*win
)
583 GtkWidget
*widget
= win
->widget
;
585 g_return_if_fail(widget
!= NULL
);
589 gtk_widget_destroy(widget
);
592 static void state_changed(IconWindow
*win
)
603 /* Set the _NET_WM_ICON_GEOMETRY property, which indicates where this window
604 * will be iconified to. Should be inside a push/pop.
606 static void set_iconify_pos(IconWindow
*win
)
610 data
[0] = iconify_next_x
;
611 data
[1] = iconify_next_y
;
615 XChangeProperty(gdk_display
, win
->xwindow
,
616 gdk_x11_atom_to_xatom(xa__NET_WM_ICON_GEOMETRY
),
617 XA_CARDINAL
, 32, PropModeReplace
, (guchar
*) data
, 4);
620 static void update_style(gpointer key
, gpointer data
, gpointer user_data
)
622 IconWindow
*win
= (IconWindow
*) data
;
626 gtk_widget_modify_fg(win
->label
,
627 GTK_STATE_NORMAL
, &pin_text_fg_col
);
628 gtk_widget_modify_bg(win
->label
,
629 GTK_STATE_NORMAL
, &pin_text_bg_col
);