1 /*
   2  * Copyright (c) 2001, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #ifdef HEADLESS
  27     #error This file should not be included in headless library
  28 #endif
  29 
  30 /*
  31  * Some SCIENCE stuff happens, and it is CONFUSING
  32  */
  33 
  34 #include "awt_p.h"
  35 
  36 #include <X11/Xproto.h>
  37 #include <X11/Xlib.h>
  38 #include <X11/Xatom.h>
  39 #include <Xm/MwmUtil.h>
  40 
  41 /* JNI headers */
  42 #include "java_awt_Frame.h"     /* for frame state constants */
  43 
  44 #include "awt_wm.h"
  45 #include "awt_util.h"           /* for X11 error handling macros */
  46 
  47 /*
  48  * NB: 64 bit awareness.
  49  *
  50  * Since this code reads/writes window properties heavily, one thing
  51  * should be noted well.  Xlib uses C type 'long' for properties of
  52  * format 32.  Fortunately, typedef for Atom is 'long' as well, so
  53  * passing property data as or casting returned property data to
  54  * arrays of atoms is safe.
  55  */
  56 
  57 
  58 /*
  59  * Atoms used to communicate with window manager(s).
  60  * Naming convention:
  61  *   o  for atom  "FOO" the variable is  "XA_FOO"
  62  *   o  for atom "_BAR" the variable is "_XA_BAR"
  63  * Don't forget to add initialization to awt_wm_initAtoms below.
  64  */
  65 
  66 /*
  67  * Before WM rototill JDK used to check for running WM by just testing
  68  * if certain atom is interned or not.  We'd better not confuse older
  69  * JDK by interning these atoms.  Use awt_wm_atomInterned() to intern
  70  * them lazily.
  71  *
  72  * ENLIGHTENMENT_COMMS
  73  * _ICEWM_WINOPTHINT
  74  * _SAWMILL_TIMESTAMP
  75  * _DT_SM_WINDOW_INFO
  76  * _MOTIF_WM_INFO
  77  * _SUN_WM_PROTOCOLS
  78  */
  79 
  80 /* Good old ICCCM */
  81 static Atom XA_WM_STATE;
  82 
  83 /* New "netwm" spec from www.freedesktop.org */
  84 static Atom XA_UTF8_STRING;     /* like STRING but encoding is UTF-8 */
  85 static Atom _XA_NET_SUPPORTING_WM_CHECK;
  86 static Atom _XA_NET_SUPPORTED;  /* list of protocols (property of root) */
  87 static Atom _XA_NET_WM_NAME;    /* window property */
  88 static Atom _XA_NET_WM_STATE;   /* both window property and request */
  89 
  90 /*
  91  * _NET_WM_STATE is a list of atoms.
  92  * NB: Standard spelling is "HORZ" (yes, without an 'I'), but KDE2
  93  * uses misspelled "HORIZ" (see KDE bug #20229).  This was fixed in
  94  * KDE 2.2.  Under earlier versions of KDE2 horizontal and full
  95  * maximization doesn't work .
  96  */
  97 static Atom _XA_NET_WM_STATE_MAXIMIZED_HORZ;
  98 static Atom _XA_NET_WM_STATE_MAXIMIZED_VERT;
  99 static Atom _XA_NET_WM_STATE_SHADED;
 100 static Atom _XA_NET_WM_STATE_ABOVE;
 101 static Atom _XA_NET_WM_STATE_BELOW;
 102 static Atom _XA_NET_WM_STATE_HIDDEN;
 103 
 104 /* Currently we only care about max_v and max_h in _NET_WM_STATE */
 105 #define AWT_NET_N_KNOWN_STATES 2
 106 
 107 /* Gnome WM spec (superseded by "netwm" above, but still in use) */
 108 static Atom _XA_WIN_SUPPORTING_WM_CHECK;
 109 static Atom _XA_WIN_PROTOCOLS;
 110 static Atom _XA_WIN_STATE;
 111 static Atom _XA_WIN_LAYER;
 112 
 113 /* Enlightenment */
 114 static Atom _XA_E_FRAME_SIZE;
 115 
 116 /* KWin (KDE2) */
 117 static Atom _XA_KDE_NET_WM_FRAME_STRUT;
 118 
 119 /* KWM (KDE 1.x) OBSOLETE??? */
 120 static Atom XA_KWM_WIN_ICONIFIED;
 121 static Atom XA_KWM_WIN_MAXIMIZED;
 122 
 123 /* OpenLook */
 124 static Atom _XA_OL_DECOR_DEL;
 125 static Atom _XA_OL_DECOR_HEADER;
 126 static Atom _XA_OL_DECOR_RESIZE;
 127 static Atom _XA_OL_DECOR_PIN;
 128 static Atom _XA_OL_DECOR_CLOSE;
 129 
 130 /* For _NET_WM_STATE ClientMessage requests */
 131 #define _NET_WM_STATE_REMOVE    0 /* remove/unset property */
 132 #define _NET_WM_STATE_ADD       1 /* add/set property      */
 133 #define _NET_WM_STATE_TOGGLE    2 /* toggle property       */
 134 
 135 /* _WIN_STATE bits */
 136 #define WIN_STATE_STICKY          (1<<0) /* everyone knows sticky            */
 137 #define WIN_STATE_MINIMIZED       (1<<1) /* Reserved - definition is unclear */
 138 #define WIN_STATE_MAXIMIZED_VERT  (1<<2) /* window in maximized V state      */
 139 #define WIN_STATE_MAXIMIZED_HORIZ (1<<3) /* window in maximized H state      */
 140 #define WIN_STATE_HIDDEN          (1<<4) /* not on taskbar but window visible*/
 141 #define WIN_STATE_SHADED          (1<<5) /* shaded (MacOS / Afterstep style) */
 142 #define WIN_LAYER_ONTOP           6
 143 #define WIN_LAYER_NORMAL          4
 144 
 145 #define  URGENCY_HINT             (1<<8)
 146 #define  LAYER_ALWAYS_ON_TOP      1
 147 #define  LAYER_NORMAL             0
 148 
 149 
 150 /*
 151  * Intern a bunch of atoms we are going use.
 152  */
 153 static void
 154 awt_wm_initAtoms(void)
 155 {
 156     /* Minimize X traffic by creating atoms en mass...  This requires
 157        slightly more code but reduces number of server requests. */
 158     struct atominit {
 159         Atom *atomptr;
 160         const char *name;
 161     };
 162 
 163     /* Just add new atoms to this list */
 164     static struct atominit atom_list[] = {
 165         { &XA_WM_STATE,                      "WM_STATE"                      },
 166 
 167         { &XA_UTF8_STRING,                   "UTF8_STRING"                   },
 168 
 169         { &_XA_NET_SUPPORTING_WM_CHECK,      "_NET_SUPPORTING_WM_CHECK"      },
 170         { &_XA_NET_SUPPORTED,                "_NET_SUPPORTED"                },
 171         { &_XA_NET_WM_STATE,                 "_NET_WM_STATE"                 },
 172         { &_XA_NET_WM_STATE_MAXIMIZED_VERT,  "_NET_WM_STATE_MAXIMIZED_VERT"  },
 173         { &_XA_NET_WM_STATE_MAXIMIZED_HORZ,  "_NET_WM_STATE_MAXIMIZED_HORZ"  },
 174         { &_XA_NET_WM_STATE_SHADED,          "_NET_WM_STATE_SHADED"          },
 175         { &_XA_NET_WM_STATE_ABOVE,           "_NET_WM_STATE_ABOVE"           },
 176         { &_XA_NET_WM_STATE_BELOW,           "_NET_WM_STATE_BELOW"           },
 177         { &_XA_NET_WM_STATE_HIDDEN,          "_NET_WM_STATE_HIDDEN"          },
 178         { &_XA_NET_WM_NAME,                  "_NET_WM_NAME"                  },
 179 
 180         { &_XA_WIN_SUPPORTING_WM_CHECK,      "_WIN_SUPPORTING_WM_CHECK"      },
 181         { &_XA_WIN_PROTOCOLS,                "_WIN_PROTOCOLS"                },
 182         { &_XA_WIN_STATE,                    "_WIN_STATE"                    },
 183         { &_XA_WIN_LAYER,                    "_WIN_LAYER"                    },
 184 
 185         { &_XA_KDE_NET_WM_FRAME_STRUT,       "_KDE_NET_WM_FRAME_STRUT"       },
 186 
 187         { &_XA_E_FRAME_SIZE,                 "_E_FRAME_SIZE"                 },
 188 
 189         { &XA_KWM_WIN_ICONIFIED,             "KWM_WIN_ICONIFIED"             },
 190         { &XA_KWM_WIN_MAXIMIZED,             "KWM_WIN_MAXIMIZED"             },
 191 
 192         { &_XA_OL_DECOR_DEL,                 "_OL_DECOR_DEL"                 },
 193         { &_XA_OL_DECOR_HEADER,              "_OL_DECOR_HEADER"              },
 194         { &_XA_OL_DECOR_RESIZE,              "_OL_DECOR_RESIZE"              },
 195         { &_XA_OL_DECOR_PIN,                 "_OL_DECOR_PIN"                 },
 196         { &_XA_OL_DECOR_CLOSE,               "_OL_DECOR_CLOSE"               }
 197     };
 198 #define ATOM_LIST_LENGTH (sizeof(atom_list)/sizeof(atom_list[0]))
 199 
 200     const char *names[ATOM_LIST_LENGTH];
 201     Atom atoms[ATOM_LIST_LENGTH];
 202     Status status;
 203     size_t i;
 204 
 205     /* Fill the array of atom names */
 206     for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
 207         names[i] = atom_list[i].name;
 208     }
 209 
 210     DTRACE_PRINT("WM: initializing atoms ...  ");
 211     status = XInternAtoms(awt_display, (char**)names, ATOM_LIST_LENGTH,
 212                           False, atoms);
 213     if (status == 0) {
 214         DTRACE_PRINTLN("failed");
 215         return;
 216     }
 217 
 218     /* Store returned atoms into corresponding global variables */
 219     DTRACE_PRINTLN("ok");
 220     for (i = 0; i < ATOM_LIST_LENGTH; ++i) {
 221         *atom_list[i].atomptr = atoms[i];
 222     }
 223 #undef ATOM_LIST_LENGTH
 224 }
 225 
 226 
 227 /*
 228  * When checking for various WMs don't intern certain atoms we use to
 229  * distinguish those WMs.  Rather check if the atom is interned first.
 230  * If it's not, further tests are not necessary anyway.
 231  * This also saves older JDK a great deal of confusion (4487993).
 232  */
 233 static Boolean
 234 awt_wm_atomInterned(Atom *pa, const char *name)
 235 {
 236     DASSERT(pa != NULL);
 237     if (*pa == None) {
 238         DASSERT(name != NULL);
 239         *pa = XInternAtom(awt_display, name, True);
 240         if (*pa == None) {
 241             DTRACE_PRINTLN1("\"%s\" is not interned", name);
 242             return False;
 243         } else {
 244             return True;
 245         }
 246     } else {
 247         return True;
 248     }
 249 }
 250 
 251 
 252 
 253 /*****************************************************************************\
 254  *
 255  * DTRACE utils for various states ...
 256  *
 257 \*****************************************************************************/
 258 
 259 
 260 static void
 261 awt_wm_dtraceWMState(uint32_t wm_state)
 262 {
 263 #ifdef DEBUG
 264     DTRACE_PRINT("WM_STATE = ");
 265     switch (wm_state) {
 266       case WithdrawnState:
 267           DTRACE_PRINTLN("Withdrawn");
 268           break;
 269       case NormalState:
 270           DTRACE_PRINTLN("Normal");
 271           break;
 272       case IconicState:
 273           DTRACE_PRINTLN("Iconic");
 274           break;
 275       default:
 276           DTRACE_PRINTLN1("unknown state %d", wm_state);
 277           break;
 278     }
 279 #endif /* DEBUG */
 280 }
 281 
 282 static void
 283 awt_wm_dtraceStateNet(Atom *net_wm_state, unsigned long nitems)
 284 {
 285 #ifdef DEBUG
 286     unsigned long i;
 287 
 288     DTRACE_PRINT("_NET_WM_STATE = {");
 289     for (i = 0; i < nitems; ++i) {
 290         char *name, *print_name;
 291         name = XGetAtomName(awt_display, net_wm_state[i]);
 292         if (name == NULL) {
 293             print_name = "???";
 294         } else if (strncmp(name, "_NET_WM_STATE", 13) == 0) {
 295             print_name = name + 13; /* skip common prefix to reduce noice */
 296         } else {
 297             print_name = name;
 298         }
 299         DTRACE_PRINT1(" %s", print_name);
 300         if (name) {
 301             XFree(name);
 302         }
 303     }
 304     DTRACE_PRINTLN(" }");
 305 #endif
 306 }
 307 
 308 
 309 static void
 310 awt_wm_dtraceStateWin(uint32_t win_state)
 311 {
 312 #ifdef DEBUG
 313     DTRACE_PRINT("_WIN_STATE = {");
 314     if (win_state & WIN_STATE_STICKY) {
 315         DTRACE_PRINT(" STICKY");
 316     }
 317     if (win_state & WIN_STATE_MINIMIZED) {
 318         DTRACE_PRINT(" MINIMIZED");
 319     }
 320     if (win_state & WIN_STATE_MAXIMIZED_VERT) {
 321         DTRACE_PRINT(" MAXIMIZED_VERT");
 322     }
 323     if (win_state & WIN_STATE_MAXIMIZED_HORIZ) {
 324         DTRACE_PRINT(" MAXIMIZED_HORIZ");
 325     }
 326     if (win_state & WIN_STATE_HIDDEN) {
 327         DTRACE_PRINT(" HIDDEN");
 328     }
 329     if (win_state & WIN_STATE_SHADED) {
 330         DTRACE_PRINT(" SHADED");
 331     }
 332     DTRACE_PRINTLN(" }");
 333 #endif
 334 }
 335 
 336 
 337 static void
 338 awt_wm_dtraceStateJava(jint java_state)
 339 {
 340 #ifdef DEBUG
 341     DTRACE_PRINT("java state = ");
 342     if (java_state == java_awt_Frame_NORMAL) {
 343         DTRACE_PRINTLN("NORMAL");
 344     }
 345     else {
 346         DTRACE_PRINT("{");
 347         if (java_state & java_awt_Frame_ICONIFIED) {
 348             DTRACE_PRINT(" ICONIFIED");
 349         }
 350         if ((java_state & java_awt_Frame_MAXIMIZED_BOTH)
 351                        == java_awt_Frame_MAXIMIZED_BOTH)
 352         {
 353             DTRACE_PRINT(" MAXIMIZED_BOTH");
 354         }
 355         else if (java_state & java_awt_Frame_MAXIMIZED_HORIZ) {
 356             DTRACE_PRINT(" MAXIMIZED_HORIZ");
 357         }
 358         else if (java_state & java_awt_Frame_MAXIMIZED_VERT) {
 359             DTRACE_PRINT(" MAXIMIZED_VERT");
 360         }
 361         DTRACE_PRINTLN(" }");
 362     }
 363 #endif /* DEBUG */
 364 }
 365 
 366 
 367 
 368 /*****************************************************************************\
 369  *
 370  * Utility functions ...
 371  *
 372 \*****************************************************************************/
 373 
 374 /*
 375  * Instead of validating window id, we simply call XGetWindowProperty,
 376  * but temporary install this function as the error handler to ignore
 377  * BadWindow error.
 378  */
 379 int /* but ingored */
 380 xerror_ignore_bad_window(Display *dpy, XErrorEvent *err)
 381 {
 382     XERROR_SAVE(err);
 383     if (err->error_code == BadWindow) {
 384         DTRACE_PRINTLN("IGNORING BadWindow");
 385         return 0; /* ok to fail */
 386     }
 387     else {
 388         return (*xerror_saved_handler)(dpy, err);
 389     }
 390 }
 391 
 392 
 393 /*
 394  * Convenience wrapper for XGetWindowProperty for XA_ATOM properties.
 395  * E.g. WM_PROTOCOLS, _NET_WM_STATE, _OL_DECOR_DEL.
 396  * It's up to caller to XFree returned value.
 397  * Number of items returned is stored to nitems_ptr (if non-null).
 398  */
 399 static Atom *
 400 awt_getAtomListProperty(Window w, Atom property, unsigned long *nitems_ptr)
 401 {
 402     /* Request status */
 403     int status;
 404 
 405     /* Returns of XGetWindowProperty */
 406     Atom actual_type;
 407     int actual_format;
 408     unsigned long nitems_stub;
 409     unsigned long bytes_after;
 410     Atom *list;
 411 
 412     if (nitems_ptr == NULL) {
 413         /* Caller is not interested in the number of items,
 414            provide a stub for XGetWindowProperty */
 415         nitems_ptr = &nitems_stub;
 416     }
 417 
 418     status = XGetWindowProperty(awt_display, w,
 419                  property, 0, 0xFFFF, False, XA_ATOM,
 420                  &actual_type, &actual_format, nitems_ptr, &bytes_after,
 421                  (unsigned char **)&list);
 422 
 423     if (status != Success || list == NULL) {
 424         *nitems_ptr = 0;
 425         return NULL;
 426     }
 427 
 428     if (actual_type != XA_ATOM || actual_format != 32) {
 429         XFree(list);
 430         *nitems_ptr = 0;
 431         return NULL;
 432     }
 433 
 434     if (*nitems_ptr == 0) {
 435         XFree(list);
 436         return NULL;
 437     }
 438 
 439     return list;
 440 }
 441 
 442 
 443 /*
 444  * Auxiliary function that returns the value of 'property' of type
 445  * 'property_type' on window 'w'.  Format of the property must be 8.
 446  * Terminating zero added by XGetWindowProperty is preserved.
 447  * It's up to caller to XFree the result.
 448  */
 449 static unsigned char *
 450 awt_getProperty8(Window w, Atom property, Atom property_type)
 451 {
 452     /* Request status */
 453     int status;
 454 
 455     /* Returns of XGetWindowProperty */
 456     Atom actual_type;
 457     int actual_format;
 458     unsigned long nitems;
 459     unsigned long bytes_after;
 460     unsigned char *string;
 461 
 462     /* BadWindow is ok and will be blocked by our special handler */
 463     WITH_XERROR_HANDLER(xerror_ignore_bad_window);
 464     {
 465         status = XGetWindowProperty(awt_display, w,
 466                      property, 0, 0xFFFF, False, property_type,
 467                      &actual_type, &actual_format, &nitems, &bytes_after,
 468                      &string);
 469     }
 470     RESTORE_XERROR_HANDLER;
 471 
 472     if (status != Success || string == NULL) {
 473         return NULL;
 474     }
 475 
 476     if (actual_type != property_type || actual_format != 8) {
 477         XFree(string);
 478         return NULL;
 479     }
 480 
 481     /* XGetWindowProperty kindly supplies terminating zero */
 482     return string;
 483 }
 484 
 485 
 486 /*
 487  * Auxiliary function that returns the value of 'property' of type
 488  * 'property_type' on window 'w'.  Format of the property must be 32.
 489  */
 490 static int32_t
 491 awt_getProperty32(Window w, Atom property, Atom property_type)
 492 {
 493     /* Property value*/
 494     int32_t value;
 495 
 496     /* Request status */
 497     int status;
 498 
 499     /* Returns of XGetWindowProperty */
 500     Atom actual_type;
 501     int actual_format;
 502     unsigned long nitems;
 503     unsigned long bytes_after;
 504     long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
 505 
 506     /* BadWindow is ok and will be blocked by our special handler */
 507     WITH_XERROR_HANDLER(xerror_ignore_bad_window);
 508     {
 509         status = XGetWindowProperty(awt_display, w,
 510                      property, 0, 1, False, property_type,
 511                      &actual_type, &actual_format, &nitems, &bytes_after,
 512                      (unsigned char **)&data);
 513     }
 514     RESTORE_XERROR_HANDLER;
 515 
 516     if (status != Success || data == NULL) {
 517         return 0;
 518     }
 519 
 520     if (actual_type != property_type || actual_format != 32) {
 521         XFree(data);            /* NULL data already catched above */
 522         return 0;
 523     }
 524 
 525     value = (int32_t)*data;
 526     XFree(data);
 527 
 528     return value;
 529 }
 530 
 531 
 532 
 533 /*****************************************************************************\
 534  *
 535  * Detecting WM ...
 536  *
 537 \*****************************************************************************/
 538 
 539 
 540 
 541 /*
 542  * Check for anchor_prop(anchor_type) on root, take the value as the
 543  * window id and check if that window exists and has anchor_prop(anchor_type)
 544  * with the same value (i.e. pointing back to self).
 545  *
 546  * Returns the anchor window, as some WM may put interesting stuff in
 547  * its properties (e.g. sawfish).
 548  */
 549 static Window
 550 awt_wm_checkAnchor(Atom anchor_prop, Atom anchor_type)
 551 {
 552     Window root_xref;
 553     Window self_xref;
 554 
 555     root_xref = (Window)awt_getProperty32(DefaultRootWindow(awt_display),
 556                                           anchor_prop, anchor_type);
 557     if (root_xref == None) {
 558         DTRACE_PRINTLN("no");
 559         return None;
 560     }
 561 
 562     DTRACE_PRINT1("0x%x ...  ", (unsigned int)root_xref);
 563     self_xref = (Window)awt_getProperty32(root_xref,
 564                                           anchor_prop, anchor_type);
 565     if (self_xref != root_xref) {
 566         DTRACE_PRINTLN("stale");
 567         return None;
 568     }
 569 
 570     DTRACE_PRINTLN("ok");
 571     return self_xref;
 572 }
 573 
 574 
 575 /*
 576  * New WM spec: KDE 2.0.1, sawfish 0.3x, ...
 577  * <http://www.freedesktop.org/standards/wm-spec.html>
 578  */
 579 static Window
 580 awt_wm_isNetSupporting(void)
 581 {
 582     static Boolean checked = False;
 583     static Window isNetSupporting = None;
 584 
 585     if (checked) {
 586         return isNetSupporting;
 587     }
 588 
 589     DTRACE_PRINT("WM: checking for _NET_SUPPORTING ...  ");
 590     isNetSupporting = awt_wm_checkAnchor(_XA_NET_SUPPORTING_WM_CHECK,
 591                                          XA_WINDOW);
 592     checked = True;
 593     return isNetSupporting;
 594 }
 595 
 596 
 597 /*
 598  * Old Gnome WM spec: WindowMaker, Enlightenment, IceWM ...
 599  * <http://developer.gnome.org/doc/standards/wm/book1.html>
 600  */
 601 static Window
 602 awt_wm_isWinSupporting(void)
 603 {
 604     static Boolean checked = False;
 605     static Window isWinSupporting = None;
 606 
 607     if (checked) {
 608         return isWinSupporting;
 609     }
 610 
 611     DTRACE_PRINT("WM: checking for _WIN_SUPPORTING ...  ");
 612     isWinSupporting = awt_wm_checkAnchor(_XA_WIN_SUPPORTING_WM_CHECK,
 613                                          XA_CARDINAL);
 614     checked = True;
 615     return isWinSupporting;
 616 }
 617 
 618 
 619 /*
 620  * Check that that the list of protocols specified by WM in property
 621  * named LIST_NAME on the root window contains protocol PROTO.
 622  */
 623 static Boolean
 624 awt_wm_checkProtocol(Atom list_name, Atom proto)
 625 {
 626     Atom *protocols;
 627     unsigned long nproto;
 628     Boolean found;
 629     unsigned long i;
 630 
 631     protocols = awt_getAtomListProperty(DefaultRootWindow(awt_display),
 632                                         list_name, &nproto);
 633     if (protocols == NULL) {
 634         return False;
 635     }
 636 
 637     found = False;
 638     for (i = 0; i < nproto; ++i) {
 639         if (protocols[i] == proto) {
 640             found = True;
 641             break;
 642         }
 643     }
 644 
 645     if (protocols != NULL) {
 646         XFree(protocols);
 647     }
 648     return found;
 649 }
 650 
 651 static Boolean
 652 awt_wm_doStateProtocolNet(void)
 653 {
 654     static Boolean checked = False;
 655     static Boolean supported = False;
 656 
 657     if (checked) {
 658         return supported;
 659     }
 660 
 661     if (awt_wm_isNetSupporting()) {
 662         DTRACE_PRINT("WM: checking for _NET_WM_STATE in _NET_SUPPORTED ...  ");
 663         supported = awt_wm_checkProtocol(_XA_NET_SUPPORTED, _XA_NET_WM_STATE);
 664         DTRACE_PRINTLN1("%s", supported ? "yes" : "no");
 665     }
 666 
 667     checked = True;
 668     return supported;
 669 }
 670 
 671 static Boolean
 672 awt_wm_doStateProtocolWin(void)
 673 {
 674     static Boolean checked = False;
 675     static Boolean supported = False;
 676 
 677     if (checked) {
 678         return supported;
 679     }
 680 
 681     if (awt_wm_isWinSupporting()) {
 682         DTRACE_PRINT("WM: checking for _WIN_STATE in _WIN_PROTOCOLS ...  ");
 683         supported = awt_wm_checkProtocol(_XA_WIN_PROTOCOLS, _XA_WIN_STATE);
 684         DTRACE_PRINTLN1("%s", supported ? "yes" : "no");
 685     }
 686     checked = True;
 687     return supported;
 688 }
 689 
 690 
 691 
 692 /*
 693  * Helper function for awt_wm_isEnlightenment.
 694  * Enlightenment uses STRING property for its comms window id.  Gaaa!
 695  * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
 696  * is "WINID %8x".  Gee, I haven't been using scanf for *ages*... :-)
 697  */
 698 static Window
 699 awt_getECommsWindowIDProperty(Window w)
 700 {
 701     static Atom XA_ENLIGHTENMENT_COMMS = None;
 702 
 703     /* Property value*/
 704     Window value;
 705 
 706     /* Request status */
 707     int status;
 708 
 709     /* Returns of XGetWindowProperty */
 710     Atom actual_type;
 711     int actual_format;
 712     unsigned long nitems;
 713     unsigned long bytes_after;
 714     unsigned char *data;
 715 
 716     if (!awt_wm_atomInterned(&XA_ENLIGHTENMENT_COMMS, "ENLIGHTENMENT_COMMS")) {
 717         return False;
 718     }
 719 
 720     /* BadWindow is ok and will be blocked by our special handler */
 721     WITH_XERROR_HANDLER(xerror_ignore_bad_window);
 722     {
 723         status = XGetWindowProperty(awt_display, w,
 724                      XA_ENLIGHTENMENT_COMMS, 0, 14, False, XA_STRING,
 725                      &actual_type, &actual_format, &nitems, &bytes_after,
 726                      &data);
 727     }
 728     RESTORE_XERROR_HANDLER;
 729 
 730     if (status != Success || data == NULL) {
 731         DTRACE_PRINTLN("no ENLIGHTENMENT_COMMS");
 732         return None;
 733     }
 734 
 735     if (actual_type != XA_STRING || actual_format != 8
 736         || nitems != 14 || bytes_after != 0)
 737     {
 738         DTRACE_PRINTLN("malformed ENLIGHTENMENT_COMMS");
 739         XFree(data);            /* NULL data already catched above */
 740         return None;
 741     }
 742 
 743     value = None;
 744     sscanf((char *)data, "WINID %8lx", &value); /* NB: 64 bit: XID is long */
 745     XFree(data);
 746 
 747     return value;
 748 }
 749 
 750 
 751 /*
 752  * Is Enlightenment WM running?  Congruent to awt_wm_checkAnchor, but
 753  * uses STRING property peculiar to Enlightenment.
 754  */
 755 static Boolean
 756 awt_wm_isEnlightenment(void)
 757 {
 758     Window root_xref;
 759     Window self_xref;
 760 
 761     DTRACE_PRINT("WM: checking for Enlightenment ...  ");
 762     root_xref = awt_getECommsWindowIDProperty(DefaultRootWindow(awt_display));
 763     if (root_xref == None) {
 764         return False;
 765     }
 766 
 767     DTRACE_PRINT1("0x%x ...  ", root_xref);
 768     self_xref = awt_getECommsWindowIDProperty(root_xref);
 769     if (self_xref != root_xref) {
 770         return False;
 771     }
 772 
 773     DTRACE_PRINTLN("ok");
 774     return True;
 775 }
 776 
 777 
 778 /*
 779  * Is CDE running?
 780  *
 781  * XXX: This is hairy...  CDE is MWM as well.  It seems we simply test
 782  * for default setup and will be bitten if user changes things...
 783  *
 784  * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root.  Take the
 785  * second element of the property and check for presence of
 786  * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
 787  *
 788  * XXX: Any header that defines this structures???
 789  */
 790 static Boolean
 791 awt_wm_isCDE(void)
 792 {
 793     static Atom _XA_DT_SM_WINDOW_INFO = None;
 794     static Atom _XA_DT_SM_STATE_INFO = None;
 795 
 796     /* Property value*/
 797     Window wmwin;
 798 
 799     /* Request status */
 800     int status;
 801 
 802     /* Returns of XGetWindowProperty */
 803     Atom actual_type;
 804     int actual_format;
 805     unsigned long nitems;
 806     unsigned long bytes_after;
 807     long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
 808 
 809     DTRACE_PRINT("WM: checking for CDE ...  ");
 810 
 811     if (!awt_wm_atomInterned(&_XA_DT_SM_WINDOW_INFO, "_DT_SM_WINDOW_INFO")) {
 812         return False;
 813     }
 814 
 815     status = XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
 816                  _XA_DT_SM_WINDOW_INFO, 0, 2, False, _XA_DT_SM_WINDOW_INFO,
 817                  &actual_type, &actual_format, &nitems, &bytes_after,
 818                  (unsigned char **)&data);
 819 
 820     if (status != Success || data == NULL) {
 821         DTRACE_PRINTLN("no _DT_SM_WINDOW_INFO on root");
 822         return False;
 823     }
 824 
 825     if (actual_type != _XA_DT_SM_WINDOW_INFO || actual_format != 32
 826         || nitems != 2 || bytes_after != 0)
 827     {
 828         DTRACE_PRINTLN("malformed _DT_SM_WINDOW_INFO on root");
 829         XFree(data);            /* NULL data already catched above */
 830         return False;
 831     }
 832 
 833     wmwin = (Window)data[1];
 834     XFree(data);
 835 
 836     /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
 837 
 838     if (!awt_wm_atomInterned(&_XA_DT_SM_STATE_INFO, "_DT_SM_STATE_INFO")) {
 839         return False;
 840     }
 841 
 842     /* BadWindow is ok and will be blocked by our special handler */
 843     WITH_XERROR_HANDLER(xerror_ignore_bad_window);
 844     {
 845         status = XGetWindowProperty(awt_display, wmwin,
 846                      _XA_DT_SM_STATE_INFO, 0, 1, False, _XA_DT_SM_STATE_INFO,
 847                      &actual_type, &actual_format, &nitems, &bytes_after,
 848                      (unsigned char **)&data);
 849     }
 850     RESTORE_XERROR_HANDLER;
 851 
 852     if (status != Success || data == NULL) {
 853         DTRACE_PRINTLN("no _DT_SM_STATE_INFO");
 854         return False;
 855     }
 856 
 857     if (actual_type != _XA_DT_SM_STATE_INFO || actual_format != 32) {
 858         DTRACE_PRINTLN("malformed _DT_SM_STATE_INFO");
 859         XFree(data);            /* NULL data already catched above */
 860         return False;
 861     }
 862 
 863     DTRACE_PRINTLN("yes");
 864     XFree(data);
 865     return True;
 866 }
 867 
 868 /*
 869  * Is MWM running?  (Note that CDE will test positive as well).
 870  *
 871  * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root.  Take the
 872  * second element of the property and check for presence of
 873  * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
 874  */
 875 static Boolean
 876 awt_wm_isMotif(void)
 877 {
 878     /*
 879      * Grr.  Motif just had to be different, ain't it!?  Everyone use
 880      * "XA" for things of type Atom, but motif folks chose to define
 881      * _XA_MOTIF_* to be atom *names*.  How pathetic...
 882      */
 883 #undef _XA_MOTIF_WM_INFO
 884     static Atom _XA_MOTIF_WM_INFO = None;
 885     static Atom _XA_DT_WORKSPACE_CURRENT = None;
 886 
 887     /* Property value */
 888     Window wmwin;
 889     Atom *curws;
 890 
 891     /* Request status */
 892     int status;
 893 
 894     /* Returns of XGetWindowProperty */
 895     Atom actual_type;
 896     int actual_format;
 897     unsigned long nitems;
 898     unsigned long bytes_after;
 899     long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
 900 
 901     DTRACE_PRINT("WM: checking for MWM ...  ");
 902 
 903     if (!awt_wm_atomInterned(&_XA_MOTIF_WM_INFO, "_MOTIF_WM_INFO")
 904         || !awt_wm_atomInterned(&_XA_DT_WORKSPACE_CURRENT, "_DT_WORKSPACE_CURRENT"))
 905     {
 906         return False;
 907     }
 908 
 909 
 910     status = XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
 911                  _XA_MOTIF_WM_INFO, 0, PROP_MOTIF_WM_INFO_ELEMENTS, False,
 912                  _XA_MOTIF_WM_INFO, &actual_type,
 913                  &actual_format, &nitems, &bytes_after,
 914                  (unsigned char **)&data);
 915 
 916     if (status != Success || data == NULL) {
 917         DTRACE_PRINTLN("no _MOTIF_WM_INFO on root");
 918         return False;
 919     }
 920 
 921     if (actual_type != _XA_MOTIF_WM_INFO || actual_format != 32
 922         || nitems != PROP_MOTIF_WM_INFO_ELEMENTS || bytes_after != 0)
 923     {
 924         DTRACE_PRINTLN("malformed _MOTIF_WM_INFO on root");
 925         XFree(data);            /* NULL data already catched above */
 926         return False;
 927     }
 928 
 929     /* NB: 64 bit: Cannot cast data to MotifWmInfo */
 930     wmwin = (Window)data[1];
 931     XFree(data);
 932 
 933     /* Now check that this window has _DT_WORKSPACE_CURRENT */
 934     curws = awt_getAtomListProperty(wmwin, _XA_DT_WORKSPACE_CURRENT, NULL);
 935     if (curws == NULL) {
 936         DTRACE_PRINTLN("no _DT_WORKSPACE_CURRENT");
 937         return False;
 938     }
 939 
 940     DTRACE_PRINTLN("yes");
 941     XFree(curws);
 942     return True;
 943 }
 944 
 945 
 946 static Boolean
 947 awt_wm_isNetWMName(char *name)
 948 {
 949     Window anchor;
 950     unsigned char *net_wm_name;
 951     Boolean matched;
 952 
 953     anchor = awt_wm_isNetSupporting();
 954     if (anchor == None) {
 955         return False;
 956     }
 957 
 958     DTRACE_PRINT1("WM: checking for %s by _NET_WM_NAME ...  ", name);
 959 
 960     /*
 961      * Check both UTF8_STRING and STRING.  We only call this function
 962      * with ASCII names and UTF8 preserves ASCII bit-wise.  wm-spec
 963      * mandates UTF8_STRING for _NET_WM_NAME but at least sawfish-1.0
 964      * still uses STRING.  (mmm, moving targets...).
 965      */
 966     net_wm_name = awt_getProperty8(anchor, _XA_NET_WM_NAME, XA_UTF8_STRING);
 967     if (net_wm_name == NULL) {
 968         net_wm_name = awt_getProperty8(anchor, _XA_NET_WM_NAME, XA_STRING);
 969     }
 970 
 971     if (net_wm_name == NULL) {
 972         DTRACE_PRINTLN("no (missing _NET_WM_NAME)");
 973         return False;
 974     }
 975 
 976     matched = (strcmp((char *)net_wm_name, name) == 0);
 977     if (matched) {
 978         DTRACE_PRINTLN("yes");
 979     } else {
 980         DTRACE_PRINTLN1("no (_NET_WM_NAME = \"%s\")", net_wm_name);
 981     }
 982     XFree(net_wm_name);
 983     return matched;
 984 }
 985 
 986 /*
 987  * Is Sawfish running?
 988  */
 989 static Boolean
 990 awt_wm_isSawfish(void)
 991 {
 992     return awt_wm_isNetWMName("Sawfish");
 993 }
 994 
 995 /*
 996  * Is KDE2 (KWin) running?
 997  */
 998 static Boolean
 999 awt_wm_isKDE2(void)
