source: trunk/third/gnome-core/panel/gwmh.c @ 15821

Revision 15821, 66.0 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15820, which included commits to RCS files with non-trunk default branches.
Line 
1/* gwmh.c - GNOME WM interaction helper functions
2 * Copyright (C) 1999 Tim Janik
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
17 *
18 * this code is heavily based on the original gnomepager_applet
19 * implementation of The Rasterman (Carsten Haitzler) <raster@rasterman.com>
20 */
21#include <gwmh.h>
22
23#include <gstc.h>
24#include <gdk/gdkx.h>
25
26#include <X11/Xmd.h>
27#include <X11/Xlib.h>
28#include <X11/Xatom.h>
29
30
31/* --- defines --- */
32#define TASK_XROOT(task)           ((task)->sroot ? GSTC_PARENT_XWINDOW ((task)->sroot) : 0)
33#define GDK_WINDOW_IS_FOREIGN(w)   (((GdkWindowPrivate*) (w))->window_type == GDK_WINDOW_FOREIGN)
34#define GDK_WINDOW_IS_DESTROYED(w) (((GdkWindowPrivate*) (w))->destroyed)
35
36
37/* --- preinitialized Atoms --- */
38gulong GWMHA_WIN_SUPPORTING_WM_CHECK = 0;
39gulong GWMHA_WIN_PROTOCOLS = 0;
40gulong GWMHA_WIN_LAYER = 0;
41gulong GWMHA_WIN_STATE = 0;
42gulong GWMHA_WIN_HINTS = 0;
43gulong GWMHA_WIN_APP_STATE = 0;
44gulong GWMHA_WIN_EXPANDED_SIZE = 0;
45gulong GWMHA_WIN_ICONS = 0;
46gulong GWMHA_WIN_WORKSPACE = 0;
47gulong GWMHA_WIN_WORKSPACE_COUNT = 0;
48gulong GWMHA_WIN_WORKSPACE_NAMES = 0;
49gulong GWMHA_WIN_CLIENT_LIST = 0;
50gulong GWMHA_WIN_AREA = 0;
51gulong GWMHA_WIN_AREA_COUNT = 0;
52gulong GWMHA_WIN_UNIFIED_AREA = 0;
53static gulong XA_WM_STATE = 0;
54static gulong XA_WM_PROTOCOLS = 0;
55static gulong XA_WM_DELETE_WINDOW = 0;
56static gulong XA_WM_TAKE_FOCUS = 0;
57static gulong XA_ENLIGHTENMENT_DESKTOP = 0;
58static gulong XA_KWM_WIN_ICON = 0;
59static gulong XA_COMPOUND_TEXT = 0;
60
61static const struct {
62  gulong      *atom;
63  const gchar *atom_name;
64} gwmh_atoms[] = {
65  { &GWMHA_WIN_PROTOCOLS,               "_WIN_PROTOCOLS", },
66  { &GWMHA_WIN_EXPANDED_SIZE,           "_WIN_EXPANDED_SIZE", },
67  { &GWMHA_WIN_ICONS,                   "_WIN_ICONS", },
68  { &GWMHA_WIN_WORKSPACE,               "_WIN_WORKSPACE", },
69  { &GWMHA_WIN_WORKSPACE_COUNT,         "_WIN_WORKSPACE_COUNT", },
70  { &GWMHA_WIN_WORKSPACE_NAMES,         "_WIN_WORKSPACE_NAMES", },
71  { &GWMHA_WIN_CLIENT_LIST,             "_WIN_CLIENT_LIST", },
72  { &GWMHA_WIN_AREA,                    "_WIN_AREA", },
73  { &GWMHA_WIN_AREA_COUNT,              "_WIN_AREA_COUNT", },
74  { &GWMHA_WIN_UNIFIED_AREA,            "_WIN_UNIFIED_AREA", },
75#define N_GWMHA_IGNORE_ATOMS (10)
76  { &GWMHA_WIN_LAYER,                   "_WIN_LAYER", },
77  { &GWMHA_WIN_APP_STATE,               "_WIN_APP_STATE", },
78  { &GWMHA_WIN_STATE,                   "_WIN_STATE", },
79  { &GWMHA_WIN_HINTS,                   "_WIN_HINTS", },
80  { &GWMHA_WIN_SUPPORTING_WM_CHECK,     "_WIN_SUPPORTING_WM_CHECK", },
81  { &XA_WM_STATE,                       "WM_STATE", },
82  { &XA_WM_PROTOCOLS,                   "WM_PROTOCOLS", },
83  { &XA_WM_DELETE_WINDOW,               "WM_DELETE_WINDOW", },
84  { &XA_WM_TAKE_FOCUS,                  "WM_TAKE_FOCUS", },
85  { &XA_ENLIGHTENMENT_DESKTOP,          "ENLIGHTENMENT_DESKTOP", },
86  { &XA_KWM_WIN_ICON,                   "KWM_WIN_ICON", },
87  { &XA_COMPOUND_TEXT,                  "COMPOUND_TEXT", },
88};
89
90
91/* --- prototypes --- */
92static gpointer         get_typed_property_data   (Display     *xdisplay,
93                                                   Window       xwindow,
94                                                   Atom         property,
95                                                   Atom         requested_type,
96                                                   gint        *size_p,
97                                                   guint        expected_format);
98static gboolean         wm_protocol_check_support (Window     xwin,
99                                                   Atom       check_atom);
100static gboolean         send_client_message_2L    (Window     recipient,
101                                                   Window     event_window,
102                                                   Atom       message_type,
103                                                   long       event_mask,
104                                                   glong      long1,
105                                                   glong      long2);
106static gboolean         send_client_message_3L    (Window     recipient,
107                                                   Window     event_window,
108                                                   Atom       message_type,
109                                                   long       event_mask,
110                                                   glong      long1,
111                                                   glong      long2,
112                                                   glong      long3);
113static gboolean         send_client_message_5L    (Window     recipient,
114                                                   Window     event_window,
115                                                   Atom       message_type,
116                                                   long       event_mask,
117                                                   glong      long1,
118                                                   glong      long2,
119                                                   glong      long3,
120                                                   glong      long4,
121                                                   glong      long5);
122static void             get_task_root_and_frame   (GwmhTask    *task);
123static GdkWindow*       gdk_window_ref_from_xid   (Window       xwin);
124static guint            gwmh_property_atom2info   (Atom         atom,
125                                                   gboolean     per_task);
126static void             gwmh_task_queue_full      (GwmhTask          *task,
127                                                   GwmhTaskInfoMask   imask,
128                                                   GwmhTaskInfoMask   notify_mask);
129static void             gwmh_desk_update          (GwmhDeskInfoMask   imask);
130static gboolean         gwmh_task_update          (GwmhTask          *task,
131                                                   GwmhTaskInfoMask   imask,
132                                                   gboolean           skip_notify);
133static void             gwmh_desk_notify          (GwmhDeskInfoMask   imask);
134static void             gwmh_task_notify          (GwmhTask          *task,
135                                                   GwmhTaskNotifyType ntype,
136                                                   GwmhTaskInfoMask   imask);
137static GwmhTask*        task_new                  (GdkWindow          *gdkwindow);
138static void             task_delete               (GwmhTask          *task);
139static gboolean         client_list_sync          (guint32           *xwindows_ids,
140                                                   guint              n_ids);
141static GdkFilterReturn  task_event_monitor        (GdkXEvent         *gdk_xevent,
142                                                   GdkEvent          *event,
143                                                   gpointer           task_pointer);
144static GdkFilterReturn  task_event_monitor_frame_wrapper (GdkXEvent*, GdkEvent*, gpointer);
145static GdkFilterReturn  root_event_monitor        (GdkXEvent         *gdk_xevent,
146                                                   GdkEvent          *event,
147                                                   gpointer           gdk_root);
148static guint32*         hack_a_client_list        (Display           *display,
149                                                   Window             xroot,
150                                                   guint             *_n_clients);
151
152
153/* --- variables --- */
154static Window           gwmh_gnome_wm_win = None;
155static gboolean         gwmh_need_reboot = FALSE;
156static GHookList        gwmh_desk_hook_list = { 0, };
157static GHookList        gwmh_task_hook_list = { 0, };
158static GSList          *gwmh_task_update_queue = NULL;
159static guint            gwmh_syncs_frozen = 0;
160static guint            gwmh_idle_handler_id = 0;
161static GwmhDeskInfoMask gwmh_desk_update_queued = 0;
162static guint           *gwmh_harea_cache = NULL;
163static guint           *gwmh_varea_cache = NULL;
164static GwmhDesk         gwmh_desk = {
165  0     /* n_desktops */,
166  0     /* n_hareas */,
167  0     /* n_vareas */,
168  NULL  /* desktop_names */,
169  0     /* current_desktop */,
170  0     /* current_harea */,
171  0     /* current_varea */,
172  NULL  /* client_list */,
173  FALSE /* detected_gnome_wm */,
174  FALSE /* unified_area */,
175};
176static gboolean         gwmh_hack_think_unified_area = FALSE;
177static gboolean         gwmh_hack_violate_client_msg = TRUE;
178
179
180/* --- functions --- */
181static inline gint
182gwmh_string_equals (const gchar *string1,
183                    const gchar *string2)
184{
185  if (string1 && string2)
186    return strcmp (string1, string2) == 0;
187  else
188    return string1 == string2;
189}
190
191static inline void
192gwmh_sync (void)
193{
194  if (!gwmh_syncs_frozen)
195    gdk_flush ();
196}
197
198static inline void
199gwmh_thaw_syncs (void)
200{
201  if (gwmh_syncs_frozen)
202    gwmh_syncs_frozen--;
203  gwmh_sync ();
204}
205
206static inline void
207gwmh_freeze_syncs (void)
208{
209  gwmh_syncs_frozen++;
210}
211
212gboolean
213gwmh_init (void)
214{
215  static volatile guint n_init_calls = 0;
216  XWindowAttributes attribs = { 0, };
217  guint32 *property_data;
218  gint size = 0;
219  guint i;
220
221  n_init_calls++;
222  if (n_init_calls == 1)
223    {
224      GdkWindow *window;
225
226      /* initialize hook lists */
227      g_hook_list_init (&gwmh_desk_hook_list, sizeof (GHook));
228      g_hook_list_init (&gwmh_task_hook_list, sizeof (GHook));
229     
230      /* setup the root window event monitor */
231      window = gdk_window_ref_from_xid (GDK_ROOT_WINDOW ());
232      if (!window)
233        g_error (G_GNUC_PRETTY_FUNCTION "(): root window id %ld invalid? bad bad...",
234                 GDK_ROOT_WINDOW ());
235      gdk_window_add_filter (window,
236                             root_event_monitor,
237                             window);
238    }
239
240  /* *first* kill remaining tasks with notification */
241  while (gwmh_desk.client_list)
242    task_delete (gwmh_desk.client_list->data);
243
244  /* initialize desk structure */
245  for (i = 0; i < gwmh_desk.n_desktops; i++)
246    g_free (gwmh_desk.desktop_names[i]);
247  gwmh_desk.n_desktops = 1;
248  gwmh_desk.n_hareas = 1;
249  gwmh_desk.n_vareas = 1;
250  gwmh_desk.desktop_names = g_renew (gchar*,
251                                     gwmh_desk.desktop_names,
252                                     gwmh_desk.n_desktops);
253  gwmh_harea_cache = g_renew (guint,
254                              gwmh_harea_cache,
255                              gwmh_desk.n_desktops);
256  gwmh_varea_cache = g_renew (guint,
257                              gwmh_varea_cache,
258                              gwmh_desk.n_desktops);
259  for (i = 0; i < gwmh_desk.n_desktops; i++)
260    {
261      gwmh_desk.desktop_names[i] = NULL;
262      gwmh_harea_cache[i] = 0;
263      gwmh_varea_cache[i] = 0;
264    }
265  gwmh_desk.current_desktop = 0;
266  gwmh_desk.current_harea = 0;
267  gwmh_desk.current_varea = 0;
268  gwmh_desk.detected_gnome_wm = FALSE;
269  gwmh_desk.unified_area = FALSE;
270
271  /* setup preinitialized atoms */
272  for (i = 0; i < sizeof (gwmh_atoms) / sizeof (gwmh_atoms[0]); i++)
273    *gwmh_atoms[i].atom = gdk_atom_intern (gwmh_atoms[i].atom_name, FALSE);
274 
275  gdk_error_trap_push ();
276
277  /* set event mask for events on root window */
278  XGetWindowAttributes (GDK_DISPLAY (),
279                        GDK_ROOT_WINDOW (),
280                        &attribs);
281  XSelectInput (GDK_DISPLAY (),
282                GDK_ROOT_WINDOW (),
283                attribs.your_event_mask |
284                StructureNotifyMask |
285                PropertyChangeMask);
286  gdk_flush ();
287 
288  /* check for a GNOME window manager */
289  gwmh_gnome_wm_win = None;
290  property_data = get_typed_property_data (GDK_DISPLAY (),
291                                           GDK_ROOT_WINDOW (),
292                                           GWMHA_WIN_SUPPORTING_WM_CHECK,
293                                           XA_CARDINAL,
294                                           &size, 32);
295  if (property_data)
296    {
297      Window check_window = property_data[0];
298      guint32 *wm_check_data;
299     
300      wm_check_data = get_typed_property_data (GDK_DISPLAY (),
301                                               check_window,
302                                               GWMHA_WIN_SUPPORTING_WM_CHECK,
303                                               XA_CARDINAL,
304                                               &size, 32);
305      if (wm_check_data && wm_check_data[0] == check_window)
306        gwmh_gnome_wm_win = check_window;
307      g_free (wm_check_data);
308      g_free (property_data);
309    }
310  gwmh_desk.detected_gnome_wm = gwmh_gnome_wm_win != None;
311
312  /* we ignore certain GWMH properties if there's no GNOME compliant wm
313   * mostly, so we don't get fooled by stale properties
314   */
315  if (!gwmh_gnome_wm_win)
316    for (i = 0; i < N_GWMHA_IGNORE_ATOMS; i++)
317      *gwmh_atoms[i].atom = None;
318
319  gwmh_syncs_frozen = 0;
320  gwmh_sync ();
321
322  gdk_error_trap_pop ();
323
324  /* in case handlers are already setup, they need to know about wm changes,
325   * and our new structure setup
326   */
327  gwmh_desk_notify (GWMH_DESK_INFO_ALL | GWMH_DESK_INFO_BOOTUP);
328
329  /* and recheck everything from queue handler */
330  gwmh_desk_queue_update (GWMH_DESK_INFO_FORCE_EVERYTHING);
331
332  return gwmh_gnome_wm_win;
333}
334
335static GdkWindow*
336gdk_window_ref_from_xid (Window xwin)
337{
338  GdkWindow *window;
339
340  /* the xid maybe invalid already, in that case we return NULL */
341  window = gdk_window_lookup (xwin);
342  if (!window)
343    window = gdk_window_foreign_new (xwin);
344  else
345    gdk_window_ref (window);
346
347  return window;
348}
349
350static gpointer
351get_typed_property_data (Display *xdisplay,
352                         Window   xwindow,
353                         Atom     property,
354                         Atom     requested_type,
355                         gint    *size_p,
356                         guint    expected_format)
357{
358  static const guint prop_buffer_lengh = 1024 * 1024;
359  unsigned char *prop_data = NULL;
360  Atom type_returned = 0;
361  unsigned long nitems_return = 0, bytes_after_return = 0;
362  int format_returned = 0;
363  gpointer data = NULL;
364  gboolean abort = FALSE;
365
366  g_return_val_if_fail (size_p != NULL, NULL);
367  *size_p = 0;
368
369  gdk_error_trap_push ();
370
371  abort = XGetWindowProperty (xdisplay,
372                              xwindow,
373                              property,
374                              0, prop_buffer_lengh,
375                              False,
376                              requested_type,
377                              &type_returned, &format_returned,
378                              &nitems_return,
379                              &bytes_after_return,
380                              &prop_data) != Success;
381  if (gdk_error_trap_pop () ||
382      type_returned == None)
383    abort++;
384  if (!abort &&
385      requested_type != AnyPropertyType &&
386      requested_type != type_returned)
387    {
388      /* aparently this can happen for some properties of broken apps, be silent */
389      abort++;
390    }
391  if (!abort && bytes_after_return)
392    {
393      g_warning (G_GNUC_PRETTY_FUNCTION "(): Eeek, property has more than %u bytes, stored on harddisk?",
394                 prop_buffer_lengh);
395      abort++;
396    }
397  if (!abort && expected_format && expected_format != format_returned)
398    {
399      g_warning (G_GNUC_PRETTY_FUNCTION "(): Expected format (%u) unmatched (%d), programmer was drunk?",
400                 expected_format, format_returned);
401      abort++;
402    }
403  if (!abort && prop_data && nitems_return && format_returned)
404    {
405      switch (format_returned)
406        {
407        case 32:
408          *size_p = nitems_return * 4;
409          if (sizeof (gulong) == 8)
410            {
411              guint32 i, *mem = g_malloc0 (*size_p + 1);
412              gulong *prop_longs = (gulong*) prop_data;
413
414              for (i = 0; i < *size_p / 4; i++)
415                mem[i] = prop_longs[i];
416              data = mem;
417            }
418          break;
419        case 16:
420          *size_p = nitems_return * 2;
421          break;
422        case 8:
423          *size_p = nitems_return;
424          break;
425        default:
426          g_warning ("Unknown property data format with %d bits (extraterrestrial?)",
427                     format_returned);
428          break;
429        }
430      if (!data && *size_p)
431        {
432          guint8 *mem = NULL;
433
434          if (format_returned == 8 && type_returned == XA_COMPOUND_TEXT)
435            {
436              gchar **tlist = NULL;
437              gint count = gdk_text_property_to_text_list (type_returned, 8, prop_data,
438                                                           nitems_return, &tlist);
439
440              if (count && tlist && tlist[0])
441                {
442                  mem = (guint8 *)g_strdup (tlist[0]);
443                  *size_p = strlen ((char *)mem);
444                }
445              if (tlist)
446                gdk_free_text_list (tlist);
447            }
448          if (!mem)
449            {
450              mem = g_malloc (*size_p + 1);
451              memcpy (mem, prop_data, *size_p);
452              mem[*size_p] = 0;
453            }
454          data = mem;
455        }
456    }
457
458  if (prop_data)
459    XFree (prop_data);
460 
461  return data;
462}
463
464static gboolean
465wm_protocol_check_support (Window xwin,
466                           Atom   check_atom)
467{
468  Atom *pdata = NULL;
469  guint32 *gdata = NULL;
470  int n_pids = 0;
471  gboolean is_supported = FALSE;
472  guint i, n_gids = 0;
473
474  gdk_error_trap_push ();
475
476  if (!XGetWMProtocols (GDK_DISPLAY (),
477                        xwin,
478                        &pdata,
479                        &n_pids))
480    {
481      gint size = 0;
482
483      gdata = get_typed_property_data (GDK_DISPLAY (),
484                                       xwin,
485                                       XA_WM_PROTOCOLS,
486                                       XA_WM_PROTOCOLS,
487                                       &size, 32);
488      n_gids = size / 4;
489    }
490
491  gdk_error_trap_pop ();
492
493  for (i = 0; i < n_pids; i++)
494    if (pdata[i] == check_atom)
495      {
496        is_supported = TRUE;
497        break;
498      }
499  if (pdata)
500    XFree (pdata);
501  if (!is_supported)
502    for (i = 0; i < n_gids; i++)
503      if (gdata[i] == check_atom)
504        {
505          is_supported = TRUE;
506          break;
507        }
508  g_free (gdata);
509
510  return is_supported;
511}
512
513static gboolean
514send_client_message_2L (Window recipient,
515                        Window event_window,
516                        Atom   message_type,
517                        long   event_mask,
518                        glong  long1,
519                        glong  long2)
520{
521  return send_client_message_5L (recipient, event_window, message_type, event_mask, long1, long2, 0, 0, 0);
522}
523
524static gboolean
525send_client_message_3L (Window recipient,
526                        Window event_window,
527                        Atom   message_type,
528                        long   event_mask,
529                        glong  long1,
530                        glong  long2,
531                        glong  long3)
532{
533  return send_client_message_5L (recipient, event_window, message_type, event_mask, long1, long2, long3, 0, 0);
534}
535
536static gboolean
537send_client_message_5L (Window recipient,
538                        Window event_window,
539                        Atom   message_type,
540                        long   event_mask,
541                        glong  long1,
542                        glong  long2,
543                        glong  long3,
544                        glong  long4,
545                        glong  long5)
546{
547  XEvent xevent = { 0, };
548
549  /* g_return_val_if_fail (n_longs < 6, FALSE); */
550
551  xevent.type = ClientMessage;
552  xevent.xclient.window = event_window;
553  xevent.xclient.message_type = message_type;
554  xevent.xclient.format = 32;
555  xevent.xclient.data.l[0] = long1;
556  xevent.xclient.data.l[1] = long2;
557  xevent.xclient.data.l[2] = long3;
558  xevent.xclient.data.l[3] = long4;
559  xevent.xclient.data.l[4] = long5;
560
561  gdk_error_trap_push ();
562
563  XSendEvent (GDK_DISPLAY (), recipient, False, event_mask, &xevent);
564  gwmh_sync ();
565
566  return !gdk_error_trap_pop ();
567}
568
569GdkWindow*
570gwmh_root_put_atom_window (const gchar   *atom_name,
571                           GdkWindowType  window_type,
572                           GdkWindowClass window_class,
573                           GdkEventMask   event_mask)
574{
575  GdkAtom atom;
576  GdkWindow *window;
577  GdkWindowAttr attributes;
578  guint attributes_mask;
579  Window xwin;
580
581  g_return_val_if_fail (atom_name != NULL, NULL);
582
583  atom = gdk_atom_intern (atom_name, FALSE);
584  attributes.window_type = window_type;
585  attributes.wclass = window_class;
586  attributes.x = -99;
587  attributes.y = -99;
588  attributes.width = 1;
589  attributes.height = 1;
590  attributes.event_mask = event_mask;
591  attributes_mask = GDK_WA_X | GDK_WA_Y;
592 
593  window = gdk_window_new (NULL, &attributes, attributes_mask);
594  xwin = GDK_WINDOW_XWINDOW (window);
595  gdk_property_change (GDK_ROOT_PARENT (),
596                       atom,
597                       XA_WINDOW, 32,
598                       GDK_PROP_MODE_REPLACE,
599                       (guchar*) &xwin, 1);
600  gdk_property_change (window,
601                       atom,
602                       XA_WINDOW, 32,
603                       GDK_PROP_MODE_REPLACE,
604                       (guchar*) &xwin, 1);
605
606  return window;
607}
608
609static void
610get_task_root_and_frame (GwmhTask *task)
611{
612  Window *children = NULL, xframe = 0;
613  Window xwin = task->xwin, xparent = xwin, xroot = xwin;
614  guint size = 0;
615 
616  gdk_error_trap_push ();
617
618  while (XQueryTree (GDK_DISPLAY (), xwin, &xroot, &xparent, &children, &size))
619    {
620      guint32 *value;
621
622      if (children)
623        {
624          XFree (children);
625          children = NULL;
626        }
627
628      xframe = xwin;
629      xwin = xparent;
630      if (xparent == xroot)
631        break;
632
633      value = get_typed_property_data (GDK_DISPLAY (),
634                                       xparent,
635                                       XA_ENLIGHTENMENT_DESKTOP,
636                                       XA_CARDINAL,
637                                       &size, 0);
638      if (value)
639        {
640          g_free (value);
641          break;
642        }
643    }
644
645  if (gdk_error_trap_pop ())
646    {
647      xparent = None;
648      xframe = None;
649      /* aparently this can happen if a window is destructed shortly after
650       * creation, just be silent.
651       * g_warning (G_GNUC_PRETTY_FUNCTION "(): task window id %ld invalid?", task->xwin);
652       */
653    }
654
655  if (!task->sroot || GSTC_PARENT_XWINDOW (task->sroot) != xparent)
656    {
657      GdkWindow *window;
658
659      if (task->sroot)
660        {
661          gstc_parent_delete_watch (task->sroot);
662          task->sroot = NULL;
663        }
664
665      if (xparent)
666        {
667          window = gdk_window_ref_from_xid (xparent);
668          if (window)
669            {
670              task->sroot = gstc_parent_add_watch (window);
671              gdk_window_unref (window);
672            }
673        }
674    }
675 
676  if (task->xframe != xframe)
677    {
678      if (task->gdkframe)
679        {
680          gdk_window_remove_filter (task->gdkframe, task_event_monitor_frame_wrapper, task);
681          gdk_window_unref (task->gdkframe);
682          task->gdkframe = NULL;
683        }
684
685      task->xframe = xframe;
686      if (xframe)
687        {
688          XWindowAttributes attribs = { 0, };
689         
690          task->gdkframe = gdk_window_ref_from_xid (task->xframe);
691          if (task->gdkframe)
692            {
693              gdk_window_add_filter (task->gdkframe, task_event_monitor_frame_wrapper, task);
694             
695              /* select events */
696              gdk_error_trap_push ();
697              XGetWindowAttributes (GDK_DISPLAY (),
698                                    task->xframe,
699                                    &attribs);
700              XSelectInput (GDK_DISPLAY (),
701                            task->xframe,
702                            attribs.your_event_mask |
703                            StructureNotifyMask);
704              gwmh_sync ();
705              gdk_error_trap_pop ();
706            }
707        }
708    }
709}
710
711static GdkFilterReturn
712root_event_monitor (GdkXEvent *gdk_xevent,
713                    GdkEvent  *event,
714                    gpointer   gdk_root)
715{
716  XEvent *xevent = gdk_xevent;
717 
718  switch (xevent->type)
719    {
720      GwmhDeskInfoMask imask;
721
722    case MapNotify:
723    case UnmapNotify:
724    case CirculateNotify:
725    case ReparentNotify:
726    case DestroyNotify:
727      if (!gwmh_gnome_wm_win)
728        gwmh_desk_queue_update (GWMH_DESK_INFO_CLIENT_LIST);
729      if (gwmh_gnome_wm_win == xevent->xdestroywindow.window)
730        {
731          gwmh_need_reboot = TRUE;
732          gwmh_desk_queue_update (GWMH_DESK_INFO_FORCE_EVERYTHING);
733        }
734      break;
735    case PropertyNotify:
736      imask = gwmh_property_atom2info (xevent->xproperty.atom, FALSE);
737      if (imask)
738        gwmh_desk_queue_update (imask);
739      if (xevent->xproperty.atom == GWMHA_WIN_SUPPORTING_WM_CHECK)
740        {
741          gwmh_need_reboot = TRUE;
742          gwmh_desk_queue_update (GWMH_DESK_INFO_FORCE_EVERYTHING);
743        }
744      break;
745    default:
746      break;
747    }
748 
749  return GDK_FILTER_CONTINUE;
750}
751
752static GdkFilterReturn
753task_event_monitor_frame_wrapper (GdkXEvent *gdk_xevent,
754                                  GdkEvent  *event,
755                                  gpointer   task_pointer)
756{
757  return task_event_monitor (gdk_xevent, event, task_pointer);
758}
759
760static GdkFilterReturn
761task_event_monitor (GdkXEvent *gdk_xevent,
762                    GdkEvent  *event,
763                    gpointer   task_pointer)
764{
765  GwmhTask *task = task_pointer;
766  XEvent *xevent = gdk_xevent;
767  GwmhTaskInfoMask imask = 0;
768  GwmhTaskInfoMask ichanges = 0;
769 
770  switch (xevent->type)
771    {
772    case ConfigureNotify:
773      if (xevent->xconfigure.window == task->xwin)
774        {
775          task->win_x = xevent->xconfigure.x;
776          task->win_y = xevent->xconfigure.y;
777          task->win_width = xevent->xconfigure.width;
778          task->win_height = xevent->xconfigure.height;
779          ichanges |= GWMH_TASK_INFO_WIN_GEO;
780        }
781      else if (xevent->xconfigure.window == task->xframe &&
782               !xevent->xconfigure.send_event /* fvwm sends bogus sent_event configures */)
783        {
784          task->frame_x = xevent->xconfigure.x;
785          task->frame_y = xevent->xconfigure.y;
786          task->frame_width = xevent->xconfigure.width;
787          task->frame_height = xevent->xconfigure.height;
788          ichanges |= GWMH_TASK_INFO_FRAME_GEO;
789        }
790      break;
791    case PropertyNotify:
792      imask = gwmh_property_atom2info (xevent->xproperty.atom, TRUE);
793      break;
794    case ReparentNotify:
795      /* refetch frame and root window */
796      imask = GWMH_TASK_INFO_DESKTOP | GWMH_TASK_INFO_AREA;
797      /* fall through */
798    case MapNotify:
799    case UnmapNotify:
800    case CirculateNotify:
801    case DestroyNotify:
802      if (!gwmh_gnome_wm_win)
803        {
804          imask = GWMH_TASK_INFO_ALL;
805          gwmh_desk_queue_update (GWMH_DESK_INFO_CLIENT_LIST);
806        }
807      if (GWMH_TASK_FOCUSED (task) && (xevent->type == UnmapNotify ||
808                                       xevent->type == DestroyNotify))
809        {
810          task->focused = FALSE;
811          ichanges |= GWMH_TASK_INFO_FOCUSED;
812        }
813      break;
814    case FocusOut:
815      if (GWMH_TASK_FOCUSED (task) && (xevent->xfocus.detail != NotifyPointer &&
816                                       xevent->xfocus.detail != NotifyInferior))
817        {
818          task->focused = FALSE;
819          ichanges |= GWMH_TASK_INFO_FOCUSED;
820        }
821      break;
822    case FocusIn:
823      if (!GWMH_TASK_FOCUSED (task))
824        {
825          task->focused = TRUE;
826          ichanges |= GWMH_TASK_INFO_FOCUSED;
827        }
828      break;
829    default:
830      break;
831    }
832
833  if (!gwmh_gnome_wm_win && (ichanges || imask))
834    imask = GWMH_TASK_INFO_ALL;
835
836  if (imask || ichanges)
837    gwmh_task_queue_full (task, imask, ichanges);
838
839  return GDK_FILTER_CONTINUE;
840}
841
842static guint
843gwmh_property_atom2info (Atom     atom,
844                         gboolean per_task)
845{
846  static const Atom gwmh_XA_WM_HINTS = XA_WM_HINTS;
847  static const Atom gwmh_XA_WM_NAME = XA_WM_NAME;
848  static const Atom gwmh_XA_WM_ICON_NAME = XA_WM_ICON_NAME;
849 
850  static const struct {
851    const Atom *atom_p;
852    guint       iflag;
853  } desk_atom_masks[] = {
854    /* GwmhDeskInfoMask */
855    { &GWMHA_WIN_CLIENT_LIST,           GWMH_DESK_INFO_CLIENT_LIST, },
856    { &GWMHA_WIN_AREA,                  GWMH_DESK_INFO_CURRENT_AREA, },
857    { &GWMHA_WIN_AREA_COUNT,            GWMH_DESK_INFO_N_AREAS, },
858    { &GWMHA_WIN_WORKSPACE,             GWMH_DESK_INFO_CURRENT_DESKTOP, },
859    { &GWMHA_WIN_WORKSPACE_NAMES,       GWMH_DESK_INFO_DESKTOP_NAMES, },
860    { &GWMHA_WIN_WORKSPACE_COUNT,       GWMH_DESK_INFO_N_DESKTOPS, },
861    { &GWMHA_WIN_UNIFIED_AREA,          GWMH_DESK_INFO_N_AREAS, },
862  }, task_atom_masks[] = {
863    /* GwmhTaskInfoMask */
864    { &GWMHA_WIN_APP_STATE,             GWMH_TASK_INFO_APP_STATE, },
865    { &GWMHA_WIN_STATE,                 GWMH_TASK_INFO_GSTATE, },
866    { &GWMHA_WIN_HINTS,                 GWMH_TASK_INFO_GHINTS, },
867    { &GWMHA_WIN_LAYER,                 GWMH_TASK_INFO_LAYER, },
868    { &GWMHA_WIN_AREA,                  GWMH_TASK_INFO_AREA, },
869    { &GWMHA_WIN_WORKSPACE,             GWMH_TASK_INFO_DESKTOP, },
870    { &XA_WM_STATE,                     GWMH_TASK_INFO_ICONIFIED, },
871    { &gwmh_XA_WM_HINTS,                GWMH_TASK_INFO_WM_HINTS, },
872    { &gwmh_XA_WM_NAME,                 GWMH_TASK_INFO_MISC, },
873    { &gwmh_XA_WM_ICON_NAME,            GWMH_TASK_INFO_MISC, },
874  };
875  guint i;
876 
877  if (per_task)
878    {
879      for (i = 0; i < sizeof (task_atom_masks) / sizeof (task_atom_masks[0]); i++)
880        if (*task_atom_masks[i].atom_p == atom)
881          return task_atom_masks[i].iflag;
882    }
883  else
884    {
885      for (i = 0; i < sizeof (desk_atom_masks) / sizeof (desk_atom_masks[0]); i++)
886        if (*desk_atom_masks[i].atom_p == atom)
887          return desk_atom_masks[i].iflag;
888    }
889 
890  return 0;
891}
892
893static void
894gwmh_desk_update (GwmhDeskInfoMask imask)
895{
896  GdkWindow *window = GDK_ROOT_PARENT ();
897  Display *xdisplay = GDK_WINDOW_XDISPLAY (window);
898  Window xwindow = GDK_WINDOW_XWINDOW (window);
899  GwmhDeskInfoMask ichanges = 0;
900  gboolean force_everything = FALSE;
901 
902  gdk_error_trap_push ();
903 
904  if (imask & GWMH_DESK_INFO_N_DESKTOPS)
905    {
906      gint size = 0;
907      guint32 *n_data;
908      guint n_desktops;
909     
910      n_data = get_typed_property_data (xdisplay, xwindow,
911                                        GWMHA_WIN_WORKSPACE_COUNT,
912                                        XA_CARDINAL,
913                                        &size, 32);
914      n_desktops = MAX (1, n_data ? n_data[0] : 0);
915      g_free (n_data);
916      if (n_desktops != gwmh_desk.n_desktops)
917        {
918          guint i, old_n = gwmh_desk.n_desktops;
919         
920          gwmh_desk.n_desktops = n_desktops;
921          ichanges |= GWMH_DESK_INFO_N_DESKTOPS;
922         
923          for (i = gwmh_desk.n_desktops; i < old_n; i++)
924            g_free (gwmh_desk.desktop_names[i]);
925          gwmh_desk.desktop_names = g_renew (gchar*,
926                                             gwmh_desk.desktop_names,
927                                             gwmh_desk.n_desktops);
928          gwmh_harea_cache = g_renew (guint,
929                                      gwmh_harea_cache,
930                                      gwmh_desk.n_desktops);
931          gwmh_varea_cache = g_renew (guint,
932                                      gwmh_varea_cache,
933                                      gwmh_desk.n_desktops);
934          for (i = old_n; i < gwmh_desk.n_desktops; i++)
935            {
936              gwmh_desk.desktop_names[i] = NULL;
937              gwmh_harea_cache[i] = 0;
938              gwmh_varea_cache[i] = 0;
939            }
940          if (old_n < gwmh_desk.n_desktops)
941            imask |= GWMH_DESK_INFO_DESKTOP_NAMES;
942        }
943      imask |= GWMH_DESK_INFO_CURRENT_DESKTOP;
944    }
945 
946  if (imask & GWMH_DESK_INFO_N_AREAS)
947    {
948      gint size = 0;
949      guint32 *size_data;
950      guint n_hareas, n_vareas;
951
952      /* FIXME: this should be a property check for UNIFIED_AREA */
953      if (gwmh_hack_think_unified_area != gwmh_desk.unified_area)
954        {
955          ichanges |= GWMH_DESK_INFO_CURRENT_AREA | GWMH_DESK_INFO_N_AREAS;
956          gwmh_desk.unified_area = gwmh_hack_think_unified_area;
957        }
958
959      size_data = get_typed_property_data (xdisplay, xwindow,
960                                           GWMHA_WIN_AREA_COUNT,
961                                           XA_CARDINAL,
962                                           &size, 32);
963      n_hareas = MAX (1, size_data ? size_data[0] : 0);
964      n_vareas = MAX (1, size_data ? size_data[1] : 0);
965      g_free (size_data);
966      if (n_hareas != gwmh_desk.n_hareas ||
967          n_vareas != gwmh_desk.n_vareas)
968        {
969          guint i;
970         
971          gwmh_desk.n_hareas = n_hareas;
972          gwmh_desk.n_vareas = n_vareas;
973          ichanges |= GWMH_DESK_INFO_N_AREAS;
974         
975          for (i = 0; i < gwmh_desk.n_desktops; i++)
976            {
977              gwmh_harea_cache[i] = MIN (gwmh_harea_cache[i], gwmh_desk.n_hareas - 1);
978              gwmh_varea_cache[i] = MIN (gwmh_varea_cache[i], gwmh_desk.n_vareas - 1);
979            }
980        }
981      imask |= GWMH_DESK_INFO_CURRENT_AREA;
982    }
983 
984  if (imask & GWMH_DESK_INFO_DESKTOP_NAMES)
985    {
986      XTextProperty text_property = { 0, };
987      guint i = 0;
988     
989      if (XGetTextProperty (xdisplay, xwindow,
990                            &text_property,
991                            GWMHA_WIN_WORKSPACE_NAMES))
992        {
993          gchar **property_strings = NULL;
994          int n_strings = 0;
995         
996          if (XTextPropertyToStringList (&text_property,
997                                         &property_strings,
998                                         &n_strings))
999            {
1000             
1001              for (; i < MIN (n_strings, gwmh_desk.n_desktops); i++)
1002                {
1003                  if (!gwmh_string_equals (gwmh_desk.desktop_names[i],
1004                                           property_strings[i]))
1005                    {
1006                      g_free (gwmh_desk.desktop_names[i]);
1007                      gwmh_desk.desktop_names[i] = g_strdup (property_strings[i]);
1008                      ichanges |= GWMH_DESK_INFO_DESKTOP_NAMES;
1009                    }
1010                }
1011             
1012              if (property_strings)
1013                XFreeStringList (property_strings);
1014            }
1015         
1016          if (text_property.value)
1017            XFree (text_property.value);
1018        }
1019     
1020      for (; i < gwmh_desk.n_desktops; i++)
1021        if (gwmh_desk.desktop_names[i])
1022          {
1023            g_free (gwmh_desk.desktop_names[i]);
1024            gwmh_desk.desktop_names[i] = NULL;
1025            ichanges |= GWMH_DESK_INFO_DESKTOP_NAMES;
1026          }
1027    }
1028 
1029  if (imask & GWMH_DESK_INFO_CURRENT_DESKTOP)
1030    {
1031      gint size = 0;
1032      guint32 *indx;
1033      guint current_desktop;
1034     
1035      indx = get_typed_property_data (xdisplay, xwindow,
1036                                      GWMHA_WIN_WORKSPACE,
1037                                      XA_CARDINAL,
1038                                      &size, 32);
1039      current_desktop = MIN (indx ? indx[0] : 0, gwmh_desk.n_desktops - 1);
1040      g_free (indx);
1041      if (current_desktop != gwmh_desk.current_desktop)
1042        {
1043          gwmh_desk.current_desktop = current_desktop;
1044          ichanges |= GWMH_DESK_INFO_CURRENT_DESKTOP;
1045        }
1046      imask |= GWMH_DESK_INFO_CURRENT_AREA;
1047    }
1048 
1049  if (imask & GWMH_DESK_INFO_CURRENT_AREA)
1050    {
1051      gint size = 0;
1052      guint32 *indx_data;
1053      guint i, harea, varea;
1054     
1055      indx_data = get_typed_property_data (xdisplay, xwindow,
1056                                           GWMHA_WIN_AREA,
1057                                           XA_CARDINAL,
1058                                           &size, 32);
1059      harea = MIN (indx_data ? indx_data[0] : 0, gwmh_desk.n_hareas - 1);
1060      varea = MIN (indx_data ? indx_data[1] : 0, gwmh_desk.n_vareas - 1);
1061      g_free (indx_data);
1062      if (harea != gwmh_desk.current_harea ||
1063          varea != gwmh_desk.current_varea)
1064        {
1065          gwmh_desk.current_harea = harea;
1066          gwmh_desk.current_varea = varea;
1067          ichanges |= GWMH_DESK_INFO_CURRENT_AREA;
1068        }
1069      if (gwmh_harea_cache[gwmh_desk.current_desktop] != gwmh_desk.current_harea ||
1070          gwmh_varea_cache[gwmh_desk.current_desktop] != gwmh_desk.current_varea)
1071        {
1072          gwmh_harea_cache[gwmh_desk.current_desktop] = gwmh_desk.current_harea;
1073          gwmh_varea_cache[gwmh_desk.current_desktop] = gwmh_desk.current_varea;
1074          ichanges |= GWMH_DESK_INFO_CURRENT_AREA;
1075        }
1076      if (gwmh_desk.unified_area && (ichanges & GWMH_DESK_INFO_CURRENT_AREA))
1077        for (i = 0; i < gwmh_desk.n_desktops; i++)
1078          {
1079            gwmh_harea_cache[i] = gwmh_desk.current_harea;
1080            gwmh_varea_cache[i] = gwmh_desk.current_varea;
1081          }
1082    }
1083 
1084  if (imask & GWMH_DESK_INFO_CLIENT_LIST)
1085    {
1086      guint n_tasks = 0;
1087      gint size = 0;
1088      guint32 *task_ids;
1089
1090      if (!gwmh_gnome_wm_win)
1091        task_ids = hack_a_client_list (xdisplay, xwindow, &n_tasks);
1092      else
1093        {
1094          task_ids = get_typed_property_data (xdisplay, xwindow,
1095                                              GWMHA_WIN_CLIENT_LIST,
1096                                              XA_CARDINAL,
1097                                              &size, 32);
1098          n_tasks = size / 4;
1099        }
1100      if (client_list_sync (task_ids, n_tasks))
1101        ichanges |= GWMH_DESK_INFO_CLIENT_LIST;
1102      g_free (task_ids);
1103    }
1104 
1105  gdk_error_trap_pop ();
1106
1107  force_everything = (imask & GWMH_DESK_INFO_HACK_FLAG) != 0;
1108
1109  gwmh_desk_update_queued &= ~imask;
1110 
1111  if ((ichanges & (GWMH_DESK_INFO_CURRENT_DESKTOP |
1112                   GWMH_DESK_INFO_CURRENT_AREA)) ||
1113      force_everything)
1114    {
1115      GList *node;
1116
1117      for (node = gwmh_desk.client_list; node; node = node->next)
1118        {
1119          GwmhTask *task = node->data;
1120
1121          if (force_everything)
1122            gwmh_task_queue_update (task, GWMH_TASK_INFO_ALL);
1123          else if (GWMH_TASK_STICKY (task))
1124            gwmh_task_queue_update (task, (GWMH_TASK_INFO_DESKTOP |
1125                                           GWMH_TASK_INFO_AREA));
1126        }
1127    }
1128
1129  if (force_everything)
1130    gwmh_desk_notify (GWMH_DESK_INFO_ALL);
1131  else if (ichanges)
1132    gwmh_desk_notify (ichanges);
1133}
1134
1135static gboolean
1136gwmh_task_update (GwmhTask        *task,
1137                  GwmhTaskInfoMask imask,
1138                  gboolean         skip_notify)
1139{
1140  Display *xdisplay = GDK_WINDOW_XDISPLAY (task->gdkwindow);
1141  Window xwindow = task->xwin;
1142  gint size;
1143  GwmhTaskInfoMask ichanges = 0;
1144  GwmhDeskInfoMask desk_imask_queued;
1145  gboolean was_queued;
1146
1147  desk_imask_queued = gwmh_desk_update_queued & (GWMH_DESK_INFO_N_DESKTOPS |
1148                                                 GWMH_DESK_INFO_N_AREAS |
1149                                                 GWMH_DESK_INFO_CURRENT_DESKTOP |
1150                                                 GWMH_DESK_INFO_CURRENT_AREA |
1151                                                 GWMH_DESK_INFO_HACK_FLAG);
1152  if (desk_imask_queued)
1153    gwmh_desk_update (desk_imask_queued);
1154
1155  gdk_error_trap_push ();
1156 
1157  if (imask & GWMH_TASK_INFO_GSTATE)
1158    {
1159      guint32 *flags;
1160      GwmhState gstate;
1161      gboolean was_sticky = GWMH_TASK_STICKY (task);
1162     
1163      flags = get_typed_property_data (xdisplay, xwindow,
1164                                       GWMHA_WIN_STATE,
1165                                       XA_CARDINAL,
1166                                       &size, 32);
1167      gstate = flags ? flags[0] : 0;
1168      g_free (flags);
1169      if (task->gstate != gstate)
1170        {
1171          task->gstate = gstate;
1172          ichanges |= GWMH_TASK_INFO_GSTATE;
1173        }
1174      if (was_sticky != GWMH_TASK_STICKY (task))
1175        imask |= GWMH_TASK_INFO_DESKTOP | GWMH_TASK_INFO_AREA;
1176    }
1177 
1178  /* we need to feature this right at the beginning,
1179   * to complete the task_new () process
1180   */
1181  if (imask & (GWMH_TASK_INFO_DESKTOP | GWMH_TASK_INFO_AREA))
1182    {
1183      Window xroot = task->sroot ? GSTC_PARENT_XWINDOW (task->sroot) : 0;
1184      Window xframe = task->xframe;
1185     
1186      get_task_root_and_frame (task);
1187      if (!task->xframe || !task->sroot)
1188        {
1189          /* eek, that's ugly, we need to get rid of this task as
1190           * soon as possible, but *not* right here.
1191           */
1192          gwmh_desk_queue_update (GWMH_DESK_INFO_CLIENT_LIST);
1193          return FALSE;
1194        }
1195      if (task->xframe != xframe ||
1196          (task->sroot ? GSTC_PARENT_XWINDOW (task->sroot) : 0) != !xroot)
1197        imask |= (GWMH_TASK_INFO_DESKTOP |
1198                  GWMH_TASK_INFO_AREA |
1199                  (task->xframe != xframe ? GWMH_TASK_INFO_FRAME_GEO : 0));
1200    }
1201 
1202  if (imask & GWMH_TASK_INFO_MISC)
1203    {
1204      gchar *name;
1205     
1206      name = get_typed_property_data (xdisplay, xwindow,
1207                                      XA_WM_NAME,
1208                                      XA_STRING,
1209                                      &size, 8);
1210      if (!name && !size)
1211        name = get_typed_property_data (xdisplay, xwindow,
1212                                        XA_WM_NAME,
1213                                        XA_COMPOUND_TEXT,
1214                                        &size, 8);
1215      if (!gwmh_string_equals (task->name, name))
1216        {
1217          g_free (task->name);
1218          task->name = g_strdup (name);
1219          ichanges |= GWMH_TASK_INFO_MISC;
1220        }
1221      g_free (name);
1222
1223      name = get_typed_property_data (xdisplay, xwindow,
1224                                      XA_WM_ICON_NAME,
1225                                      XA_STRING,
1226                                      &size, 8);
1227      if (!name && !size)
1228        name = get_typed_property_data (xdisplay, xwindow,
1229                                        XA_WM_ICON_NAME,
1230                                        XA_COMPOUND_TEXT,
1231                                        &size, 8);
1232      if (!gwmh_string_equals (task->icon_name, name))
1233        {
1234          g_free (task->icon_name);
1235          task->icon_name = g_strdup (name);
1236          ichanges |= GWMH_TASK_INFO_MISC;
1237        }
1238      g_free (name);
1239    }
1240 
1241  if (imask & GWMH_TASK_INFO_FOCUSED)
1242    {
1243      Window focus;
1244      int revert_to;
1245      gboolean focused;
1246     
1247      XGetInputFocus (xdisplay, &focus, &revert_to);
1248      focused = task->xwin == focus;
1249      if (focused != GWMH_TASK_FOCUSED (task))
1250        {
1251          if (task->focused)
1252            g_message ("%ssetting focus according to XGetInputFocus() for %ld",
1253                       !focused ? "re" : "",
1254                       task->xwin);
1255          task->focused = focused;
1256          ichanges |= GWMH_TASK_INFO_FOCUSED;
1257        }
1258    }
1259 
1260  if (imask & GWMH_TASK_INFO_ICONIFIED)
1261    {
1262      gint32 *state;
1263      gboolean iconified;
1264     
1265      state = get_typed_property_data (xdisplay, xwindow,
1266                                       XA_WM_STATE,
1267                                       XA_WM_STATE,
1268                                       &size, 32);
1269      iconified = state && state[0] == IconicState;
1270      g_free (state);
1271      if (iconified != task->iconified)
1272        {
1273          task->iconified = iconified;
1274          ichanges |= GWMH_TASK_INFO_ICONIFIED;
1275        }
1276    }
1277 
1278  if (imask & GWMH_TASK_INFO_GHINTS)
1279    {
1280      guint32 *ghint_data;
1281      GwmhHints ghints;
1282     
1283      ghint_data = get_typed_property_data (xdisplay, xwindow,
1284                                            GWMHA_WIN_HINTS,
1285                                            XA_CARDINAL,
1286                                            &size, 32);
1287      ghints = ghint_data ? ghint_data[0] : 0;
1288      g_free (ghint_data);
1289      if (task->ghints != ghints)
1290        {
1291          task->ghints = ghints;
1292          ichanges |= GWMH_TASK_INFO_GHINTS;
1293        }
1294    }
1295 
1296  if (imask & GWMH_TASK_INFO_APP_STATE)
1297    {
1298      guint32 *state_data;
1299      GnomeWinAppState app_state;
1300     
1301      state_data = get_typed_property_data (xdisplay, xwindow,
1302                                            GWMHA_WIN_APP_STATE,
1303                                            XA_CARDINAL,
1304                                            &size, 32);
1305      app_state = state_data ? state_data[0] : 0;
1306      g_free (state_data);
1307      if (app_state != task->app_state)
1308        {
1309          task->app_state = app_state;
1310          ichanges |= GWMH_TASK_INFO_APP_STATE;
1311        }
1312    }
1313 
1314  if (imask & GWMH_TASK_INFO_DESKTOP)
1315    {
1316      guint desktop;
1317     
1318      if (!GWMH_TASK_STICKY (task))
1319        {
1320          guint32 *indx = get_typed_property_data (xdisplay, xwindow,
1321                                                   GWMHA_WIN_WORKSPACE,
1322                                                   XA_CARDINAL,
1323                                                   &size, 32);
1324          desktop = MIN (indx ? indx[0] : 0, gwmh_desk.n_desktops - 1);
1325          g_free (indx);
1326        }
1327      else
1328        desktop = gwmh_desk.current_desktop;
1329      if (desktop != task->desktop)
1330        {
1331          task->last_desktop = task->desktop;
1332          task->desktop = desktop;
1333          ichanges |= GWMH_TASK_INFO_DESKTOP;
1334        }
1335    }
1336 
1337  if (imask & GWMH_TASK_INFO_AREA)
1338    {
1339      guint harea, varea;
1340     
1341      if (!GWMH_TASK_STICKY (task))
1342        {
1343          guint32 *coords = get_typed_property_data (xdisplay, xwindow,
1344                                                     GWMHA_WIN_AREA,
1345                                                     XA_CARDINAL,
1346                                                     &size, 32);
1347          harea = MIN (coords ? coords[0] : 0, gwmh_desk.n_hareas - 1);
1348          varea = MIN (coords ? coords[1] : 0, gwmh_desk.n_vareas - 1);
1349          g_free (coords);
1350        }
1351      else
1352        {
1353          harea = gwmh_desk.current_harea;
1354          varea = gwmh_desk.current_varea;
1355        }
1356      if (task->harea != harea || task->varea != varea)
1357        {
1358          task->last_harea = task->harea;
1359          task->last_varea = task->varea;
1360          task->harea = harea;
1361          task->varea = varea;
1362          ichanges |= GWMH_TASK_INFO_AREA;
1363          imask |= GWMH_TASK_INFO_ALLOCATION;
1364        }
1365    }
1366 
1367  if (imask & GWMH_TASK_INFO_LAYER)
1368    {
1369      guint32 *layer_data;
1370      guint layer;
1371     
1372      layer_data = get_typed_property_data (xdisplay, xwindow,
1373                                            GWMHA_WIN_LAYER,
1374                                            XA_CARDINAL,
1375                                            &size, 32);
1376      layer = layer_data ? layer_data[0] : 0;
1377      g_free (layer_data);
1378      if (layer != task->layer)
1379        {
1380          task->last_layer = task->layer;
1381          task->layer = layer;
1382          ichanges |= GWMH_TASK_INFO_LAYER;
1383        }
1384    }
1385 
1386  if (imask & GWMH_TASK_INFO_ALLOCATION)
1387    {
1388      Window dummy_win;
1389      gint x = 0, y = 0;
1390      guint border, depth, width = 0, height = 0;
1391     
1392      if (imask & GWMH_TASK_INFO_FRAME_GEO)
1393        {
1394          XGetGeometry (xdisplay, task->xframe,
1395                        &dummy_win,
1396                        &x, &y,
1397                        &width, &height,
1398                        &border, &depth);
1399          XTranslateCoordinates (xdisplay, task->xframe,
1400                                 TASK_XROOT (task),
1401                                 0, 0,
1402                                 &x, &y,
1403                                 &dummy_win);
1404          if (task->frame_x != x || task->frame_y != y ||
1405              task->frame_width != width || task->frame_height != height)
1406            {
1407              task->frame_x = x;
1408              task->frame_y = y;
1409              task->frame_width = width;
1410              task->frame_height = height;
1411              ichanges |= GWMH_TASK_INFO_FRAME_GEO;
1412            }
1413        }
1414      if (imask & GWMH_TASK_INFO_WIN_GEO)
1415        {
1416          x = 0;
1417          y = 0;
1418          width = 0;
1419          height = 0;
1420          XGetGeometry (xdisplay, task->xwin,
1421                        &dummy_win,
1422                        &x, &y,
1423                        &width, &height,
1424                        &border, &depth);
1425          XTranslateCoordinates (xdisplay, task->xwin,
1426                                 TASK_XROOT (task),
1427                                 0, 0,
1428                                 &x, &y,
1429                                 &dummy_win);
1430          if (task->win_x != x || task->win_y != y ||
1431              task->win_width != width || task->win_height != height)
1432            {
1433              task->win_x = x;
1434              task->win_y = y;
1435              task->win_width = width;
1436              task->win_height = height;
1437              ichanges |= GWMH_TASK_INFO_WIN_GEO;
1438            }
1439        }
1440    }
1441
1442  if (imask & GWMH_TASK_INFO_UNOTIFY)
1443    {
1444      ichanges |= imask & GWMH_TASK_INFO_UNOTIFY;
1445    }
1446 
1447  gdk_error_trap_pop ();
1448 
1449#if 0
1450  if (GWMH_TASK_STICKY (task))
1451    g_print ("sticky window %ld (%d:%d,%d) has frame %ld at %d, %d, %d, %d\n",
1452             task->xwin,
1453             task->desktop, task->harea, task->varea,
1454             task->xframe,
1455             task->frame_x, task->frame_y,
1456             task->frame_width, task->frame_height);
1457#endif
1458
1459  was_queued = GWMH_TASK_UPDATE_QUEUED (task);
1460  task->imask_queued &= ~imask;
1461  if (skip_notify)
1462    task->imask_notify |= ichanges;
1463  else
1464    {
1465      ichanges |= task->imask_notify;
1466      task->imask_notify = 0;
1467    }
1468 
1469  if (was_queued && !GWMH_TASK_UPDATE_QUEUED (task))
1470    gwmh_task_update_queue = g_slist_remove (gwmh_task_update_queue, task);
1471 
1472  if (!skip_notify && ichanges)
1473    gwmh_task_notify (task, GWMH_NOTIFY_INFO_CHANGED, ichanges);
1474
1475  return TRUE;
1476}
1477
1478static gboolean
1479gwmh_idle_handler (gpointer data)
1480{
1481  if (gwmh_need_reboot)
1482    {
1483      gwmh_need_reboot = FALSE;
1484      gwmh_idle_handler_id = 0;
1485      gwmh_init ();
1486      return FALSE;
1487    }
1488 
1489  if (gwmh_desk_update_queued)
1490    gwmh_desk_update (gwmh_desk_update_queued);
1491
1492  while (gwmh_task_update_queue)
1493    {
1494      GSList *node = gwmh_task_update_queue;
1495      GwmhTask *task = node->data;
1496      GwmhTaskInfoMask imask = task->imask_queued;
1497
1498      gwmh_task_update_queue = node->next;
1499      g_slist_free_1 (node);
1500
1501      task->imask_queued = 0;
1502      gwmh_task_update (task, imask, FALSE);
1503    }
1504
1505  gwmh_idle_handler_id = 0;
1506
1507  if (gwmh_desk_update_queued)
1508    gwmh_desk_update (gwmh_desk_update_queued);
1509
1510  return FALSE;
1511}
1512
1513void
1514gwmh_desk_queue_update (GwmhDeskInfoMask imask)
1515{
1516  gwmh_task_queue_full (NULL, 0, 0);
1517
1518  gwmh_desk_update_queued |= imask;
1519}
1520
1521static void
1522gwmh_task_queue_full (GwmhTask        *task,
1523                      GwmhTaskInfoMask imask,
1524                      GwmhTaskInfoMask notify_mask)
1525{
1526  if (!gwmh_idle_handler_id)
1527    gwmh_idle_handler_id = gtk_idle_add_priority (GWMH_PRIORITY_UPDATE,
1528                                                  gwmh_idle_handler,
1529                                                  NULL);
1530  if (task)
1531    {
1532      gboolean was_queued = GWMH_TASK_UPDATE_QUEUED (task);
1533
1534      task->imask_queued |= imask;
1535      task->imask_notify |= notify_mask;
1536
1537      if (!was_queued && GWMH_TASK_UPDATE_QUEUED (task))
1538        gwmh_task_update_queue = g_slist_prepend (gwmh_task_update_queue, task);
1539    }
1540}
1541
1542void
1543gwmh_task_queue_update (GwmhTask        *task,
1544                        GwmhTaskInfoMask imask)
1545{
1546  g_return_if_fail (task != NULL);
1547
1548  gwmh_task_queue_full (task, imask, 0);
1549}
1550
1551guint
1552gwmh_desk_notifier_add (GwmhDeskNotifierFunc func,
1553                        gpointer             func_data)
1554{
1555  GHook *hook;
1556
1557  g_return_val_if_fail (func != NULL, 0);
1558
1559  hook = g_hook_alloc (&gwmh_desk_hook_list);
1560  hook->func = func;
1561  hook->data = func_data;
1562  g_hook_prepend (&gwmh_desk_hook_list, hook);
1563
1564  return hook->hook_id;
1565}
1566
1567guint
1568gwmh_task_notifier_add (GwmhTaskNotifierFunc func,
1569                        gpointer             func_data)
1570{
1571  GHook *hook;
1572
1573  g_return_val_if_fail (func != NULL, 0);
1574
1575  hook = g_hook_alloc (&gwmh_task_hook_list);
1576  hook->func = func;
1577  hook->data = func_data;
1578  g_hook_prepend (&gwmh_task_hook_list, hook);
1579
1580  return hook->hook_id;
1581}
1582
1583void
1584gwmh_desk_notifier_remove (guint id)
1585{
1586  g_return_if_fail (id > 0);
1587
1588  if (!g_hook_destroy (&gwmh_desk_hook_list, id))
1589    g_warning (G_GNUC_PRETTY_FUNCTION "(): unable to remove notifier (%d)",
1590               id);
1591}
1592
1593void
1594gwmh_task_notifier_remove (guint id)
1595{
1596  g_return_if_fail (id > 0);
1597
1598  if (!g_hook_destroy (&gwmh_task_hook_list, id))
1599    g_warning (G_GNUC_PRETTY_FUNCTION "(): unable to remove notifier (%d)",
1600               id);
1601}
1602
1603static gboolean
1604match_hook (GHook   *hook,
1605            gpointer data_p)
1606{
1607  gpointer *data = data_p;
1608
1609  return hook->func == data[0] && hook->data == data[1];
1610}
1611
1612void
1613gwmh_desk_notifier_remove_func (GwmhDeskNotifierFunc func,
1614                                gpointer             func_data)
1615{
1616  GHook *hook;
1617  gpointer data[2];
1618
1619  g_return_if_fail (func != NULL);
1620
1621  data[0] = func;
1622  data[1] = func_data;
1623  hook = g_hook_find (&gwmh_desk_hook_list, TRUE, match_hook, data);
1624  if (hook)
1625    g_hook_destroy_link (&gwmh_desk_hook_list, hook);
1626  else
1627    g_warning (G_GNUC_PRETTY_FUNCTION "(): unable to remove notifier <%p> (%p)",
1628               func, func_data);
1629}
1630
1631void
1632gwmh_task_notifier_remove_func (GwmhTaskNotifierFunc func,
1633                                gpointer             func_data)
1634{
1635  GHook *hook;
1636  gpointer data[2];
1637
1638  g_return_if_fail (func != NULL);
1639
1640  data[0] = func;
1641  data[1] = func_data;
1642  hook = g_hook_find (&gwmh_task_hook_list, TRUE, match_hook, data);
1643  if (hook)
1644    g_hook_destroy_link (&gwmh_task_hook_list, hook);
1645  else
1646    g_warning (G_GNUC_PRETTY_FUNCTION "(): unable to remove notifier <%p> (%p)",
1647               func, func_data);
1648}
1649
1650static gboolean
1651desk_marshaller (GHook   *hook,
1652                 gpointer data_p)
1653{
1654  gpointer *data = data_p;
1655  GwmhDeskNotifierFunc func = hook->func;
1656
1657  return func (hook->data,
1658               &gwmh_desk,
1659               GPOINTER_TO_UINT (data[0]));
1660}
1661
1662static void
1663gwmh_desk_notify (GwmhDeskInfoMask imask)
1664{
1665  gpointer data[1];
1666
1667  data[0] = GUINT_TO_POINTER (imask);
1668 
1669  g_hook_list_marshal_check (&gwmh_desk_hook_list, TRUE, desk_marshaller, data);
1670}
1671
1672static gboolean
1673task_marshaller (GHook   *hook,
1674                 gpointer data_p)
1675{
1676  gpointer *data = data_p;
1677  GwmhTaskNotifierFunc func = hook->func;
1678
1679  return func (hook->data,
1680               data[0],
1681               GPOINTER_TO_UINT (data[1]),
1682               GPOINTER_TO_UINT (data[2]));
1683}
1684
1685static void
1686gwmh_task_notify (GwmhTask          *task,
1687                  GwmhTaskNotifyType ntype,
1688                  GwmhTaskInfoMask   imask)
1689{
1690  gpointer data[3];
1691
1692  g_return_if_fail (task != NULL);
1693  g_return_if_fail (ntype < GWMH_NOTIFY_LAST);
1694
1695  data[0] = task;
1696  data[1] = GUINT_TO_POINTER (ntype);
1697  data[2] = ntype == GWMH_NOTIFY_INFO_CHANGED ? GUINT_TO_POINTER (imask) : 0;
1698 
1699  g_hook_list_marshal_check (&gwmh_task_hook_list, TRUE, task_marshaller, data);
1700}
1701
1702static GwmhTask*
1703task_new (GdkWindow *window)
1704{
1705  GwmhTask *task;
1706  XWindowAttributes attribs;
1707
1708  g_return_val_if_fail (window != NULL, NULL);
1709
1710  task = g_new0 (GwmhTask, 1);
1711  gwmh_desk.client_list = g_list_prepend (gwmh_desk.client_list, task);
1712  task->name = NULL;
1713  task->icon_name = NULL;
1714  task->frame_x = -1;
1715  task->frame_y = -1;
1716  task->win_x = -1;
1717  task->win_y = -1;
1718  task->gdkwindow = window;
1719  gdk_window_ref (window);
1720  task->gdkframe = NULL;
1721  g_datalist_init (&task->datalist);
1722  task->xwin = GDK_WINDOW_XWINDOW (window);
1723 
1724  /* monitor events on the GdkWindow */
1725  if (task->gdkwindow)
1726    gdk_window_add_filter (task->gdkwindow, task_event_monitor, task);
1727  /* select events */
1728  gdk_error_trap_push ();
1729  XGetWindowAttributes (GDK_DISPLAY (), task->xwin, &attribs);
1730  XSelectInput (GDK_DISPLAY (),
1731                task->xwin,
1732                attribs.your_event_mask |
1733                PropertyChangeMask |
1734                FocusChangeMask |
1735                StructureNotifyMask);
1736  gwmh_sync ();
1737  gdk_error_trap_pop ();
1738
1739  /* gwmh_task_update () will do this for us:
1740   * get_task_root_and_frame (task);
1741   */
1742  if (gwmh_task_update (task, GWMH_TASK_INFO_ALL, TRUE))
1743    {
1744      gwmh_task_notify (task, GWMH_NOTIFY_NEW, 0);
1745      gwmh_task_update (task, 0, FALSE);
1746
1747      return task;
1748    }
1749  else
1750    {
1751      task_delete (task);
1752
1753      return NULL;
1754    }
1755}
1756
1757static void
1758task_delete (GwmhTask *task)
1759{
1760  GSList *list, *wlist = NULL;
1761
1762  g_return_if_fail (g_list_find (gwmh_desk.client_list, task));
1763
1764  /* reset event masks for still existing windows */
1765  wlist = g_slist_prepend (wlist, gdk_window_ref_from_xid (task->xframe));
1766  wlist = g_slist_prepend (wlist, gdk_window_ref_from_xid (task->xwin));
1767  gdk_error_trap_push ();
1768  for (list = wlist; list; list = list->next)
1769    {
1770      GdkWindow *window = list->data;
1771
1772      if (window)
1773        {
1774          if (GDK_WINDOW_IS_FOREIGN (window) &&
1775              !GDK_WINDOW_IS_DESTROYED (window))
1776            {
1777              XWindowAttributes attribs = { 0, };
1778
1779              XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
1780                                    GDK_WINDOW_XWINDOW (window),
1781                                    &attribs);
1782              XSelectInput (GDK_WINDOW_XDISPLAY (window),
1783                            GDK_WINDOW_XWINDOW (window),
1784                            attribs.your_event_mask &
1785                            ~(StructureNotifyMask | FocusChangeMask | PropertyChangeMask));
1786            }
1787          gdk_window_unref (window);
1788        }
1789    }
1790  gwmh_sync ();
1791  g_slist_free (wlist);
1792  gdk_error_trap_pop ();
1793
1794  gwmh_task_notify (task, GWMH_NOTIFY_DESTROY, 0);
1795  g_datalist_clear (&task->datalist);
1796
1797  gdk_window_remove_filter (task->gdkwindow, task_event_monitor, task);
1798  gdk_window_unref (task->gdkwindow);
1799  if (task->gdkframe)
1800    {
1801      gdk_window_remove_filter (task->gdkframe, task_event_monitor_frame_wrapper, task);
1802      gdk_window_unref (task->gdkframe);
1803    }
1804  if (task->sroot)
1805    gstc_parent_delete_watch (task->sroot);
1806  if (GWMH_TASK_UPDATE_QUEUED (task))
1807    gwmh_task_update_queue = g_slist_remove (gwmh_task_update_queue, task);
1808  gwmh_desk.client_list = g_list_remove (gwmh_desk.client_list, task);
1809  g_free (task->name);
1810  task->name = "DELETED";
1811  g_free (task->icon_name);
1812  task->icon_name = NULL;
1813 
1814  if (g_list_find (gwmh_desk.client_list, task))
1815    g_error ("deleted task still in client_list\n"); /* FIXME */
1816  if (g_slist_find (gwmh_task_update_queue, task))
1817    g_error ("deleted task still in queue\n"); /* FIXME */
1818 
1819  g_free (task);
1820}
1821
1822static gboolean
1823client_list_sync (guint32 *xwindow_ids,
1824                  guint    n_ids)
1825{
1826  guint i;
1827  GList *node, *client_list = NULL;
1828  gboolean clients_changed = FALSE;
1829 
1830  node = gwmh_desk.client_list;
1831  while (node)
1832    {
1833      GwmhTask *task = node->data;
1834      gboolean found_client = FALSE;
1835     
1836      node = node->next;
1837
1838      for (i = 0; i < n_ids; i++)
1839        if (xwindow_ids[i] == task->xwin)
1840          {
1841            n_ids--;
1842            xwindow_ids[i] = xwindow_ids[n_ids];
1843            client_list = g_list_prepend (client_list, task);
1844            found_client = TRUE;
1845            break;
1846          }
1847      if (!found_client)
1848        {
1849          task_delete (task);
1850          clients_changed = TRUE;
1851        }
1852    }
1853
1854  for (i = 0; i < n_ids; i++)
1855    {
1856      GdkWindow *window = gdk_window_ref_from_xid (xwindow_ids[i]);
1857
1858      if (window)
1859        {
1860          GwmhTask *task = task_new (window);
1861
1862          if (task)
1863            {
1864              client_list = g_list_prepend (client_list, task);
1865              clients_changed = TRUE;
1866            }
1867          gdk_window_unref (window);
1868        }
1869    }
1870
1871  g_assert (g_list_length (gwmh_desk.client_list) == g_list_length (client_list)); /* FIXME */
1872  if (clients_changed)
1873    {
1874      g_list_free (gwmh_desk.client_list);
1875      gwmh_desk.client_list = client_list;
1876    }
1877  else
1878    g_list_free (client_list);
1879 
1880  return clients_changed;
1881}
1882
1883GwmhTask*
1884gwmh_task_from_window (GdkWindow *window)
1885{
1886  GList *node;
1887
1888  g_return_val_if_fail (window != NULL, NULL);
1889
1890  for (node = gwmh_desk.client_list; node; node = node->next)
1891    {
1892      GwmhTask *task = node->data;
1893
1894      if (task->gdkwindow == window)
1895        return task;
1896    }
1897
1898  return NULL;
1899}
1900
1901void
1902gwmh_task_set_qdata_full (GwmhTask      *task,
1903                          GQuark         quark,
1904                          gpointer       data,
1905                          GDestroyNotify destroy)
1906{
1907  g_return_if_fail (task != NULL);
1908
1909  g_datalist_id_set_data_full (&task->datalist, quark, data, destroy);
1910}
1911
1912gpointer
1913gwmh_task_get_qdata (GwmhTask *task,
1914                     GQuark    quark)
1915{
1916  g_return_val_if_fail (task != NULL, NULL);
1917 
1918  return g_datalist_id_get_data (&task->datalist, quark);
1919}
1920
1921gpointer
1922gwmh_task_steal_qdata (GwmhTask *task,
1923                       GQuark    quark)
1924{
1925  gpointer data;
1926
1927  g_return_val_if_fail (task != NULL, NULL);
1928 
1929  data = g_datalist_id_get_data (&task->datalist, quark);
1930  if (data)
1931    g_datalist_id_remove_no_notify (&task->datalist, quark);
1932
1933  return data;
1934}
1935
1936void
1937gwmh_task_kill (GwmhTask *task)
1938{
1939  g_return_if_fail (task != NULL);
1940
1941  gdk_error_trap_push ();
1942  XKillClient (GDK_DISPLAY (), task->xwin);
1943  gwmh_sync ();
1944  gdk_error_trap_pop ();
1945}
1946
1947void
1948gwmh_task_get_frame_area_pos (GwmhTask *task,
1949                              gint     *x_p,
1950                              gint     *y_p)
1951     
1952{
1953  gint x, y;
1954  gint swidth = gdk_screen_width ();
1955  gint sheight = gdk_screen_height ();
1956
1957  g_return_if_fail (task != NULL);
1958 
1959  /* convert from current_area relative into task->area relative */
1960
1961  x = task->frame_x + (gwmh_harea_cache[task->desktop] - task->harea) * swidth;
1962  y = task->frame_y + (gwmh_varea_cache[task->desktop] - task->varea) * sheight;
1963  if (x_p)
1964    *x_p = x;
1965  if (y_p)
1966    *y_p = y;
1967}
1968
1969gboolean
1970gwmh_task_close (GwmhTask *task)
1971{
1972  gboolean can_delete = FALSE;
1973
1974  g_return_val_if_fail (task != NULL, FALSE);
1975
1976  can_delete = wm_protocol_check_support (task->xwin, XA_WM_DELETE_WINDOW);
1977
1978  if (can_delete)
1979    can_delete = send_client_message_2L (task->xwin, task->xwin,
1980                                         XA_WM_PROTOCOLS,
1981                                         0,
1982                                         XA_WM_DELETE_WINDOW, CurrentTime);
1983
1984  return can_delete;
1985}
1986
1987void
1988gwmh_task_iconify (GwmhTask *task)
1989{
1990  g_return_if_fail (task != NULL);
1991
1992  gdk_error_trap_push ();
1993  XIconifyWindow (GDK_DISPLAY (), task->xwin, DefaultScreen (GDK_DISPLAY ()));
1994  gwmh_sync ();
1995  gdk_error_trap_pop ();
1996}
1997
1998void
1999gwmh_task_deiconify (GwmhTask *task)
2000{
2001  g_return_if_fail (task != NULL);
2002
2003  if (gwmh_task_update (task, GWMH_TASK_INFO_ICONIFIED, FALSE) &&
2004      GWMH_TASK_ICONIFIED (task))
2005    {
2006      gdk_error_trap_push ();
2007     
2008      XMapWindow (GDK_DISPLAY (), task->xwin);
2009     
2010      gwmh_sync ();
2011      gdk_error_trap_pop ();
2012    }
2013}
2014
2015void
2016gwmh_task_focus (GwmhTask *task)
2017{
2018  g_return_if_fail (task != NULL);
2019
2020  if (!gwmh_task_update (task, GWMH_TASK_INFO_FOCUSED, FALSE))
2021    return;
2022
2023  if (wm_protocol_check_support (task->xwin, XA_WM_TAKE_FOCUS))
2024    send_client_message_2L (task->xwin, task->xwin,
2025                            XA_WM_PROTOCOLS,
2026                            0,
2027                            XA_WM_TAKE_FOCUS, CurrentTime);
2028
2029  if (!GWMH_TASK_FOCUSED (task))
2030    {
2031      gdk_error_trap_push ();
2032
2033      XSetInputFocus (GDK_DISPLAY (), task->xwin, RevertToPointerRoot, CurrentTime);
2034
2035      gwmh_sync ();
2036      gdk_error_trap_pop ();
2037    }
2038}
2039
2040void
2041gwmh_task_show (GwmhTask *task)
2042{
2043  g_return_if_fail (task != NULL);
2044
2045  if (!gwmh_task_update (task,
2046                         (GWMH_TASK_INFO_ICONIFIED |
2047                          GWMH_TASK_INFO_FOCUSED |
2048                          GWMH_TASK_INFO_GSTATE),
2049                         FALSE))
2050    return;
2051
2052  if (!GWMH_TASK_SKIP_FOCUS (task) &&
2053      wm_protocol_check_support (task->xwin, XA_WM_TAKE_FOCUS))
2054    send_client_message_2L (task->xwin, task->xwin,
2055                            XA_WM_PROTOCOLS,
2056                            0,
2057                            XA_WM_TAKE_FOCUS, CurrentTime);
2058
2059  gdk_error_trap_push ();
2060
2061  if (GWMH_TASK_ICONIFIED (task))
2062    XMapWindow (GDK_DISPLAY (), task->xwin);
2063  if (!GWMH_TASK_SKIP_FOCUS (task) && !GWMH_TASK_FOCUSED (task))
2064    XSetInputFocus (GDK_DISPLAY (), task->xwin, RevertToPointerRoot, CurrentTime);
2065  XRaiseWindow (GDK_DISPLAY (), task->xwin);
2066  if (GWMH_TASK_SHADED (task))
2067    send_client_message_3L (GDK_ROOT_WINDOW (), task->xwin,
2068                            GWMHA_WIN_STATE,
2069                            SubstructureNotifyMask,
2070                            GWMH_STATE_SHADED, 0, CurrentTime);
2071 
2072  gwmh_sync ();
2073 
2074  gdk_error_trap_pop ();
2075}
2076
2077void
2078gwmh_task_raise (GwmhTask *task)
2079{
2080  g_return_if_fail (task != NULL);
2081
2082  gdk_error_trap_push ();
2083
2084  XRaiseWindow (GDK_DISPLAY (), task->xwin);
2085  gwmh_sync ();
2086 
2087  gdk_error_trap_pop ();
2088}
2089
2090void
2091gwmh_task_set_gstate_flags (GwmhTask *task,
2092                            GwmhState flags)
2093{
2094  g_return_if_fail (task != NULL);
2095
2096  if (!gwmh_task_update (task, GWMH_TASK_INFO_GSTATE, FALSE))
2097    return;
2098
2099  if ((GWMH_TASK_GSTATE (task) & flags) != flags)
2100    send_client_message_3L (GDK_ROOT_WINDOW (), task->xwin,
2101                            GWMHA_WIN_STATE,
2102                            SubstructureNotifyMask,
2103                            flags, flags, CurrentTime);
2104}
2105
2106void
2107gwmh_task_unset_gstate_flags (GwmhTask *task,
2108                              GwmhState flags)
2109{
2110  g_return_if_fail (task != NULL);
2111
2112  if (!gwmh_task_update (task, GWMH_TASK_INFO_GSTATE, FALSE))
2113    return;
2114
2115  if (GWMH_TASK_GSTATE (task) & flags)
2116    send_client_message_3L (GDK_ROOT_WINDOW (), task->xwin,
2117                            GWMHA_WIN_STATE,
2118                            SubstructureNotifyMask,
2119                            flags, 0, CurrentTime);
2120}
2121
2122void
2123gwmh_task_set_ghint_flags (GwmhTask *task,
2124                           GwmhHints flags)
2125{
2126  g_return_if_fail (task != NULL);
2127
2128  if (!gwmh_task_update (task, GWMH_TASK_INFO_GHINTS, FALSE))
2129    return;
2130
2131  if ((GWMH_TASK_GHINTS (task) & flags) != flags)
2132    send_client_message_3L (GDK_ROOT_WINDOW (), task->xwin,
2133                            GWMHA_WIN_HINTS,
2134                            SubstructureNotifyMask,
2135                            flags, flags, CurrentTime);
2136}
2137
2138void
2139gwmh_task_unset_ghint_flags (GwmhTask *task,
2140                             GwmhHints flags)
2141{
2142  g_return_if_fail (task != NULL);
2143
2144  if (!gwmh_task_update (task, GWMH_TASK_INFO_GHINTS, FALSE))
2145    return;
2146
2147  if (GWMH_TASK_GHINTS (task) & flags)
2148    send_client_message_3L (GDK_ROOT_WINDOW (), task->xwin,
2149                            GWMHA_WIN_HINTS,
2150                            SubstructureNotifyMask,
2151                            flags, 0, CurrentTime);
2152}
2153
2154void
2155gwmh_task_set_app_state (GwmhTask        *task,
2156                         GnomeWinAppState app_state)
2157{
2158  g_return_if_fail (task != NULL);
2159
2160  if (!gwmh_task_update (task, GWMH_TASK_INFO_APP_STATE, FALSE))
2161    return;
2162
2163  if (GWMH_TASK_APP_STATE (task) != app_state)
2164    send_client_message_2L (GDK_ROOT_WINDOW (), task->xwin,
2165                            GWMHA_WIN_APP_STATE,
2166                            SubstructureNotifyMask,
2167                            app_state, CurrentTime);
2168}
2169
2170void
2171gwmh_task_get_mini_icon (GwmhTask   *task,
2172                         GdkPixmap **pixmap,
2173                         GdkBitmap **mask)
2174{
2175  GdkGC *gc;
2176  GdkGCPrivate *gc_private;
2177  GdkPixmapPrivate *private;
2178  guint32 *atomdata;
2179  Window xwindow = task->xwin;
2180  Window root;
2181  int x, y;
2182  guint b, width, height, depth;
2183  gint size;
2184  Display *xdisplay;
2185
2186  g_return_if_fail (task != NULL);
2187  g_return_if_fail (pixmap != NULL);
2188  g_return_if_fail (mask != NULL);
2189
2190  xdisplay = GDK_WINDOW_XDISPLAY (task->gdkwindow);
2191
2192  atomdata = get_typed_property_data (xdisplay,
2193                                      xwindow,
2194                                      XA_KWM_WIN_ICON,
2195                                      XA_KWM_WIN_ICON,
2196                                      &size,
2197                                      32);
2198  if (!atomdata || !atomdata[0] || !size)
2199    {
2200      *pixmap = NULL;
2201      *mask = NULL;
2202
2203      return;
2204    }
2205
2206  /* Get icon size and depth */
2207  XGetGeometry (xdisplay, (Drawable) atomdata[0], &root, &x, &y,
2208                &width, &height, &b, &depth);
2209 
2210  /* Create a new GdkPixmap and copy the mini icon pixmap to it */
2211  *pixmap = gdk_pixmap_new (NULL, width, height, depth);
2212  gc = gdk_gc_new (*pixmap);
2213  gc_private = (GdkGCPrivate*) gc;
2214  private = (GdkPixmapPrivate*) *pixmap;
2215  XCopyArea (private->xdisplay, atomdata[0], private->xwindow, gc_private->xgc,
2216             0, 0, width, height, 0, 0);
2217  gdk_gc_destroy (gc);
2218 
2219  /* get mask size and depth */
2220  XGetGeometry (xdisplay, (Drawable)atomdata[1], &root, &x, &y,
2221                &width, &height, &b, &depth);
2222
2223  /* Create a new GdkBitmap and copy the mini icon mask to it */
2224  *mask = gdk_pixmap_new (NULL, width, height, 1);
2225  gc = gdk_gc_new (*mask);
2226  gc_private = (GdkGCPrivate*) gc;
2227  private = (GdkPixmapPrivate*) *mask;
2228  XCopyArea (private->xdisplay, atomdata[1], private->xwindow, gc_private->xgc,
2229             0, 0, width, height, 0, 0);
2230  gdk_gc_destroy (gc);
2231  g_free (atomdata);
2232}
2233
2234void
2235gwmh_task_set_layer (GwmhTask *task,
2236                     GwmhLayer layer)
2237{
2238  g_return_if_fail (task != NULL);
2239
2240  if (!gwmh_task_update (task, GWMH_TASK_INFO_LAYER, FALSE))
2241    return;
2242
2243  if (task->layer != layer)
2244    send_client_message_2L (GDK_ROOT_WINDOW (), task->xwin,
2245                            GWMHA_WIN_LAYER,
2246                            SubstructureNotifyMask,
2247                            layer, CurrentTime);
2248}
2249
2250void
2251gwmh_task_set_area (GwmhTask *task,
2252                    guint     desktop,
2253                    guint     harea,
2254                    guint     varea)
2255{
2256  g_return_if_fail (task != NULL);
2257
2258  if (!gwmh_task_update (task, GWMH_TASK_INFO_DESKTOP | GWMH_TASK_INFO_AREA, FALSE))
2259    return;
2260 
2261  if (desktop >= gwmh_desk.n_desktops ||
2262      harea >= gwmh_desk.n_hareas ||
2263      varea >= gwmh_desk.n_vareas ||
2264      (desktop == task->desktop &&
2265       harea == task->harea &&
2266       varea == task->varea))
2267    return;
2268 
2269  gwmh_sync ();
2270 
2271  /* ugly hack for buggy window managers
2272   * that violate the spec and expect us to modify properties directly
2273   */
2274  if (gwmh_hack_violate_client_msg)
2275    {
2276      long data[2];
2277     
2278      gdk_error_trap_push ();
2279     
2280      data[0] = desktop;
2281      if (desktop != task->desktop)
2282        XChangeProperty (GDK_DISPLAY (), task->xwin, GWMHA_WIN_WORKSPACE,
2283                         XA_CARDINAL, 32, PropModeReplace, (unsigned char*) data, 1);
2284     
2285      data[0] = harea;
2286      data[1] = varea;
2287      XChangeProperty (GDK_DISPLAY (), task->xwin, GWMHA_WIN_AREA,
2288                       XA_CARDINAL, 32, PropModeReplace, (unsigned char*) data, 2);
2289     
2290      gdk_error_trap_pop ();
2291    }
2292  else
2293    {
2294      gwmh_freeze_syncs ();
2295      if (desktop != task->desktop)
2296        send_client_message_2L (GDK_ROOT_WINDOW (), task->xwin,
2297                                GWMHA_WIN_WORKSPACE,
2298                                SubstructureNotifyMask,
2299                                desktop, CurrentTime);
2300      send_client_message_3L (GDK_ROOT_WINDOW (), task->xwin,
2301                              GWMHA_WIN_AREA,
2302                              SubstructureNotifyMask,
2303                              harea, varea, CurrentTime);
2304      gwmh_thaw_syncs ();
2305    }
2306}
2307
2308void
2309gwmh_task_set_desktop (GwmhTask *task,
2310                       guint     desktop)
2311{
2312  g_return_if_fail (task != NULL);
2313
2314  if (!gwmh_task_update (task, GWMH_TASK_INFO_DESKTOP, FALSE) ||
2315      desktop >= gwmh_desk.n_desktops || task->desktop == desktop)
2316    return;
2317 
2318  /* ugly hack for buggy window managers
2319   * that violate the spec and expect us to modify properties directly
2320   */
2321  if (gwmh_hack_violate_client_msg)
2322    {
2323      long data[2];
2324     
2325      gdk_error_trap_push ();
2326     
2327      data[0] = desktop;
2328      if (desktop != task->desktop)
2329        XChangeProperty (GDK_DISPLAY (), task->xwin, GWMHA_WIN_WORKSPACE,
2330                         XA_CARDINAL, 32, PropModeReplace, (unsigned char*) data, 1);
2331     
2332      gdk_error_trap_pop ();
2333    }
2334  else
2335    send_client_message_2L (GDK_ROOT_WINDOW (), task->xwin,
2336                            GWMHA_WIN_WORKSPACE,
2337                            SubstructureNotifyMask,
2338                            desktop, CurrentTime);
2339}
2340
2341GwmhDesk*
2342gwmh_desk_get_config (void)
2343{
2344  return &gwmh_desk;
2345}
2346
2347void
2348gwmh_window_send_client_message (GdkWindow *window,
2349                                 gulong     atom,
2350                                 gulong     long1,
2351                                 gulong     long2,
2352                                 gulong     long3,
2353                                 gulong     long4,
2354                                 gulong     long5)
2355{
2356  g_return_if_fail (window != NULL);
2357  g_return_if_fail (atom > 0);
2358
2359  if (!GDK_WINDOW_IS_DESTROYED (window))
2360    send_client_message_5L (GDK_ROOT_WINDOW (),
2361                            GDK_WINDOW_XWINDOW (window),
2362                            atom,
2363                            SubstructureNotifyMask,
2364                            long1, long2, long3, long4, long5);
2365}
2366
2367void
2368gwmh_desk_guess_desktop_area (guint  desktop,
2369                              guint *harea,
2370                              guint *varea)
2371{
2372  if (harea)
2373    *harea = 0;
2374  if (varea)
2375    *varea = 0;
2376  g_return_if_fail (desktop < gwmh_desk.n_desktops);
2377 
2378  if (harea)
2379    *harea = gwmh_harea_cache ? gwmh_harea_cache[desktop] : 1;
2380  if (varea)
2381    *varea = gwmh_varea_cache ? gwmh_varea_cache[desktop] : 1;
2382}
2383
2384void
2385gwmh_desk_set_current_desktop (guint desktop)
2386{
2387  g_return_if_fail (desktop < gwmh_desk.n_desktops);
2388
2389  gwmh_desk_update (GWMH_DESK_INFO_CURRENT_DESKTOP);
2390 
2391  if (desktop >= gwmh_desk.n_desktops ||
2392      desktop == gwmh_desk.current_desktop)
2393    return;
2394
2395  send_client_message_2L (GDK_ROOT_WINDOW (), GDK_ROOT_WINDOW (),
2396                          GWMHA_WIN_WORKSPACE,
2397                          SubstructureNotifyMask,
2398                          desktop, CurrentTime);
2399}
2400
2401void
2402gwmh_desk_set_hack_values (gboolean unified_area,
2403                           gboolean violate_client_msg)
2404{
2405  gwmh_hack_think_unified_area = unified_area != FALSE;
2406  gwmh_hack_violate_client_msg = violate_client_msg != FALSE;
2407
2408  gwmh_desk_update (GWMH_DESK_INFO_N_AREAS);
2409}
2410
2411void
2412gwmh_desk_set_current_area (guint desktop,
2413                            guint harea,
2414                            guint varea)
2415{
2416  gwmh_desk_update (GWMH_DESK_INFO_CURRENT_DESKTOP |
2417                    GWMH_DESK_INFO_CURRENT_AREA);
2418 
2419  if (desktop >= gwmh_desk.n_desktops ||
2420      harea >= gwmh_desk.n_hareas ||
2421      varea >= gwmh_desk.n_vareas ||
2422      (desktop == gwmh_desk.current_desktop &&
2423       harea == gwmh_desk.current_harea &&
2424       varea == gwmh_desk.current_varea))
2425    return;
2426
2427  gwmh_sync ();
2428  gwmh_freeze_syncs ();
2429  if (desktop != gwmh_desk.current_desktop)
2430    send_client_message_2L (GDK_ROOT_WINDOW (), GDK_ROOT_WINDOW (),
2431                            GWMHA_WIN_WORKSPACE,
2432                            SubstructureNotifyMask,
2433                            desktop, CurrentTime);
2434  send_client_message_3L (GDK_ROOT_WINDOW (), GDK_ROOT_WINDOW (),
2435                          GWMHA_WIN_AREA,
2436                          SubstructureNotifyMask,
2437                          harea, varea, CurrentTime);
2438  gwmh_thaw_syncs ();
2439}
2440
2441void
2442gwmh_desk_set_desktop_name (guint        desktop,
2443                            const gchar *name)
2444{
2445  gchar *old_name;
2446
2447  g_return_if_fail (desktop < gwmh_desk.n_desktops);
2448
2449  gwmh_desk_update (GWMH_DESK_INFO_DESKTOP_NAMES);
2450
2451  if (desktop >= gwmh_desk.n_desktops)
2452    return;
2453
2454  old_name = (gwmh_desk.desktop_names[desktop]
2455              ? gwmh_desk.desktop_names[desktop]
2456              : "");
2457  if (!gwmh_string_equals (name, old_name))
2458    {
2459      g_warning ("gwmh_desk_set_desktop_name() unimplemented");
2460    }
2461}
2462
2463GList*
2464gwmh_task_list_get (void)
2465{
2466  return gwmh_desk.client_list;
2467}
2468
2469GList*
2470gwmh_task_list_stack_sort (GList *task_list)
2471{
2472  GstcParent *sroot_cache = NULL;
2473  GSList *slist, *sparent_list = NULL;
2474  GList *node, *stacked_tasks = NULL;
2475
2476  for (node = task_list; node; node = node->next)
2477    {
2478      GwmhTask *task = node->data;
2479
2480      if (sroot_cache == task->sroot)
2481        continue;
2482      sroot_cache = task->sroot;
2483      if (task->sroot &&
2484          !g_slist_find (sparent_list, task->sroot))
2485        sparent_list = g_slist_prepend (sparent_list, task->sroot);
2486    }
2487  sparent_list = g_slist_reverse (sparent_list);
2488
2489  node = NULL;
2490  for (slist = sparent_list; slist; slist = slist->next)
2491    {
2492      GstcParent *sparent = slist->data;
2493      guint i;
2494
2495      for (i = 0; i < sparent->n_children; i++)
2496        for (node = task_list; node; node = node->next)
2497          {
2498            GwmhTask *task = node->data;
2499           
2500            if (task->xframe == sparent->children[i])
2501              {
2502                if (node->prev)
2503                  node->prev->next = node->next;
2504                else
2505                  task_list = node->next;
2506                if (node->next)
2507                  node->next->prev = node->prev;
2508               
2509                if (stacked_tasks)
2510                  stacked_tasks->prev = node;
2511                node->next = stacked_tasks;
2512                node->prev = NULL;
2513                stacked_tasks = node;
2514               
2515                break;
2516              }
2517          }
2518    }
2519  g_slist_free (sparent_list);
2520
2521  node = g_list_last (task_list);
2522  stacked_tasks = g_list_reverse (stacked_tasks);
2523  if (node)
2524    {
2525      if (stacked_tasks)
2526        stacked_tasks->prev = node;
2527      node->next = stacked_tasks;
2528      stacked_tasks = task_list;
2529    }
2530 
2531  return stacked_tasks;
2532}
2533
2534static gboolean
2535check_client (Display *display,
2536              Window   xwindow,
2537              Atom     state_atom)
2538{
2539  gboolean valid_client = TRUE;
2540
2541  if (valid_client)
2542    {
2543      Atom dummy1;
2544      int format = 0;
2545      unsigned long dummy2, nitems = 0, *prop = NULL;
2546     
2547      XGetWindowProperty (display, xwindow, state_atom, 0, 1024, False, state_atom,
2548                          &dummy1, &format, &nitems, &dummy2, (unsigned char **) &prop);
2549      if (prop)
2550        {
2551          valid_client = format == 32 && nitems > 0 && (prop[0] == NormalState ||
2552                                                        prop[0] == IconicState);
2553          XFree (prop);
2554        }
2555    }
2556
2557  if (valid_client)
2558    {
2559      XWindowAttributes attributes = { 0, };
2560
2561      XGetWindowAttributes (display, xwindow, &attributes);
2562      valid_client = (attributes.class == InputOutput &&
2563                      attributes.map_state == IsViewable);
2564    }
2565
2566  if (valid_client)
2567    {
2568      XWMHints *hints = XGetWMHints (display, xwindow);
2569     
2570      valid_client &= hints && (hints->flags & InputHint) && hints->input;
2571      if (hints)
2572        XFree (hints);
2573    }
2574 
2575  return valid_client;
2576}
2577
2578static Window
2579find_input_client (Display *display,
2580                   Window   xwindow,
2581                   Atom     state_atom)
2582{
2583  Window dummy1, dummy2, *children = NULL;
2584  unsigned int n_children = 0;
2585  guint i;
2586 
2587  if (check_client (display, xwindow, state_atom))
2588    return xwindow;
2589 
2590  if (!XQueryTree (display, xwindow, &dummy1, &dummy2, &children, &n_children) || !children)
2591    return None;
2592 
2593  for (i = 0; i < n_children; i++)
2594    {
2595      xwindow = find_input_client (display, children[i], state_atom);
2596      if (xwindow)
2597        break;
2598    }
2599 
2600  XFree (children);
2601 
2602  return xwindow;
2603}
2604
2605static guint32*
2606hack_a_client_list (Display *display,
2607                    Window   xroot,
2608                    guint   *_n_clients)
2609{
2610  Window xwindow, dummy, *children = NULL;
2611  unsigned int n_children = 0;
2612  guint i, n_clients = 0;
2613  guint32 *clients = NULL;
2614 
2615  *_n_clients = 0;
2616 
2617  gdk_error_trap_push ();
2618
2619  XGrabServer (display);
2620
2621  if (!XQueryTree (display, xroot, &xwindow, &dummy, &children, &n_children) || !children)
2622    {
2623      XUngrabServer (display);
2624      return NULL;
2625    }
2626 
2627  for (i = 0; i < n_children; i++)
2628    {
2629      xwindow = find_input_client (display,
2630                                   children[i],
2631                                   XInternAtom (display, "WM_STATE", False));
2632      if (xwindow)
2633        {
2634          n_clients++;
2635          clients = g_renew (guint32, clients, n_clients);
2636          clients[n_clients - 1] = xwindow;
2637        }
2638    }
2639
2640  XUngrabServer (display);
2641 
2642  XFree (children);
2643 
2644  *_n_clients = n_clients;
2645
2646  gdk_error_trap_pop ();
2647 
2648  return clients;
2649}
Note: See TracBrowser for help on using the repository browser.