1000 {
1001     return awt_wm_isNetWMName("KWin");
1002 }
1003 
1004 
1005 /*
1006  * Is Metacity running?
1007  */
1008 static Boolean
1009 awt_wm_isMetacity(void)
1010 {
1011     return awt_wm_isNetWMName("Metacity");
1012 }
1013 
1014 
1015 /*
1016  * Temporary error handler that ensures that we know if
1017  * XChangeProperty succeeded or not.
1018  */
1019 static int /* but ignored */
1020 xerror_verify_change_property(Display *dpy, XErrorEvent *err)
1021 {
1022     XERROR_SAVE(err);
1023     if (err->request_code == X_ChangeProperty) {
1024         return 0;
1025     }
1026     else {
1027         return (*xerror_saved_handler)(dpy, err);
1028     }
1029 }
1030 
1031 
1032 /*
1033  * Prepare IceWM check.
1034  *
1035  * The only way to detect IceWM, seems to be by setting
1036  * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
1037  * was immediately deleted by IceWM.
1038  *
1039  * But messing with PropertyNotify here is way too much trouble, so
1040  * approximate the check by setting the property in this function and
1041  * checking if it still exists later on.
1042  *
1043  * Gaa, dirty dances...
1044  */
1045 static Boolean
1046 awt_wm_prepareIsIceWM(void)
1047 {
1048     static Atom _XA_ICEWM_WINOPTHINT = None;
1049 
1050     /*
1051      * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
1052      * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
1053      */
1054     static unsigned char opt[] = {
1055         'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
1056         'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
1057         '0','\0'
1058     };
1059 
1060     DTRACE_PRINT("WM: scheduling check for IceWM ...  ");
1061 
1062     if (!awt_wm_atomInterned(&_XA_ICEWM_WINOPTHINT, "_ICEWM_WINOPTHINT")) {
1063         return False;
1064     }
1065 
1066     WITH_XERROR_HANDLER(xerror_verify_change_property);
1067     {
1068         XChangeProperty(awt_display, DefaultRootWindow(awt_display),
1069                         _XA_ICEWM_WINOPTHINT, _XA_ICEWM_WINOPTHINT, 8,
1070                         PropModeReplace, opt, sizeof(opt));
1071     }
1072     RESTORE_XERROR_HANDLER;
1073 
1074     if (xerror_code != Success) {
1075         DTRACE_PRINTLN1("can't set _ICEWM_WINOPTHINT, error = %d",
1076                         xerror_code);
1077         return False;
1078     }
1079     else {
1080         DTRACE_PRINTLN("scheduled");
1081         return True;
1082     }
1083 }
1084 
1085 /*
1086  * Is IceWM running?
1087  *
1088  * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
1089  * false positive will be reported.
1090  */
1091 static Boolean
1092 awt_wm_isIceWM(void)
1093 {
1094     static Atom _XA_ICEWM_WINOPTHINT = None;
1095 
1096     /* Request status */
1097     int status;
1098 
1099     /* Returns of XGetWindowProperty */
1100     Atom actual_type;
1101     int actual_format;
1102     unsigned long nitems;
1103     unsigned long bytes_after;
1104     unsigned char *data;
1105 
1106     DTRACE_PRINT("WM: checking for IceWM ...  ");
1107 
1108     if (!awt_wm_atomInterned(&_XA_ICEWM_WINOPTHINT, "_ICEWM_WINOPTHINT")) {
1109         return False;
1110     }
1111 
1112     XGetWindowProperty(awt_display, DefaultRootWindow(awt_display),
1113                  _XA_ICEWM_WINOPTHINT, 0, 0xFFFF, True, /* NB: deleting! */
1114                  _XA_ICEWM_WINOPTHINT, &actual_type,
1115                  &actual_format, &nitems, &bytes_after,
1116                  &data);
1117 
1118     if (data != NULL) {
1119         XFree(data);
1120     }
1121 
1122     if (actual_type == None) {
1123         DTRACE_PRINTLN("yes");
1124         return True;
1125     }
1126     else {
1127         DTRACE_PRINTLN("no");
1128         return False;
1129     }
1130 }
1131 
1132 /*
1133  * Is OpenLook WM running?
1134  *
1135  * This one is pretty lame, but the only property peculiar to OLWM is
1136  * _SUN_WM_PROTOCOLS(ATOM[]).  Fortunately, olwm deletes it on exit.
1137  */
1138 static Boolean
1139 awt_wm_isOpenLook(void)
1140 {
1141     static Atom _XA_SUN_WM_PROTOCOLS = None;
1142     Atom *list;
1143 
1144     DTRACE_PRINT("WM: checking for OpenLook WM ...  ");
1145 
1146     if (!awt_wm_atomInterned(&_XA_SUN_WM_PROTOCOLS, "_SUN_WM_PROTOCOLS")) {
1147         return False;
1148     }
1149 
1150     list = awt_getAtomListProperty(DefaultRootWindow(awt_display),
1151                                    _XA_SUN_WM_PROTOCOLS, NULL);
1152     if (list == NULL) {
1153         DTRACE_PRINTLN("no _SUN_WM_PROTOCOLS on root");
1154         return False;
1155     }
1156 
1157     DTRACE_PRINTLN("yes");
1158     XFree(list);
1159     return True;
1160 }
1161 
1162 
1163 
1164 static Boolean winmgr_running = False;
1165 
1166 /*
1167  * Temporary error handler that checks if selecting for
1168  * SubstructureRedirect failed.
1169  */
1170 static int /* but ignored */
1171 xerror_detect_wm(Display *dpy, XErrorEvent *err)
1172 {
1173     XERROR_SAVE(err);
1174     if (err->request_code == X_ChangeWindowAttributes
1175         && err->error_code == BadAccess)
1176     {
1177         DTRACE_PRINTLN("some WM is running (hmm, we'll see)");
1178         winmgr_running = True;
1179         return 0;
1180     }
1181     else {
1182         return (*xerror_saved_handler)(dpy, err);
1183     }
1184 }
1185 
1186 
1187 /*
1188  * Make an educated guess about running window manager.
1189  * XXX: ideally, we should detect wm restart.
1190  */
1191 enum wmgr_t
1192 awt_wm_getRunningWM(void)
1193 {
1194     /*
1195      * Ideally, we should support cases when a different WM is started
1196      * during a Java app lifetime.
1197      */
1198     static enum wmgr_t awt_wmgr = UNDETERMINED_WM;
1199 
1200     XSetWindowAttributes substruct;
1201     const char *vendor_string;
1202     Boolean doIsIceWM;
1203 
1204     if (awt_wmgr != UNDETERMINED_WM) {
1205         return awt_wmgr;
1206     }
1207 
1208     /*
1209      * Quick checks for specific servers.
1210      */
1211     vendor_string = ServerVendor(awt_display);
1212     if (strstr(vendor_string, "eXcursion") != NULL) {
1213         /*
1214          * Use NO_WM since in all other aspects eXcursion is like not
1215          * having a window manager running. I.e. it does not reparent
1216          * top level shells.
1217          */
1218         DTRACE_PRINTLN("WM: eXcursion detected - treating as NO_WM");
1219         awt_wmgr = NO_WM;
1220         return awt_wmgr;
1221     }
1222 
1223     /*
1224      * If *any* window manager is running?
1225      *
1226      * Try selecting for SubstructureRedirect, that only one client
1227      * can select for, and if the request fails, than some other WM is
1228      * already running.
1229      */
1230     winmgr_running = 0;
1231     substruct.event_mask = SubstructureRedirectMask;
1232 
1233     DTRACE_PRINT("WM: trying SubstructureRedirect ...  ");
1234     WITH_XERROR_HANDLER(xerror_detect_wm);
1235     {
1236         XChangeWindowAttributes(awt_display, DefaultRootWindow(awt_display),
1237                                 CWEventMask, &substruct);
1238     }
1239     RESTORE_XERROR_HANDLER;
1240 
1241     /*
1242      * If no WM is running than our selection for SubstructureRedirect
1243      * succeeded and needs to be undone (hey we are *not* a WM ;-).
1244      */
1245     if (!winmgr_running) {
1246         DTRACE_PRINTLN("no WM is running");
1247         awt_wmgr = NO_WM;
1248         substruct.event_mask = 0;
1249         XChangeWindowAttributes(awt_display, DefaultRootWindow(awt_display),
1250                                 CWEventMask, &substruct);
1251         return NO_WM;
1252     }
1253 
1254     /* actual check for IceWM to follow below */
1255     doIsIceWM = awt_wm_prepareIsIceWM(); /* and let IceWM to act */
1256 
1257     if (awt_wm_isNetSupporting()) {
1258         awt_wm_doStateProtocolNet();
1259     }
1260     if (awt_wm_isWinSupporting()) {
1261         awt_wm_doStateProtocolWin();
1262     }
1263 
1264     /*
1265      * Ok, some WM is out there.  Check which one by testing for
1266      * "distinguishing" atoms.
1267      */
1268     if (doIsIceWM && awt_wm_isIceWM()) {
1269         awt_wmgr = ICE_WM;
1270     }
1271     else if (awt_wm_isEnlightenment()) {
1272         awt_wmgr = ENLIGHTEN_WM;
1273     }
1274     else if (awt_wm_isMetacity()) {
1275         awt_wmgr = METACITY_WM;
1276     }
1277     else if (awt_wm_isSawfish()) {
1278         awt_wmgr = SAWFISH_WM;
1279     }
1280     else if (awt_wm_isKDE2()) {
1281         awt_wmgr = KDE2_WM;
1282     }
1283     /*
1284      * We don't check for legacy WM when we already know that WM
1285      * supports WIN or _NET wm spec.
1286      */
1287     else if (awt_wm_isNetSupporting()) {
1288         DTRACE_PRINTLN("WM: other WM (supports _NET)");
1289         awt_wmgr = OTHER_WM;
1290     }
1291     else if (awt_wm_isWinSupporting()) {
1292         DTRACE_PRINTLN("WM: other WM (supports _WIN)");
1293         awt_wmgr = OTHER_WM;
1294     }
1295     /*
1296      * Check for legacy WMs.
1297      */
1298     else if (awt_wm_isCDE()) {  /* XXX: must come before isMotif */
1299         awt_wmgr = CDE_WM;
1300     }
1301     else if (awt_wm_isMotif()) {
1302         awt_wmgr = MOTIF_WM;
1303     }
1304     else if (awt_wm_isOpenLook()) {
1305         awt_wmgr = OPENLOOK_WM;
1306     }
1307     else {
1308         DTRACE_PRINTLN("WM: some other legacy WM");
1309         awt_wmgr = OTHER_WM;
1310     }
1311 
1312     return awt_wmgr;
1313 }
1314 
1315 
1316 /*
1317  * Some buggy WMs ignore window gravity when processing
1318  * ConfigureRequest and position window as if the gravity is Static.
1319  * We work around this in MWindowPeer.pReshape().
1320  */
1321 Boolean
1322 awt_wm_configureGravityBuggy(void)
1323 {
1324     static int env_not_checked = 1;
1325     static int env_buggy = 0;
1326 
1327     if (env_not_checked) {
1328         DTRACE_PRINT("WM: checking for _JAVA_AWT_WM_STATIC_GRAVITY in environment ...  ");
1329         if (getenv("_JAVA_AWT_WM_STATIC_GRAVITY") != NULL) {
1330             DTRACE_PRINTLN("set");
1331             env_buggy = 1;
1332         } else {
1333             DTRACE_PRINTLN("no");
1334         }
1335         env_not_checked = 0;
1336     }
1337 
1338     if (env_buggy) {
1339         return True;
1340     }
1341 
1342     switch (awt_wm_getRunningWM()) {
1343       case ICE_WM:
1344           /*
1345            * See bug #228981 at IceWM's SourceForge pages.
1346            * Latest stable version 1.0.8-6 still has this problem.
1347            */
1348           return True;
1349 
1350       case ENLIGHTEN_WM:
1351           /* At least E16 is buggy. */
1352           return True;
1353 
1354       default:
1355           return False;
1356     }
1357 }
1358 
1359 /**
1360  * Check if state is supported.
1361  * Note that a compound state is always reported as not supported.
1362  * Note also that MAXIMIZED_BOTH is considered not a compound state.
1363  * Therefore, a compound state is just ICONIFIED | anything else.
1364  *
1365  */
1366 Boolean
1367 awt_wm_supportsExtendedState(jint state)
1368 {
1369     switch (state) {
1370       case java_awt_Frame_MAXIMIZED_VERT:
1371       case java_awt_Frame_MAXIMIZED_HORIZ:
1372           /*
1373            * WMs that talk NET/WIN protocol, but do not support
1374            * unidirectional maximization.
1375            */
1376           if (awt_wm_getRunningWM() == METACITY_WM) {
1377               /* "This is a deliberate policy decision." -hp */
1378               return JNI_FALSE;
1379           }
1380           /* FALLTROUGH */
1381       case java_awt_Frame_MAXIMIZED_BOTH:
1382           return (awt_wm_doStateProtocolNet() || awt_wm_doStateProtocolWin());
1383       default:
1384           return JNI_FALSE;
1385     }
1386 }
1387 
1388 
1389 
1390 
1391 /*****************************************************************************\
1392  *
1393  * Size and decoration hints ...
1394  *
1395 \*****************************************************************************/
1396 
1397 
1398 /*
1399  * Remove size hints specified by the mask.
1400  * XXX: Why do we need this in the first place???
1401  */
1402 void
1403 awt_wm_removeSizeHints(Widget shell, long mask)
1404 {
1405     Display *dpy = XtDisplay(shell);
1406     Window shell_win = XtWindow(shell);
1407     XSizeHints *hints = XAllocSizeHints();
1408     long ignore = 0;
1409 
1410     if (hints == NULL) {
1411         DTRACE_PRINTLN("WM: removeSizeHints FAILED to allocate XSizeHints");
1412         return;
1413     }
1414 
1415     /* sanitize the mask, only do these hints */
1416     mask &= (PMaxSize|PMinSize|USPosition|PPosition);
1417 
1418     XGetWMNormalHints(dpy, shell_win, hints, &ignore);
1419     if ((hints->flags & mask) == 0) {
1420         XFree(hints);
1421         return;
1422     }
1423 
1424 #ifdef DEBUG
1425     DTRACE_PRINT("WM: removing hints");
1426 
1427     if (mask & PMaxSize) {
1428         DTRACE_PRINT(" Max = ");
1429         if (hints->flags & PMaxSize) {
1430             DTRACE_PRINT2("%d x %d;", hints->max_width, hints->max_height);
1431         } else {
1432             DTRACE_PRINT("none;");
1433         }
1434     }
1435 
1436     if (mask & PMinSize) {
1437         DTRACE_PRINT(" Min = ");
1438         if (hints->flags & PMinSize) {
1439             DTRACE_PRINT2("%d x %d;", hints->min_width, hints->min_height);
1440         } else {
1441             DTRACE_PRINT("none;");
1442         }
1443     }
1444 
1445     DTRACE_PRINTLN("");
1446 #endif
1447 
1448     hints->flags &= ~mask;
1449     XSetWMNormalHints(dpy, shell_win, hints);
1450     XFree(hints);
1451 }
1452 
1453 /*
1454  *
1455  *
1456  */
1457 static void
1458 awt_wm_proclaimUrgency(struct FrameData *wdata)
1459 {
1460     Display *dpy = XtDisplay(wdata->winData.shell);
1461     Window shell_win = XtWindow(wdata->winData.shell);
1462 
1463     XWMHints *hints = XGetWMHints(dpy, shell_win);
1464     if( hints == NULL ) {
1465        /* For now just */ return;
1466     }
1467     if ((hints->flags & URGENCY_HINT) != 0) {
1468         /* it's here already */
1469         XFree(hints);
1470         return;
1471     }
1472     hints->flags |= URGENCY_HINT;
1473     XSetWMHints(dpy, shell_win, hints);
1474     XFree(hints);
1475 }
1476 
1477 /*
1478  * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
1479  * to be subtracted from the decorations.  Normalize decoration spec
1480  * so that we can map motif decor to something else bit-by-bit in the
1481  * rest of the code.
1482  */
1483 static int
1484 awt_wm_normalizeMotifDecor(int decorations)
1485 {
1486     int d;
1487 
1488     if (!(decorations & MWM_DECOR_ALL))
1489         return decorations;     /* already normalized */
1490 
1491     d = MWM_DECOR_BORDER |MWM_DECOR_RESIZEH | MWM_DECOR_TITLE
1492         | MWM_DECOR_MENU | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE;
1493     d &= ~decorations;
1494     return d;
1495 }
1496 
1497 
1498 /*
1499  * Infer OL properties from MWM decorations.
1500  * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
1501  */
1502 static void
1503 awt_wm_setOLDecor(struct FrameData *wdata, Boolean resizable, int decorations)
1504 {
1505     Window shell_win = XtWindow(wdata->winData.shell);
1506     Atom decorDel[3];
1507     int nitems;
1508 
1509     if (shell_win == None) {
1510         DTRACE_PRINTLN("WM: setOLDecor - no window, returning");
1511         return;
1512     }
1513 
1514     decorations = awt_wm_normalizeMotifDecor(decorations);
1515     DTRACE_PRINT("WM: _OL_DECOR_DEL = {");
1516 
1517     nitems = 0;
1518     if (!(decorations & MWM_DECOR_TITLE)) {
1519         DTRACE_PRINT(" _OL_DECOR_HEADER");
1520         decorDel[nitems++] = _XA_OL_DECOR_HEADER;
1521     }
1522     if (!(decorations & (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE))) {
1523         DTRACE_PRINT(" _OL_DECOR_RESIZE");
1524         decorDel[nitems++] = _XA_OL_DECOR_RESIZE;
1525     }
1526     if (!(decorations & (MWM_DECOR_MENU | MWM_DECOR_MAXIMIZE
1527                          | MWM_DECOR_MINIMIZE)))
1528     {
1529         DTRACE_PRINT(" _OL_DECOR_CLOSE");
1530         decorDel[nitems++] = _XA_OL_DECOR_CLOSE;
1531     }
1532     DTRACE_PRINT(" }");
1533 
1534     if (nitems == 0) {
1535         DTRACE_PRINTLN(" ...  removing");
1536         XDeleteProperty(awt_display, shell_win, _XA_OL_DECOR_DEL);
1537     }
1538     else {
1539         DTRACE_PRINTLN(" ...  setting");
1540         XChangeProperty(awt_display, shell_win,
1541                         _XA_OL_DECOR_DEL, XA_ATOM, 32,
1542                         PropModeReplace, (unsigned char *)decorDel, nitems);
1543     }
1544 }
1545 
1546 /*
1547  * Set MWM decorations.  Infer MWM functions from decorations.
1548  */
1549 static void
1550 awt_wm_setMotifDecor(struct FrameData *wdata, Boolean resizable, int decorations)
1551 {
1552     int functions;
1553 
1554     /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
1555     if ((decorations & MWM_DECOR_ALL) && (decorations != MWM_DECOR_ALL)) {
1556         decorations = awt_wm_normalizeMotifDecor(decorations);
1557         DTRACE_PRINTLN1("WM: setMotifDecor normalize exclusions, decor = 0x%X",
1558                         decorations);
1559     }
1560 
1561     DTRACE_PRINT("WM: setMotifDecor functions = {");
1562     functions = 0;
1563 
1564     if (decorations & MWM_DECOR_ALL) {
1565         DTRACE_PRINT(" ALL");
1566         functions |= MWM_FUNC_ALL;
1567     }
1568     else {
1569         /*
1570          * Functions we always want to be enabled as mwm(1) and
1571          * descendants not only hide disabled functions away from
1572          * user, but also ignore corresponding requests from the
1573          * program itself (e.g. 4442047).
1574          */
1575         DTRACE_PRINT(" CLOSE MOVE MINIMIZE");
1576         functions |= (MWM_FUNC_CLOSE | MWM_FUNC_MOVE | MWM_FUNC_MINIMIZE);
1577 
1578         if (resizable) {
1579             DTRACE_PRINT(" RESIZE MAXIMIZE");
1580             functions |= MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE;
1581         }
1582     }
1583 
1584     DTRACE_PRINTLN(" }");
1585 
1586     XtVaSetValues(wdata->winData.shell,
1587                   XmNmwmDecorations, decorations,
1588                   XmNmwmFunctions, functions,
1589                   NULL);
1590 }
1591 
1592 
1593 /*
1594  * Under some window managers if shell is already mapped, we MUST
1595  * unmap and later remap in order to effect the changes we make in the
1596  * window manager decorations.
1597  *
1598  * N.B.  This unmapping / remapping of the shell exposes a bug in
1599  * X/Motif or the Motif Window Manager.  When you attempt to map a
1600  * widget which is positioned (partially) off-screen, the window is
1601  * relocated to be entirely on screen. Good idea.  But if both the x
1602  * and the y coordinates are less than the origin (0,0), the first
1603  * (re)map will move the window to the origin, and any subsequent
1604  * (re)map will relocate the window at some other point on the screen.
1605  * I have written a short Motif test program to discover this bug.
1606  * This should occur infrequently and it does not cause any real
1607  * problem.  So for now we'll let it be.
1608  */
1609 static Boolean
1610 awt_wm_needRemap()
1611 {
1612     switch (awt_wm_getRunningWM()) {
1613 #if 0 /* XXX */
1614       case OPENLOOK_WM:
1615       case MOTIF_WM:
1616       case CDE_WM:
1617       case ICE_WM:
1618       case ENLIGHTEN_WM:
1619           return True;
1620 #endif
1621       default:
1622           return True;
1623     }
1624 }
1625 
1626 /*
1627  * Set decoration hints on the shell to wdata->decor adjusted
1628  * appropriately if not resizable.
1629  */
1630 void
1631 awt_wm_setShellDecor(struct FrameData *wdata, Boolean resizable)
1632 {
1633     int decorations = wdata->decor;
1634 
1635     DTRACE_PRINTLN3("WM: setShellDecor(0x%x/0x%x, %s)",
1636                     wdata->winData.shell, XtWindow(wdata->winData.shell),
1637                     resizable ? "resizable" : "not resizable");
1638 
1639     if (!resizable) {
1640         if (decorations & MWM_DECOR_ALL) {
1641             decorations |= (MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
1642         }
1643         else {
1644             decorations &= ~(MWM_DECOR_RESIZEH | MWM_DECOR_MAXIMIZE);
1645         }
1646     }
1647 
1648     DTRACE_PRINTLN1("WM:     decorations = 0x%X", decorations);
1649     awt_wm_setMotifDecor(wdata, resizable, decorations);
1650     awt_wm_setOLDecor(wdata, resizable, decorations);
1651 
1652     /* Some WMs need remap to redecorate the window */
1653     if (wdata->isShowing && awt_wm_needRemap()) {
1654         /*
1655          * Do the re/mapping at the Xlib level.  Since we essentially
1656          * work around a WM bug we don't want this hack to be exposed
1657          * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
1658          */
1659         Display *dpy = XtDisplay(wdata->winData.shell);
1660         Window shell_win = XtWindow(wdata->winData.shell);
1661 
1662         DTRACE_PRINT("WM: setShellDecor REMAPPING ...  ");
1663         XUnmapWindow(dpy, shell_win);
1664         XSync(dpy, False);      /* give WM a chance to catch up */
1665         XMapWindow(dpy, shell_win);
1666         DTRACE_PRINTLN("done");
1667     }
1668 }
1669 
1670 
1671 /*
1672  * Make specified shell resizable.
1673  */
1674 void
1675 awt_wm_setShellResizable(struct FrameData *wdata)
1676 {
1677     DTRACE_PRINTLN2("WM: setShellResizable(0x%x/0x%x)",
1678                     wdata->winData.shell, XtWindow(wdata->winData.shell));
1679 
1680     XtVaSetValues(wdata->winData.shell,
1681                   XmNallowShellResize, True,
1682                   XmNminWidth,  XtUnspecifiedShellInt,
1683                   XmNminHeight, XtUnspecifiedShellInt,
1684                   XmNmaxWidth,  XtUnspecifiedShellInt,
1685                   XmNmaxHeight, XtUnspecifiedShellInt,
1686                   NULL);
1687 
1688     /* REMINDER: will need to revisit when setExtendedStateBounds is added */
1689     awt_wm_removeSizeHints(wdata->winData.shell, PMinSize|PMaxSize);
1690 
1691     /* Restore decorations */
1692     awt_wm_setShellDecor(wdata, True);
1693 }
1694 
1695 
1696 /*
1697  * Make specified shell non-resizable.
1698  * If justChangeSize is false, update decorations as well.
1699  */
1700 void
1701 awt_wm_setShellNotResizable(struct FrameData *wdata,
1702                             int32_t width, int32_t height,
1703                             Boolean justChangeSize)
1704 {
1705     DTRACE_PRINTLN5("WM: setShellNotResizable(0x%x/0x%x, %d, %d, %s)",
1706                     wdata->winData.shell, XtWindow(wdata->winData.shell),
1707                     width, height,
1708                     justChangeSize ? "size only" : "redecorate");
1709 
1710     /* Fix min/max size hints at the specified values */
1711     if ((width > 0) && (height > 0)) {
1712         XtVaSetValues(wdata->winData.shell,
1713                       XmNwidth,     (XtArgVal)width,
1714                       XmNheight,    (XtArgVal)height,
1715                       XmNminWidth,  (XtArgVal)width,
1716                       XmNminHeight, (XtArgVal)height,
1717                       XmNmaxWidth,  (XtArgVal)width,
1718                       XmNmaxHeight, (XtArgVal)height,
1719                       NULL);
1720     }
1721 
1722     if (!justChangeSize) {      /* update decorations */
1723         awt_wm_setShellDecor(wdata, False);
1724     }
1725 }
1726 
1727 
1728 /*
1729  * Helper function for awt_wm_getInsetsFromProp.
1730  * Read property of type CARDINAL[4] = { left, right, top, bottom }
1731  */
1732 static Boolean
1733 awt_wm_readInsetsArray(Window shell_win, Atom insets_property,
1734     int32_t *top, int32_t *left, int32_t *bottom, int32_t *right)
1735 {
1736     /* Request status */
1737     int status;
1738 
1739     /* Returns of XGetWindowProperty */
1740     Atom actual_type;
1741     int actual_format;
1742     unsigned long nitems;
1743     unsigned long bytes_after;
1744     long *insets = NULL;        /* NB: 64 bit: Format 32 props are 'long' */
1745 
1746     status = XGetWindowProperty (awt_display, shell_win,
1747                  insets_property, 0, 4, False, XA_CARDINAL,
1748                  &actual_type, &actual_format, &nitems, &bytes_after,
1749                  (unsigned char **)&insets);
1750 
1751     if (status != Success || insets == NULL) {
1752         DTRACE_PRINTLN("failed");
1753         return False;
1754     }
1755 
1756     if (actual_type != XA_CARDINAL || actual_format != 32) {
1757         DTRACE_PRINTLN("type/format mismatch");
1758         XFree(insets);
1759         return False;
1760     }
1761 
1762     *left   = (int32_t)insets[0];
1763     *right  = (int32_t)insets[1];
1764     *top    = (int32_t)insets[2];
1765     *bottom = (int32_t)insets[3];
1766     XFree(insets);
1767 
1768     /* Order is that of java.awt.Insets.toString */
1769     DTRACE_PRINTLN4("[top=%d,left=%d,bottom=%d,right=%d]",
1770                     *top, *left, *bottom, *right);
1771     return True;
1772 }
1773 
1774 /*
1775  * If WM implements the insets property - fill insets with values
1776  * specified in that property.
1777  */
1778 Boolean
1779 awt_wm_getInsetsFromProp(Window shell_win,
1780     int32_t *top, int32_t *left, int32_t *bottom, int32_t *right)
1781 {
1782     switch (awt_wm_getRunningWM()) {
1783 
1784       case ENLIGHTEN_WM:
1785           DTRACE_PRINT("WM: reading _E_FRAME_SIZE ...  ");
1786           return awt_wm_readInsetsArray(shell_win, _XA_E_FRAME_SIZE,
1787                                         top, left, bottom, right);
1788 
1789 #if 0
1790      /*
1791       * uwe: disabled for now, as KDE seems to supply bogus values
1792       * when we maximize iconified frame.  Need to verify with KDE2.1.
1793       * NB: Also note, that "external" handles (e.g. in laptop decor)
1794       * are also included in the frame strut, which is probably not
1795       * what we want.
1796       */
1797       case KDE2_WM:
1798           DTRACE_PRINT("WM: reading _KDE_NET_WM_FRAME_STRUT ...  ");
1799           return awt_wm_readInsetsArray(shell_win, _XA_KDE_NET_WM_FRAME_STRUT,
1800                                         top, left, bottom, right);
1801 #endif
1802 
1803       default:
1804           return False;
1805     }
1806 }
1807 
1808 /*
1809  * XmNiconic and Map/UnmapNotify (that XmNiconic relies on) are
1810  * unreliable, since mapping changes can happen for a virtual desktop
1811  * switch or MacOS style shading that became quite popular under X as
1812  * well.  Yes, it probably should not be this way, as it violates
1813  * ICCCM, but reality is that quite a lot of window managers abuse
1814  * mapping state.
1815  */
1816 int
1817 awt_wm_getWMState(Window shell_win)
1818 {
1819     /* Request status */
1820     int status;
1821 
1822     /* Returns of XGetWindowProperty */
1823     Atom actual_type;
1824     int actual_format;
1825     unsigned long nitems;
1826     unsigned long bytes_after;
1827     long *data;                 /* NB: 64 bit: Format 32 props are 'long' */
1828 
1829     int wm_state;
1830 
1831     status = XGetWindowProperty(awt_display, shell_win,
1832                  XA_WM_STATE, 0, 1, False, XA_WM_STATE,
1833                  &actual_type, &actual_format, &nitems, &bytes_after,
1834                  (unsigned char **)&data);
1835 
1836     if (status != Success || data == NULL) {
1837         return WithdrawnState;
1838     }
1839 
1840     if (actual_type != XA_WM_STATE) {
1841         DTRACE_PRINTLN1("WM:     WM_STATE(0x%x) - wrong type", shell_win);
1842         XFree(data);
1843         return WithdrawnState;
1844     }
1845 
1846     wm_state = (int)*data;
1847     XFree(data);
1848     return wm_state;
1849 }
1850 
1851 
1852 
1853 /*****************************************************************************\
1854  *
1855  * Reading state from properties WM puts on our window ...
1856  *
1857 \*****************************************************************************/
1858 
1859 /*
1860  * New "NET" WM spec: _NET_WM_STATE/Atom[]
1861  */
1862 static jint
1863 awt_wm_getStateNet(Window shell_win)
1864 {
1865     Atom *net_wm_state;
1866     jint java_state;
1867     unsigned long nitems;
1868     unsigned long i;
1869 
1870     net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
1871     if (nitems == 0) {
1872         DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
1873         if (net_wm_state) {
1874             XFree(net_wm_state);
1875         }
1876         return java_awt_Frame_NORMAL;
1877     }
1878 #ifdef DEBUG
1879     DTRACE_PRINT("WM:     ");
1880     awt_wm_dtraceStateNet(net_wm_state, nitems);
1881 #endif
1882 
1883     java_state = java_awt_Frame_NORMAL;
1884     for (i = 0; i < nitems; ++i) {
1885         if (net_wm_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
1886             java_state |= java_awt_Frame_MAXIMIZED_VERT;
1887         }
1888         else if (net_wm_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
1889             java_state |= java_awt_Frame_MAXIMIZED_HORIZ;
1890         }
1891     }
1892     XFree(net_wm_state);
1893     return java_state;
1894 }
1895 
1896 Boolean
1897 awt_wm_isStateNetHidden(Window shell_win)
1898 {
1899     Atom *net_wm_state;
1900     Boolean result = False;
1901     unsigned long nitems;
1902     unsigned long i;
1903 
1904     net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
1905     if (nitems == 0) {
1906         DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
1907         if (net_wm_state) {
1908             XFree(net_wm_state);
1909         }
1910         return False;
1911     }
1912 #ifdef DEBUG
1913     DTRACE_PRINT("WM:     ");
1914     awt_wm_dtraceStateNet(net_wm_state, nitems);
1915 #endif
1916 
1917     for (i = 0; i < nitems; ++i) {
1918         if (net_wm_state[i] == _XA_NET_WM_STATE_HIDDEN) {
1919             result = True;
1920         }
1921     }
1922     XFree(net_wm_state);
1923     return result;
1924 }
1925 
1926 /*
1927  * Similar code to getStateNet, to get layer state.
1928  */
1929 static int
1930 awt_wm_getLayerNet(Window shell_win)
1931 {
1932     Atom *net_wm_state;
1933     int java_state;
1934     unsigned long nitems;
1935     unsigned long i;
1936 
1937     net_wm_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
1938     if (nitems == 0) {
1939         DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
1940         if (net_wm_state) {
1941             XFree(net_wm_state);
1942         }
1943         return LAYER_NORMAL;
1944     }
1945 #ifdef DEBUG
1946     DTRACE_PRINT("WM:     ");
1947     awt_wm_dtraceStateNet(net_wm_state, nitems);
1948 #endif
1949 
1950     java_state = LAYER_NORMAL;
1951     for (i = 0; i < nitems; ++i) {
1952         if (net_wm_state[i] == _XA_NET_WM_STATE_ABOVE) {
1953             java_state = LAYER_ALWAYS_ON_TOP;
1954         }
1955     }
1956     XFree(net_wm_state);
1957     return java_state;
1958 }
1959 
1960 /*
1961  * Old Gnome spec: _WIN_STATE/CARDINAL
1962  */
1963 static jint
1964 awt_wm_getStateWin(Window shell_win)
1965 {
1966     long win_state;
1967     jint java_state;
1968 
1969     win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
1970 #ifdef DEBUG
1971     DTRACE_PRINT("WM:     ");
1972     awt_wm_dtraceStateWin(win_state);
1973 #endif
1974 
1975     java_state = java_awt_Frame_NORMAL;
1976     if (win_state & WIN_STATE_MAXIMIZED_VERT) {
1977         java_state |= java_awt_Frame_MAXIMIZED_VERT;
1978     }
1979     if (win_state & WIN_STATE_MAXIMIZED_HORIZ) {
1980         java_state |= java_awt_Frame_MAXIMIZED_HORIZ;
1981     }
1982     return java_state;
1983 }
1984 
1985 /*
1986  * Code similar to getStateWin, to get layer state.
1987  */
1988 static int
1989 awt_wm_getLayerWin(Window shell_win)
1990 {
1991     long win_state;
1992     jint java_state;
1993 
1994     win_state = awt_getProperty32(shell_win, _XA_WIN_LAYER, XA_CARDINAL);
1995 #ifdef DEBUG
1996     DTRACE_PRINT("WM:     ");
1997     awt_wm_dtraceStateWin(win_state);
1998 #endif
1999 
2000     java_state = LAYER_NORMAL;
2001     if (win_state == WIN_LAYER_ONTOP) {
2002         java_state = LAYER_ALWAYS_ON_TOP;
2003     }
2004     return java_state;
2005 }
2006 
2007 
2008 static jint
2009 awt_wm_getExtendedState(Window shell_win)
2010 {
2011     if (awt_wm_doStateProtocolNet()) {
2012         return awt_wm_getStateNet(shell_win);
2013     }
2014     else if (awt_wm_doStateProtocolWin()) {
2015         return awt_wm_getStateWin(shell_win);
2016     }
2017     else {
2018         return java_awt_Frame_NORMAL;
2019     }
2020 }
2021 
2022 jint
2023 awt_wm_getState(struct FrameData *wdata)
2024 {
2025     Window shell_win = XtWindow(wdata->winData.shell);
2026     jint java_state;
2027 
2028     DTRACE_PRINTLN2("WM: getState(0x%x/0x%x)",
2029                     wdata->winData.shell, shell_win);
2030 
2031     if (shell_win == None) {
2032         DTRACE_PRINTLN("WM:     no window, use wdata");
2033         java_state = wdata->state;
2034     }
2035     else {
2036         int wm_state = awt_wm_getWMState(shell_win);
2037         if (wm_state == WithdrawnState) {
2038             DTRACE_PRINTLN("WM:     window withdrawn, use wdata");
2039             java_state = wdata->state;
2040         }
2041         else {
2042 #ifdef DEBUG
2043             DTRACE_PRINT("WM:     ");
2044             awt_wm_dtraceWMState(wm_state);
2045 #endif
2046             if (wm_state == IconicState) {
2047                 java_state = java_awt_Frame_ICONIFIED;
2048             } else {
2049                 java_state = java_awt_Frame_NORMAL;
2050             }
2051             java_state |= awt_wm_getExtendedState(shell_win);
2052         }
2053     }
2054 
2055 #ifdef DEBUG
2056     DTRACE_PRINT("WM: ");
2057     awt_wm_dtraceStateJava(java_state);
2058 #endif
2059 
2060     return java_state;
2061 }
2062 
2063 
2064 
2065 /*****************************************************************************\
2066  *
2067  * Notice window state change when WM changes a property on the window ...
2068  *
2069 \*****************************************************************************/
2070 
2071 
2072 /*
2073  * Check if property change is a window state protocol message.
2074  * If it is - return True and return the new state in *pstate.
2075  */
2076 Boolean
2077 awt_wm_isStateChange(struct FrameData *wdata, XPropertyEvent *e, jint *pstate)
2078 {
2079     Window shell_win = XtWindow(wdata->winData.shell);
2080     Boolean is_state_change = False;
2081     int wm_state;
2082 
2083     if (!wdata->isShowing) {
2084         return False;
2085     }
2086 
2087     wm_state = awt_wm_getWMState(shell_win);
2088     if (wm_state == WithdrawnState) {
2089         return False;
2090     }
2091 
2092     if (e->atom == XA_WM_STATE) {
2093         is_state_change = True;
2094     }
2095     else if (e->atom == _XA_NET_WM_STATE) {
2096         is_state_change = awt_wm_doStateProtocolNet();
2097     }
2098     else if (e->atom == _XA_WIN_STATE) {
2099         is_state_change = awt_wm_doStateProtocolWin();
2100     }
2101 
2102     if (is_state_change) {
2103 #ifdef DEBUG
2104         Widget shell = wdata->winData.shell;
2105         char *name = XGetAtomName(XtDisplay(shell), e->atom);
2106         DTRACE_PRINTLN4("WM: PropertyNotify(0x%x/0x%x) %s %s",
2107                         shell, XtWindow(shell),
2108                         name != NULL ? name : "???",
2109                         e->state == PropertyNewValue ? "changed" : "deleted");
2110         if (name != NULL) {
2111             XFree(name);
2112         }
2113         DTRACE_PRINT("WM:     ");
2114         awt_wm_dtraceWMState(wm_state);
2115 #endif
2116         if (wm_state == IconicState) {
2117             *pstate = java_awt_Frame_ICONIFIED;
2118         } else {
2119             *pstate = java_awt_Frame_NORMAL;
2120         }
2121         *pstate |= awt_wm_getExtendedState(shell_win);
2122 
2123 #ifdef DEBUG
2124         DTRACE_PRINT("WM: ");
2125         awt_wm_dtraceStateJava(*pstate);
2126 #endif
2127     }
2128 
2129     return is_state_change;
2130 }
2131 
2132 
2133 
2134 
2135 /*****************************************************************************\
2136  *
2137  * Setting/changing window state ...
2138  *
2139 \*****************************************************************************/
2140 
2141 /*
2142  * Request a state transition from a _NET supporting WM by sending
2143  * _NET_WM_STATE ClientMessage to root window.
2144  */
2145 static void
2146 awt_wm_requestStateNet(struct FrameData *wdata, jint state)
2147 {
2148     Widget shell = wdata->winData.shell;
2149     Window shell_win = XtWindow(shell);
2150     XClientMessageEvent req;
2151     jint old_net_state;
2152     jint max_changed;
2153 
2154     /* must use awt_wm_setInitialStateNet for withdrawn windows */
2155     DASSERT(wdata->isShowing);
2156 
2157     /*
2158      * We have to use toggle for maximization because of transitions
2159      * from maximization in one direction only to maximization in the
2160      * other direction only.
2161      */
2162     old_net_state = awt_wm_getStateNet(shell_win);
2163     max_changed = (state ^ old_net_state) & java_awt_Frame_MAXIMIZED_BOTH;
2164 
2165     switch (max_changed) {
2166       case 0:
2167           DTRACE_PRINTLN("WM: requestStateNet - maximization unchanged");
2168           return;
2169 
2170       case java_awt_Frame_MAXIMIZED_HORIZ:
2171           DTRACE_PRINTLN("WM: requestStateNet - toggling MAX_HORZ");
2172           req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
2173           req.data.l[2] = 0;
2174           break;
2175 
2176       case java_awt_Frame_MAXIMIZED_VERT:
2177           DTRACE_PRINTLN("WM: requestStateNet - toggling MAX_VERT");
2178           req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
2179           req.data.l[2] = 0;
2180           break;
2181 
2182       default: /* both */
2183           DTRACE_PRINTLN("WM: requestStateNet - toggling HORZ + VERT");
2184           req.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
2185           req.data.l[2] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
2186           break;
2187     }
2188 
2189     req.type         = ClientMessage;
2190     req.window       = XtWindow(shell);
2191     req.message_type = _XA_NET_WM_STATE;
2192     req.format       = 32;
2193     req.data.l[0]    = _NET_WM_STATE_TOGGLE;
2194 
2195     XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
2196                (SubstructureRedirectMask | SubstructureNotifyMask),
2197                (XEvent *)&req);
2198 }
2199 
2200 
2201 /*
2202  * Request state transition from a Gnome WM (_WIN protocol) by sending
2203  * _WIN_STATE ClientMessage to root window.
2204  */
2205 static void
2206 awt_wm_requestStateWin(struct FrameData *wdata, jint state)
2207 {
2208     Widget shell = wdata->winData.shell;
2209     XClientMessageEvent req;
2210     long win_state;             /* typeof(XClientMessageEvent.data.l) */
2211 
2212     /* must use awt_wm_setInitialStateWin for withdrawn windows */
2213     DASSERT(wdata->isShowing);
2214 
2215     win_state = 0;
2216     if (state & java_awt_Frame_MAXIMIZED_VERT) {
2217         win_state |= WIN_STATE_MAXIMIZED_VERT;
2218     }
2219     if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
2220         win_state |= WIN_STATE_MAXIMIZED_HORIZ;
2221     }
2222 
2223     req.type         = ClientMessage;
2224     req.window       = XtWindow(shell);
2225     req.message_type = _XA_WIN_STATE;
2226     req.format       = 32;
2227     req.data.l[0]    = (WIN_STATE_MAXIMIZED_HORIZ | WIN_STATE_MAXIMIZED_VERT);
2228     req.data.l[1]    = win_state;
2229 
2230     XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
2231                (SubstructureRedirectMask | SubstructureNotifyMask),
2232                (XEvent *)&req);
2233 }
2234 
2235 
2236 /*
2237  * Specify initial state for _NET supporting WM by setting
2238  * _NET_WM_STATE property on the window to the desired state before
2239  * mapping it.
2240  */
2241 static void
2242 awt_wm_setInitialStateNet(struct FrameData *wdata, jint state)
2243 {
2244     Widget shell = wdata->winData.shell;
2245     Window shell_win = XtWindow(shell);
2246     Display *dpy = XtDisplay(shell);
2247 
2248     Atom *old_state;
2249     unsigned long nitems;
2250 
2251     /* must use awt_wm_requestStateNet for managed windows */
2252     DASSERT(!wdata->isShowing);
2253 
2254     /* Be careful to not wipe out state bits we don't understand */
2255     old_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
2256 
2257     if (nitems == 0) {
2258         /*
2259          * Empty or absent _NET_WM_STATE - set a new one if necessary.
2260          */
2261         Atom net_wm_state[AWT_NET_N_KNOWN_STATES];
2262 
2263         if (old_state != NULL) {
2264             XFree(old_state);
2265         }
2266 
2267         if (state & java_awt_Frame_MAXIMIZED_VERT) {
2268             net_wm_state[nitems++] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
2269         }
2270         if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
2271             net_wm_state[nitems++] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
2272         }
2273         DASSERT(nitems <= AWT_NET_N_KNOWN_STATES);
2274 
2275         if (nitems == 0) {
2276             DTRACE_PRINTLN("WM:     initial _NET_WM_STATE not necessary");
2277             return;
2278         }
2279 
2280 #ifdef DEBUG
2281         DTRACE_PRINT("WM:     setting initial ");
2282         awt_wm_dtraceStateNet(net_wm_state, nitems);
2283 #endif
2284         XChangeProperty(dpy, shell_win,
2285                         _XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
2286                         (unsigned char *)net_wm_state, nitems);
2287     }
2288     else {
2289         /*
2290          * Tweak existing _NET_WM_STATE, preserving bits we don't use.
2291          */
2292         jint want= state        /* which flags we want */
2293             & (java_awt_Frame_MAXIMIZED_HORIZ | java_awt_Frame_MAXIMIZED_VERT);
2294 
2295         jint has = 0;           /* which flags the window already has */
2296         int mode;               /* property mode: replace/append */
2297 
2298         Atom *new_state;        /* new _net_wm_state value */
2299         int new_nitems;
2300         unsigned long i;
2301 
2302 #ifdef DEBUG
2303         DTRACE_PRINT("WM:     already has ");
2304         awt_wm_dtraceStateNet(old_state, nitems);
2305 #endif
2306 
2307         for (i = 0; i < nitems; ++i) {
2308             if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
2309                 has |= java_awt_Frame_MAXIMIZED_HORIZ;
2310             }
2311             else if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
2312                 has |= java_awt_Frame_MAXIMIZED_VERT;
2313             }
2314         }
2315 
2316         if ((has ^ want) == 0) {
2317             DTRACE_PRINTLN("WM:     no changes to _NET_WM_STATE necessary");
2318             XFree(old_state);
2319             return;
2320         }
2321 
2322         new_nitems = 0;
2323         if (has == 0) {         /* only adding flags */
2324             new_state = calloc(AWT_NET_N_KNOWN_STATES, sizeof(Atom));
2325             mode = PropModeAppend;
2326         }
2327         else {
2328             new_state = calloc(nitems + AWT_NET_N_KNOWN_STATES, sizeof(Atom));
2329             mode = PropModeReplace;
2330         }
2331 
2332         if (has != 0) {         /* copy existing flags */
2333             DTRACE_PRINT("WM:    ");
2334             for (i = 0; i < nitems; ++i) {
2335                 if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_HORZ) {
2336                     if (want & java_awt_Frame_MAXIMIZED_HORIZ) {
2337                         DTRACE_PRINT(" keep _HORZ");
2338                     } else {
2339                         DTRACE_PRINT(" drop _HORZ");
2340                         continue;
2341                     }
2342                 }
2343                 else if (old_state[i] == _XA_NET_WM_STATE_MAXIMIZED_VERT) {
2344                     if (want & java_awt_Frame_MAXIMIZED_VERT) {
2345                         DTRACE_PRINT(" keep _VERT");
2346                     } else {
2347                         DTRACE_PRINT(" drop _VERT");
2348                         continue;
2349                     }
2350                 }
2351                 new_state[new_nitems++] = old_state[i];
2352             }
2353         }
2354 
2355         /* Add missing flags */
2356         if ((want & java_awt_Frame_MAXIMIZED_HORIZ)
2357              && !(has & java_awt_Frame_MAXIMIZED_HORIZ))
2358         {
2359             DTRACE_PRINT(" add _HORZ");
2360             new_state[new_nitems] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
2361             ++new_nitems;
2362         }
2363         if ((want & java_awt_Frame_MAXIMIZED_VERT)
2364              && !(has & java_awt_Frame_MAXIMIZED_VERT))
2365         {
2366             DTRACE_PRINT(" add _VERT");
2367             new_state[new_nitems] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
2368             ++new_nitems;
2369         }
2370 
2371         DTRACE_PRINTLN(mode == PropModeReplace ?
2372                        " ...  replacing" : " ...  appending");
2373         XChangeProperty(dpy, shell_win,
2374                         _XA_NET_WM_STATE, XA_ATOM, 32, mode,
2375                         (unsigned char *)new_state, new_nitems);
2376         XFree(old_state);
2377         XFree(new_state);
2378     }
2379 }
2380 
2381 
2382 /*
2383  * Specify initial state for a Gnome WM (_WIN protocol) by setting
2384  * WIN_STATE property on the window to the desired state before
2385  * mapping it.
2386  */
2387 static void
2388 awt_wm_setInitialStateWin(struct FrameData *wdata, jint state)
2389 {
2390     Display *dpy = XtDisplay(wdata->winData.shell);
2391     Window shell_win = XtWindow(wdata->winData.shell);
2392     long win_state, old_win_state;
2393 
2394     /* must use awt_wm_requestStateWin for managed windows */
2395     DASSERT(!wdata->isShowing);
2396 
2397     /* Be careful to not wipe out state bits we don't understand */
2398     win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
2399     old_win_state = win_state;
2400 #ifdef DEBUG
2401     if (win_state != 0) {
2402         DTRACE_PRINT("WM:     already has ");
2403         awt_wm_dtraceStateWin(win_state);
2404     }
2405 #endif
2406 
2407     /*
2408      * In their stupid quest of reinventing every wheel, Gnome WM spec
2409      * have its own "minimized" hint (instead of using initial state
2410      * and WM_STATE hints).  This is bogus, but, apparently, some WMs
2411      * pay attention.
2412      */
2413     if (state & java_awt_Frame_ICONIFIED) {
2414         win_state |= WIN_STATE_MINIMIZED;
2415     } else {
2416         win_state &= ~WIN_STATE_MINIMIZED;
2417     }
2418 
2419     if (state & java_awt_Frame_MAXIMIZED_VERT) {
2420         win_state |= WIN_STATE_MAXIMIZED_VERT;
2421     } else {
2422         win_state &= ~WIN_STATE_MAXIMIZED_VERT;
2423     }
2424 
2425     if (state & java_awt_Frame_MAXIMIZED_HORIZ) {
2426         win_state |= WIN_STATE_MAXIMIZED_HORIZ;
2427     } else {
2428         win_state &= ~WIN_STATE_MAXIMIZED_HORIZ;
2429     }
2430 
2431     if (old_win_state ^ win_state) {
2432 #ifdef DEBUG
2433         DTRACE_PRINT("WM:     setting initial ");
2434         awt_wm_dtraceStateWin(win_state);
2435 #endif
2436         XChangeProperty(dpy, shell_win,
2437           _XA_WIN_STATE, XA_CARDINAL, 32, PropModeReplace,
2438           (unsigned char *)&win_state, 1);
2439     }
2440 #ifdef DEBUG
2441     else {
2442         DTRACE_PRINTLN("WM:     no changes to _WIN_STATE necessary");
2443     }
2444 #endif
2445 }
2446 
2447 /*
2448  * Request a layer change from a _NET supporting WM by sending
2449  * _NET_WM_STATE ClientMessage to root window.
2450  */
2451 static void
2452 awt_wm_requestLayerNet(struct FrameData *wdata, int state)
2453 {
2454     Widget shell = wdata->winData.shell;
2455     Window shell_win = XtWindow(shell);
2456     XClientMessageEvent req;
2457     int currentLayer;
2458     long cmd;
2459 
2460     /* must use awt_wm_setInitialLayerNet for withdrawn windows */
2461     DASSERT(wdata->isShowing);
2462 
2463     currentLayer = awt_wm_getLayerNet(shell_win);
2464     if(state == currentLayer) {
2465        return;
2466     }
2467     cmd = currentLayer == LAYER_ALWAYS_ON_TOP && state == LAYER_NORMAL ?
2468                                                   _NET_WM_STATE_REMOVE :
2469           currentLayer == LAYER_NORMAL && state == LAYER_ALWAYS_ON_TOP  ?
2470                                                   _NET_WM_STATE_ADD :
2471                                                   _NET_WM_STATE_ADD;
2472     req.type          = ClientMessage;
2473     req.window          = XtWindow(shell);
2474     req.message_type = _XA_NET_WM_STATE;
2475     req.format          = 32;
2476     req.data.l[0]    = cmd;
2477     req.data.l[1]    = _XA_NET_WM_STATE_ABOVE;
2478     req.data.l[2]    = 0L;
2479 
2480     XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
2481            (SubstructureRedirectMask | SubstructureNotifyMask),
2482            (XEvent *)&req);
2483 }
2484 
2485 /*
2486  * Request a layer change from a Gnome WM (_WIN protocol) by sending
2487  * _WIN_LAYER ClientMessage to root window.
2488  */
2489 static void
2490 awt_wm_requestLayerWin(struct FrameData *wdata, int state)
2491 {
2492     Widget shell = wdata->winData.shell;
2493     XClientMessageEvent req;
2494     Display *dpy = XtDisplay(shell);
2495 
2496     /* must use awt_wm_setInitialLayerWin for withdrawn windows */
2497     DASSERT(wdata->isShowing);
2498 
2499     req.type          = ClientMessage;
2500     req.window          = XtWindow(shell);
2501     req.message_type = _XA_WIN_LAYER;
2502     req.format          = 32;
2503     req.data.l[0]    = state == LAYER_NORMAL ? WIN_LAYER_NORMAL : WIN_LAYER_ONTOP;
2504     req.data.l[1]    = 0L;
2505     req.data.l[2]    = 0L;
2506 
2507     XSendEvent(XtDisplay(shell), RootWindowOfScreen(XtScreen(shell)), False,
2508            /*(SubstructureRedirectMask |*/
2509                SubstructureNotifyMask,
2510            (XEvent *)&req);
2511 }
2512 /*
2513  * Specify initial layer for _NET supporting WM by setting
2514  * _NET_WM_STATE property on the window to the desired state before
2515  * mapping it.
2516  * NB: looks like it doesn't have any effect.
2517  */
2518 static void
2519 awt_wm_setInitialLayerNet(struct FrameData *wdata, int state)
2520 {
2521     Widget shell = wdata->winData.shell;
2522     Window shell_win = XtWindow(shell);
2523     Display *dpy = XtDisplay(shell);
2524 
2525     Atom *old_state;
2526     unsigned long nitems;
2527     Atom new_state = _XA_NET_WM_STATE_ABOVE;
2528 
2529     /* must use awt_wm_requestLayerNet for managed windows */
2530     DASSERT(!wdata->isShowing);
2531 
2532     /* Be careful to not wipe out state bits we don't understand */
2533     old_state = awt_getAtomListProperty(shell_win, _XA_NET_WM_STATE, &nitems);
2534 
2535     if (nitems == 0 && state != LAYER_ALWAYS_ON_TOP) {
2536         if (old_state != NULL) {
2537             XFree(old_state);
2538         }
2539         return;
2540     }else if( nitems == 0 && state == LAYER_ALWAYS_ON_TOP) {
2541         unsigned long data[2];
2542         /* create new state */
2543         if (old_state != NULL) {
2544             XFree(old_state);
2545         }
2546         nitems = 1;
2547         data[0] = new_state;
2548         data[1] = 0;
2549         XChangeProperty(dpy, shell_win,
2550                 _XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
2551                 (unsigned char *)data, nitems);
2552             XSync(dpy, False);
2553     }else { /* nitems > 0 */
2554         unsigned long i;
2555         Boolean bShift = False;
2556         int mode;
2557         for(i = 0; i < nitems; i++) {
2558             if( bShift ) {
2559                 old_state[i-1] = old_state[i];
2560             }else if( old_state[i] == _XA_NET_WM_STATE_ABOVE ) {
2561                 if(state == LAYER_ALWAYS_ON_TOP) {
2562                     /* no change necessary */
2563                     XFree(old_state);
2564                     return;
2565                 }else{
2566                     /* wipe off this atom */
2567                     bShift = True;
2568                 }
2569             }
2570         }
2571 
2572         if( bShift ) {
2573             /* atom was found and removed: change property */
2574             mode = PropModeReplace;
2575             nitems--;
2576         }else if( state != LAYER_ALWAYS_ON_TOP ) {
2577             /* atom was not found and not needed */
2578             XFree( old_state);
2579             return;
2580         }else {
2581             /* must add new atom */
2582             mode = PropModeAppend;
2583             nitems = 1;
2584         }
2585 
2586         XChangeProperty(dpy, shell_win,
2587                 _XA_NET_WM_STATE, XA_ATOM, 32, mode,
2588                 mode == PropModeAppend ?
2589                             (unsigned char *)(&new_state) :
2590                             (unsigned char *)old_state, nitems);
2591         XFree(old_state);
2592             XSync(dpy, False);
2593     }
2594 }
2595 
2596 /*
2597  * Specify initial layer for a Gnome WM (_WIN protocol) by setting
2598  * WIN_LAYER property on the window to the desired state before
2599  * mapping it.
2600  */
2601 static void
2602 awt_wm_setInitialLayerWin(struct FrameData *wdata, int state)
2603 {
2604     Display *dpy = XtDisplay(wdata->winData.shell);
2605     Window shell_win = XtWindow(wdata->winData.shell);
2606     long win_state, old_win_state;
2607     int currentLayer;
2608 
2609     /* must use awt_wm_requestLayerWin for managed windows */
2610     DASSERT(!wdata->isShowing);
2611 
2612     currentLayer = awt_wm_getLayerWin(shell_win);
2613     if( currentLayer == state ) {
2614         /* no change necessary */
2615         return;
2616     }
2617     if( state == LAYER_ALWAYS_ON_TOP ) {
2618         win_state = WIN_LAYER_ONTOP;
2619     }else {
2620         win_state = WIN_LAYER_NORMAL;
2621     }
2622 
2623     XChangeProperty(dpy, shell_win,
2624             _XA_WIN_LAYER, XA_CARDINAL, 32, PropModeReplace,
2625             (unsigned char *)&win_state, 1);
2626 }
2627 
2628 void
2629 awt_wm_setExtendedState(struct FrameData *wdata, jint state)
2630 {
2631     Display *dpy = XtDisplay(wdata->winData.shell);
2632     Window shell_win = XtWindow(wdata->winData.shell);
2633 
2634 #ifdef DEBUG
2635     DTRACE_PRINT2("WM: setExtendedState(0x%x/0x%x) ",
2636                   wdata->winData.shell, shell_win);
2637     awt_wm_dtraceStateJava(state);
2638 #endif
2639 
2640     if (wdata->isShowing) {
2641         /*
2642          * If the window is managed by WM, we should send
2643          * ClientMessage requests.
2644          */
2645         if (awt_wm_doStateProtocolNet()) {
2646             awt_wm_requestStateNet(wdata, state);
2647         }
2648         else if (awt_wm_doStateProtocolWin()) {
2649             awt_wm_requestStateWin(wdata, state);
2650         }
2651         XSync(dpy, False);
2652     }
2653     else {
2654         /*
2655          * If the window is withdrawn we should set necessary
2656          * properties directly to the window before mapping it.
2657          */
2658         if (awt_wm_doStateProtocolNet()) {
2659             awt_wm_setInitialStateNet(wdata, state);
2660         }
2661         else if (awt_wm_doStateProtocolWin()) {
2662             awt_wm_setInitialStateWin(wdata, state);
2663         }
2664 #if 1
2665         /*
2666          * Purge KWM bits.
2667          * Not really tested with KWM, only with WindowMaker.
2668          */
2669         XDeleteProperty(dpy, shell_win, XA_KWM_WIN_ICONIFIED);
2670         XDeleteProperty(dpy, shell_win, XA_KWM_WIN_MAXIMIZED);
2671 #endif /* 1 */
2672     }
2673 }
2674 
2675 static Boolean
2676 awt_wm_supportsLayersNet() {
2677     Boolean supported = awt_wm_doStateProtocolNet();
2678 
2679     /*
2680        In fact, WM may report this not supported but do support.
2681      */
2682     supported &= awt_wm_checkProtocol(_XA_NET_SUPPORTED, _XA_NET_WM_STATE_ABOVE);
2683     return supported;
2684 }
2685 
2686 static Boolean
2687 awt_wm_supportsLayersWin() {
2688     Boolean supported = awt_wm_doStateProtocolWin();
2689     /*
2690      * In fact, WM may report this supported but do not support.
2691      */
2692     supported &= awt_wm_checkProtocol(_XA_WIN_PROTOCOLS, _XA_WIN_LAYER);
2693     return supported;
2694 }
2695 
2696 void
2697 awt_wm_updateAlwaysOnTop(struct FrameData *wdata, jboolean bLayerState) {
2698     Display *dpy = XtDisplay(wdata->winData.shell);
2699     Window shell_win = XtWindow(wdata->winData.shell);
2700     int layerState = bLayerState ? LAYER_ALWAYS_ON_TOP : LAYER_NORMAL;
2701 
2702     if (wdata->isShowing) {
2703         /**
2704            We don't believe anyone, and now send both ClientMessage requests.
2705            And eg Metacity under RH 6.1 required both to work.
2706          **/
2707         awt_wm_requestLayerNet(wdata, layerState);
2708         awt_wm_requestLayerWin(wdata, layerState);
2709     } else {
2710         /**
2711            We don't believe anyone, and now set both atoms.
2712            And eg Metacity under RH 6.1 required both to work.
2713          **/
2714         awt_wm_setInitialLayerNet(wdata, layerState);
2715         awt_wm_setInitialLayerWin(wdata, layerState);
2716     }
2717     XSync(dpy, False);
2718 }
2719 
2720 /*
2721  * Work around for 4775545.  _NET version.
2722  */
2723 static void
2724 awt_wm_unshadeKludgeNet(struct FrameData *wdata)
2725 {
2726     Display *dpy = XtDisplay(wdata->winData.shell);
2727     Window shell_win = XtWindow(wdata->winData.shell);
2728     Atom *net_wm_state;
2729     Boolean shaded;
2730     unsigned long nitems;
2731     unsigned long i;
2732 
2733     net_wm_state = awt_getAtomListProperty(shell_win,
2734                                            _XA_NET_WM_STATE, &nitems);
2735     if (nitems == 0) {
2736         DTRACE_PRINTLN("WM:     _NET_WM_STATE = { }");
2737         if (net_wm_state) {
2738             XFree(net_wm_state);
2739         }
2740         return;
2741     }
2742 #ifdef DEBUG
2743     DTRACE_PRINT("WM:     ");
2744     awt_wm_dtraceStateNet(net_wm_state, nitems);
2745 #endif
2746 
2747     shaded = False;
2748     for (i = 0; i < nitems; ++i) {
2749         if (net_wm_state[i] == _XA_NET_WM_STATE_SHADED) {
2750             shaded = True;
2751             break;
2752         }
2753     }
2754 
2755     if (!shaded) {
2756         DTRACE_PRINTLN("WM:     not _SHADED, no workaround necessary");
2757         return;
2758     }
2759 
2760     DTRACE_PRINTLN("WM:     removing _SHADED");
2761     ++i;                        /* skip _SHADED  */
2762     while (i < nitems) {        /* copy the rest */
2763         net_wm_state[i-1] = net_wm_state[i];
2764         ++i;
2765     }
2766     --nitems;                   /* _SHADED has been removed */
2767 
2768 #ifdef DEBUG
2769     DTRACE_PRINT("WM:     ");
2770     awt_wm_dtraceStateNet(net_wm_state, nitems);
2771 #endif
2772 
2773     WITH_XERROR_HANDLER(xerror_verify_change_property);
2774     {
2775         XChangeProperty(dpy, shell_win,
2776                         _XA_NET_WM_STATE, XA_ATOM, 32, PropModeReplace,
2777                         (unsigned char *)net_wm_state, nitems);
2778     }
2779     RESTORE_XERROR_HANDLER;
2780 
2781     if (xerror_code != Success) {
2782         DTRACE_PRINTLN1("WM:     XChangeProperty failed, error = %d",
2783                         xerror_code);
2784     }
2785 
2786     XFree(net_wm_state);
2787 }
2788 
2789 
2790 /*
2791  * Work around for 4775545.  _WIN version.
2792  */
2793 static void
2794 awt_wm_unshadeKludgeWin(struct FrameData *wdata)
2795 {
2796     Display *dpy = XtDisplay(wdata->winData.shell);
2797     Window shell_win = XtWindow(wdata->winData.shell);
2798     long win_state;
2799 
2800     win_state = awt_getProperty32(shell_win, _XA_WIN_STATE, XA_CARDINAL);
2801 #ifdef DEBUG
2802     DTRACE_PRINT("WM:     ");
2803     awt_wm_dtraceStateWin(win_state);
2804 #endif
2805 
2806     if ((win_state & WIN_STATE_SHADED) == 0) {
2807         DTRACE_PRINTLN("WM:     not _SHADED, no workaround necessary");
2808         return;
2809     }
2810 
2811     win_state &= ~WIN_STATE_SHADED;
2812     XChangeProperty(dpy, shell_win,
2813                     _XA_WIN_STATE, XA_CARDINAL, 32, PropModeReplace,
2814                     (unsigned char *)&win_state, 1);
2815 }
2816 
2817 
2818 /*
2819  * Work around for 4775545.
2820  *
2821  * If WM exits while the top-level is shaded, the shaded hint remains
2822  * on the top-level properties.  When WM restarts and sees the shaded
2823  * window it can reparent it into a "pre-shaded" decoration frame
2824  * (Metacity does), and our insets logic will go crazy, b/c it will
2825  * see a huge nagative bottom inset.  There's no clean solution for
2826  * this, so let's just be weasels and drop the shaded hint if we
2827  * detect that WM exited.  NB: we are in for a race condition with WM
2828  * restart here.  NB2: e.g. WindowMaker saves the state in a private
2829  * property that this code knows nothing about, so this workaround is
2830  * not effective; other WMs might play similar tricks.
2831  */
2832 void
2833 awt_wm_unshadeKludge(struct FrameData *wdata)
2834 {
2835     DTRACE_PRINTLN("WM: unshade kludge");
2836     DASSERT(wdata->isShowing);
2837 
2838     if (awt_wm_doStateProtocolNet()) {
2839         awt_wm_unshadeKludgeNet(wdata);
2840     }
2841     else if (awt_wm_doStateProtocolWin()) {
2842         awt_wm_unshadeKludgeWin(wdata);
2843     }
2844 #ifdef DEBUG
2845     else {
2846         DTRACE_PRINTLN("WM:     not a _NET or _WIN supporting WM");
2847     }
2848 #endif
2849 
2850     XSync(XtDisplay(wdata->winData.shell), False);
2851 }
2852 
2853 
2854 void
2855 awt_wm_init(void)
2856 {
2857     static Boolean inited = False;
2858     if (inited) {
2859         return;
2860     }
2861 
2862     awt_wm_initAtoms();
2863     awt_wm_getRunningWM();
2864     inited = True;
2865 }
2866 
2867 Boolean awt_wm_supportsAlwaysOnTop() {
2868     return awt_wm_supportsLayersNet() || awt_wm_supportsLayersWin();
2869 }