1/*
2 * Copyright (c) 2003, 2016, 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
27/**
28 * Ported from awt_wm.c, SCCS v1.11, author Valeriy Ushakov
29 * Author: Denis Mikhalkin
30 */
31package sun.awt.X11;
32
33import sun.awt.IconInfo;
34import jdk.internal.misc.Unsafe;
35import java.awt.Insets;
36import java.awt.Frame;
37import java.awt.Rectangle;
38import java.util.Collection;
39import java.util.HashMap;
40import java.util.LinkedList;
41import java.util.regex.Matcher;
42import java.util.regex.Pattern;
43import sun.util.logging.PlatformLogger;
44
45
46/**
47 * Class incapsulating knowledge about window managers in general
48 * Descendants should provide some information about specific window manager.
49 */
50final class XWM
51{
52
53    private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XWM");
54    private static final PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XWM");
55    private static final PlatformLogger stateLog = PlatformLogger.getLogger("sun.awt.X11.states.XWM");
56
57    static final XAtom XA_MWM_HINTS = new XAtom();
58
59    private static Unsafe unsafe = XlibWrapper.unsafe;
60
61
62/* Good old ICCCM */
63    static XAtom XA_WM_STATE = new XAtom();
64
65
66    XAtom XA_UTF8_STRING = XAtom.get("UTF8_STRING");    /* like STRING but encoding is UTF-8 */
67
68/* Currently we only care about max_v and max_h in _NET_WM_STATE */
69    static final int AWT_NET_N_KNOWN_STATES=2;
70
71/* Enlightenment */
72    static final XAtom XA_E_FRAME_SIZE = new XAtom();
73
74/* KWin (KDE2) */
75    static final XAtom XA_KDE_NET_WM_FRAME_STRUT = new XAtom();
76
77/* KWM (KDE 1.x) OBSOLETE??? */
78    static final XAtom XA_KWM_WIN_ICONIFIED = new XAtom();
79    static final XAtom XA_KWM_WIN_MAXIMIZED = new XAtom();
80
81/* OpenLook */
82    static final XAtom XA_OL_DECOR_DEL = new XAtom();
83    static final XAtom XA_OL_DECOR_HEADER = new XAtom();
84    static final XAtom XA_OL_DECOR_RESIZE = new XAtom();
85    static final XAtom XA_OL_DECOR_PIN = new XAtom();
86    static final XAtom XA_OL_DECOR_CLOSE = new XAtom();
87
88/* EWMH */
89    static final XAtom XA_NET_FRAME_EXTENTS = new XAtom();
90    static final XAtom XA_NET_REQUEST_FRAME_EXTENTS = new XAtom();
91
92    static final int
93        UNDETERMINED_WM = 1,
94        NO_WM = 2,
95        OTHER_WM = 3,
96        OPENLOOK_WM = 4,
97        MOTIF_WM = 5,
98        CDE_WM = 6,
99        ENLIGHTEN_WM = 7,
100        KDE2_WM = 8,
101        SAWFISH_WM = 9,
102        ICE_WM = 10,
103        METACITY_WM = 11,
104        COMPIZ_WM = 12,
105        LG3D_WM = 13,
106        CWM_WM = 14,
107        MUTTER_WM = 15,
108        UNITY_COMPIZ_WM = 16;
109    public String toString() {
110        switch  (WMID) {
111          case NO_WM:
112              return "NO WM";
113          case OTHER_WM:
114              return "Other WM";
115          case OPENLOOK_WM:
116              return "OPENLOOK";
117          case MOTIF_WM:
118              return "MWM";
119          case CDE_WM:
120              return "DTWM";
121          case ENLIGHTEN_WM:
122              return "Enlightenment";
123          case KDE2_WM:
124              return "KWM2";
125          case SAWFISH_WM:
126              return "Sawfish";
127          case ICE_WM:
128              return "IceWM";
129          case METACITY_WM:
130              return "Metacity";
131          case COMPIZ_WM:
132              return "Compiz";
133            case UNITY_COMPIZ_WM:
134              return "Unity Compiz";
135          case LG3D_WM:
136              return "LookingGlass";
137          case CWM_WM:
138              return "CWM";
139          case MUTTER_WM:
140              return "Mutter";
141          case UNDETERMINED_WM:
142          default:
143              return "Undetermined WM";
144        }
145    }
146
147
148    int WMID;
149    static final Insets zeroInsets = new Insets(0, 0, 0, 0);
150    static final Insets defaultInsets = new Insets(25, 5, 5, 5);
151
152    XWM(int WMID) {
153        this.WMID = WMID;
154        initializeProtocols();
155        if (log.isLoggable(PlatformLogger.Level.FINE)) {
156            log.fine("Window manager: " + toString());
157        }
158    }
159    int getID() {
160        return WMID;
161    }
162
163
164    static Insets normalize(Insets insets) {
165        if (insets.top > 64 || insets.top < 0) {
166            insets.top = 28;
167        }
168        if (insets.left > 32 || insets.left < 0) {
169            insets.left = 6;
170        }
171        if (insets.right > 32 || insets.right < 0) {
172            insets.right = 6;
173        }
174        if (insets.bottom > 32 || insets.bottom < 0) {
175            insets.bottom = 6;
176        }
177        return insets;
178    }
179
180    static XNETProtocol g_net_protocol = null;
181    static XWINProtocol g_win_protocol = null;
182    static boolean isNetWMName(String name) {
183        if (g_net_protocol != null) {
184            return g_net_protocol.isWMName(name);
185        } else {
186            return false;
187        }
188    }
189
190    static void initAtoms() {
191        final Object[][] atomInitList ={
192            { XA_WM_STATE,                      "WM_STATE"                  },
193
194            { XA_KDE_NET_WM_FRAME_STRUT,    "_KDE_NET_WM_FRAME_STRUT"       },
195
196            { XA_E_FRAME_SIZE,              "_E_FRAME_SIZE"                 },
197
198            { XA_KWM_WIN_ICONIFIED,          "KWM_WIN_ICONIFIED"             },
199            { XA_KWM_WIN_MAXIMIZED,          "KWM_WIN_MAXIMIZED"             },
200
201            { XA_OL_DECOR_DEL,               "_OL_DECOR_DEL"                 },
202            { XA_OL_DECOR_HEADER,            "_OL_DECOR_HEADER"              },
203            { XA_OL_DECOR_RESIZE,            "_OL_DECOR_RESIZE"              },
204            { XA_OL_DECOR_PIN,               "_OL_DECOR_PIN"                 },
205            { XA_OL_DECOR_CLOSE,             "_OL_DECOR_CLOSE"               },
206            { XA_MWM_HINTS,                  "_MOTIF_WM_HINTS"               },
207            { XA_NET_FRAME_EXTENTS,          "_NET_FRAME_EXTENTS"            },
208            { XA_NET_REQUEST_FRAME_EXTENTS,  "_NET_REQUEST_FRAME_EXTENTS"    },
209        };
210
211        String[] names = new String[atomInitList.length];
212        for (int index = 0; index < names.length; index++) {
213            names[index] = (String)atomInitList[index][1];
214        }
215
216        int atomSize = XAtom.getAtomSize();
217        long atoms = unsafe.allocateMemory(names.length*atomSize);
218        XToolkit.awtLock();
219        try {
220            int status = XlibWrapper.XInternAtoms(XToolkit.getDisplay(), names, false, atoms);
221            if (status == 0) {
222                return;
223            }
224            for (int atom = 0, atomPtr = 0; atom < names.length; atom++, atomPtr += atomSize) {
225                ((XAtom)(atomInitList[atom][0])).setValues(XToolkit.getDisplay(), names[atom], XAtom.getAtom(atoms + atomPtr));
226            }
227        } finally {
228            XToolkit.awtUnlock();
229            unsafe.freeMemory(atoms);
230        }
231    }
232
233    /*
234     * MUST BE CALLED UNDER AWTLOCK.
235     *
236     * If *any* window manager is running?
237     *
238     * According to ICCCM 2.0 section 4.3.
239     * WM will acquire ownership of a selection named WM_Sn, where n is
240     * the screen number.
241     *
242     * No selection owner, but, perhaps it is not ICCCM compliant WM
243     * (e.g. CDE/Sawfish).
244     * Try selecting for SubstructureRedirect, that only one client
245     * can select for, and if the request fails, than some other WM is
246     * already running.
247     *
248     * We also treat eXcursion as NO_WM.
249     */
250    private static boolean isNoWM() {
251        /*
252         * Quick checks for specific servers.
253         */
254        String vendor_string = XlibWrapper.ServerVendor(XToolkit.getDisplay());
255        if (vendor_string.indexOf("eXcursion") != -1) {
256            /*
257             * Use NO_WM since in all other aspects eXcursion is like not
258             * having a window manager running. I.e. it does not reparent
259             * top level shells.
260             */
261            if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
262                insLog.finer("eXcursion means NO_WM");
263            }
264            return true;
265        }
266
267        XSetWindowAttributes substruct = new XSetWindowAttributes();
268        try {
269            /*
270             * Let's check an owner of WM_Sn selection for the default screen.
271             */
272            final long default_screen_number =
273                XlibWrapper.DefaultScreen(XToolkit.getDisplay());
274            final String selection_name = "WM_S" + default_screen_number;
275
276            long selection_owner =
277                XlibWrapper.XGetSelectionOwner(XToolkit.getDisplay(),
278                                               XAtom.get(selection_name).getAtom());
279            if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
280                insLog.finer("selection owner of " + selection_name
281                             + " is " + selection_owner);
282            }
283
284            if (selection_owner != XConstants.None) {
285                return false;
286            }
287
288            winmgr_running = false;
289            substruct.set_event_mask(XConstants.SubstructureRedirectMask);
290
291            XErrorHandlerUtil.WITH_XERROR_HANDLER(detectWMHandler);
292            XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
293                                                XToolkit.getDefaultRootWindow(),
294                                                XConstants.CWEventMask,
295                                                substruct.pData);
296            XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
297
298            /*
299             * If no WM is running then our selection for SubstructureRedirect
300             * succeeded and needs to be undone (hey we are *not* a WM ;-).
301             */
302            if (!winmgr_running) {
303                substruct.set_event_mask(0);
304                XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(),
305                                                    XToolkit.getDefaultRootWindow(),
306                                                    XConstants.CWEventMask,
307                                                    substruct.pData);
308                if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
309                    insLog.finer("It looks like there is no WM thus NO_WM");
310                }
311            }
312
313            return !winmgr_running;
314        } finally {
315            substruct.dispose();
316        }
317    }
318
319    static XAtom XA_ENLIGHTENMENT_COMMS = new XAtom("ENLIGHTENMENT_COMMS", false);
320    /*
321     * Helper function for isEnlightenment().
322     * Enlightenment uses STRING property for its comms window id.  Gaaa!
323     * The property is ENLIGHTENMENT_COMMS, STRING/8 and the string format
324     * is "WINID %8x".  Gee, I haven't been using scanf for *ages*... :-)
325     */
326    static long getECommsWindowIDProperty(long window) {
327
328        if (!XA_ENLIGHTENMENT_COMMS.isInterned()) {
329            return 0;
330        }
331
332        WindowPropertyGetter getter =
333            new WindowPropertyGetter(window, XA_ENLIGHTENMENT_COMMS, 0, 14, false,
334                                     XAtom.XA_STRING);
335        try {
336            int status = getter.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
337            if (status != XConstants.Success || getter.getData() == 0) {
338                return 0;
339            }
340
341            if (getter.getActualType() != XAtom.XA_STRING
342                || getter.getActualFormat() != 8
343                || getter.getNumberOfItems() != 14 || getter.getBytesAfter() != 0)
344            {
345                return 0;
346            }
347
348            // Convert data to String, ASCII
349            byte[] bytes = XlibWrapper.getStringBytes(getter.getData());
350            String id = new String(bytes);
351
352            if (log.isLoggable(PlatformLogger.Level.FINER)) {
353                log.finer("ENLIGHTENMENT_COMMS is " + id);
354            }
355
356            // Parse WINID
357            Pattern winIdPat = Pattern.compile("WINID\\s+(\\p{XDigit}{0,8})");
358            try {
359                Matcher match = winIdPat.matcher(id);
360                if (match.matches()) {
361                    if (log.isLoggable(PlatformLogger.Level.FINEST)) {
362                        log.finest("Match group count: " + match.groupCount());
363                    }
364                    String longId = match.group(1);
365                    if (log.isLoggable(PlatformLogger.Level.FINEST)) {
366                        log.finest("Match group 1 " + longId);
367                    }
368                    long winid = Long.parseLong(longId, 16);
369                    if (log.isLoggable(PlatformLogger.Level.FINER)) {
370                        log.finer("Enlightenment communication window " + winid);
371                    }
372                    return winid;
373                } else {
374                    log.finer("ENLIGHTENMENT_COMMS has wrong format");
375                    return 0;
376                }
377            } catch (Exception e) {
378                if (log.isLoggable(PlatformLogger.Level.FINER)) {
379                    e.printStackTrace();
380                }
381                return 0;
382            }
383        } finally {
384            getter.dispose();
385        }
386    }
387
388    /*
389     * Is Enlightenment WM running?  Congruent to awt_wm_checkAnchor, but
390     * uses STRING property peculiar to Enlightenment.
391     */
392    static boolean isEnlightenment() {
393
394        long root_xref = getECommsWindowIDProperty(XToolkit.getDefaultRootWindow());
395        if (root_xref == 0) {
396            return false;
397        }
398
399        long self_xref = getECommsWindowIDProperty(root_xref);
400        if (self_xref != root_xref) {
401            return false;
402        }
403
404        return true;
405    }
406
407    /*
408     * Is CDE running?
409     *
410     * XXX: This is hairy...  CDE is MWM as well.  It seems we simply test
411     * for default setup and will be bitten if user changes things...
412     *
413     * Check for _DT_SM_WINDOW_INFO(_DT_SM_WINDOW_INFO) on root.  Take the
414     * second element of the property and check for presence of
415     * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
416     *
417     * XXX: Any header that defines this structures???
418     */
419    static final XAtom XA_DT_SM_WINDOW_INFO = new XAtom("_DT_SM_WINDOW_INFO", false);
420    static final XAtom XA_DT_SM_STATE_INFO = new XAtom("_DT_SM_STATE_INFO", false);
421    static boolean isCDE() {
422
423        if (!XA_DT_SM_WINDOW_INFO.isInterned()) {
424            if (log.isLoggable(PlatformLogger.Level.FINER)) {
425                log.finer("{0} is not interned", XA_DT_SM_WINDOW_INFO);
426            }
427            return false;
428        }
429
430        WindowPropertyGetter getter =
431            new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
432                                     XA_DT_SM_WINDOW_INFO, 0, 2,
433                                     false, XA_DT_SM_WINDOW_INFO);
434        try {
435            int status = getter.execute();
436            if (status != XConstants.Success || getter.getData() == 0) {
437                log.finer("Getting of _DT_SM_WINDOW_INFO is not successfull");
438                return false;
439            }
440            if (getter.getActualType() != XA_DT_SM_WINDOW_INFO.getAtom()
441                || getter.getActualFormat() != 32
442                || getter.getNumberOfItems() != 2 || getter.getBytesAfter() != 0)
443            {
444                log.finer("Wrong format of _DT_SM_WINDOW_INFO");
445                return false;
446            }
447
448            long wmwin = Native.getWindow(getter.getData(), 1); //unsafe.getInt(getter.getData()+4);
449
450            if (wmwin == 0) {
451                log.fine("WARNING: DT_SM_WINDOW_INFO exists but returns zero windows");
452                return false;
453            }
454
455            /* Now check that this window has _DT_SM_STATE_INFO (ignore contents) */
456            if (!XA_DT_SM_STATE_INFO.isInterned()) {
457                if (log.isLoggable(PlatformLogger.Level.FINER)) {
458                    log.finer("{0} is not interned", XA_DT_SM_STATE_INFO);
459                }
460                return false;
461            }
462            WindowPropertyGetter getter2 =
463                new WindowPropertyGetter(wmwin, XA_DT_SM_STATE_INFO, 0, 1,
464                                         false, XA_DT_SM_STATE_INFO);
465            try {
466                status = getter2.execute(XErrorHandler.IgnoreBadWindowHandler.getInstance());
467
468
469                if (status != XConstants.Success || getter2.getData() == 0) {
470                    log.finer("Getting of _DT_SM_STATE_INFO is not successfull");
471                    return false;
472                }
473                if (getter2.getActualType() != XA_DT_SM_STATE_INFO.getAtom()
474                    || getter2.getActualFormat() != 32)
475                {
476                    log.finer("Wrong format of _DT_SM_STATE_INFO");
477                    return false;
478                }
479
480                return true;
481            } finally {
482                getter2.dispose();
483            }
484        } finally {
485            getter.dispose();
486        }
487    }
488
489    /*
490     * Is MWM running?  (Note that CDE will test positive as well).
491     *
492     * Check for _MOTIF_WM_INFO(_MOTIF_WM_INFO) on root.  Take the
493     * second element of the property and check for presence of
494     * _DT_SM_STATE_INFO(_DT_SM_STATE_INFO) on that window.
495     */
496    static final XAtom XA_MOTIF_WM_INFO = new XAtom("_MOTIF_WM_INFO", false);
497    static final XAtom XA_DT_WORKSPACE_CURRENT = new XAtom("_DT_WORKSPACE_CURRENT", false);
498    static boolean isMotif() {
499
500        if (!(XA_MOTIF_WM_INFO.isInterned()/* && XA_DT_WORKSPACE_CURRENT.isInterned()*/) ) {
501            return false;
502        }
503
504        WindowPropertyGetter getter =
505            new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
506                                     XA_MOTIF_WM_INFO, 0,
507                                     MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS,
508                                     false, XA_MOTIF_WM_INFO);
509        try {
510            int status = getter.execute();
511
512            if (status != XConstants.Success || getter.getData() == 0) {
513                return false;
514            }
515
516            if (getter.getActualType() != XA_MOTIF_WM_INFO.getAtom()
517                || getter.getActualFormat() != 32
518                || getter.getNumberOfItems() != MWMConstants.PROP_MOTIF_WM_INFO_ELEMENTS
519                || getter.getBytesAfter() != 0)
520            {
521                return false;
522            }
523
524            long wmwin = Native.getLong(getter.getData(), 1);
525            if (wmwin != 0) {
526                if (XA_DT_WORKSPACE_CURRENT.isInterned()) {
527                    /* Now check that this window has _DT_WORKSPACE_CURRENT */
528                    XAtom[] curws = XA_DT_WORKSPACE_CURRENT.getAtomListProperty(wmwin);
529                    if (curws.length == 0) {
530                        return false;
531                    }
532                    return true;
533                } else {
534                    // No DT_WORKSPACE, however in our tests MWM sometimes can be without desktop -
535                    // and that is still MWM.  So simply check for the validity of this window
536                    // (through WM_STATE property).
537                    WindowPropertyGetter state_getter =
538                        new WindowPropertyGetter(wmwin,
539                                                 XA_WM_STATE,
540                                                 0, 1, false,
541                                                 XA_WM_STATE);
542                    try {
543                        if (state_getter.execute() == XConstants.Success &&
544                            state_getter.getData() != 0 &&
545                            state_getter.getActualType() == XA_WM_STATE.getAtom())
546                        {
547                            return true;
548                        }
549                    } finally {
550                        state_getter.dispose();
551                    }
552                }
553            }
554        } finally {
555            getter.dispose();
556        }
557        return false;
558    }
559
560    /*
561     * Is Sawfish running?
562     */
563    static boolean isSawfish() {
564        return isNetWMName("Sawfish");
565    }
566
567    /*
568     * Is KDE2 (KWin) running?
569     */
570    static boolean isKDE2() {
571        return isNetWMName("KWin");
572    }
573
574    static boolean isCompiz() {
575        return isNetWMName("compiz");
576    }
577
578    static boolean isUnityCompiz() {
579        return isNetWMName("Compiz");
580    }
581
582    static boolean isLookingGlass() {
583        return isNetWMName("LG3D");
584    }
585
586    static boolean isCWM() {
587        return isNetWMName("CWM");
588    }
589
590    /*
591     * Is Metacity running?
592     */
593    static boolean isMetacity() {
594        return isNetWMName("Metacity");
595//         || (
596//             XA_NET_SUPPORTING_WM_CHECK.
597//             getIntProperty(XToolkit.getDefaultRootWindow(), XA_NET_SUPPORTING_WM_CHECK.
598//                            getIntProperty(XToolkit.getDefaultRootWindow(), XAtom.XA_CARDINAL)) == 0);
599    }
600
601    static boolean isMutter() {
602        return isNetWMName("Mutter") || isNetWMName("GNOME Shell");
603    }
604
605    static int awtWMNonReparenting = -1;
606    static boolean isNonReparentingWM() {
607        if (awtWMNonReparenting == -1) {
608            awtWMNonReparenting = (XToolkit.getEnv("_JAVA_AWT_WM_NONREPARENTING") != null) ? 1 : 0;
609        }
610        return (awtWMNonReparenting == 1 || XWM.getWMID() == XWM.COMPIZ_WM
611                || XWM.getWMID() == XWM.LG3D_WM || XWM.getWMID() == XWM.CWM_WM);
612    }
613
614    /*
615     * Prepare IceWM check.
616     *
617     * The only way to detect IceWM, seems to be by setting
618     * _ICEWM_WINOPTHINT(_ICEWM_WINOPTHINT/8) on root and checking if it
619     * was immediately deleted by IceWM.
620     *
621     * But messing with PropertyNotify here is way too much trouble, so
622     * approximate the check by setting the property in this function and
623     * checking if it still exists later on.
624     *
625     * Gaa, dirty dances...
626     */
627    static final XAtom XA_ICEWM_WINOPTHINT = new XAtom("_ICEWM_WINOPTHINT", false);
628    static final char opt[] = {
629        'A','W','T','_','I','C','E','W','M','_','T','E','S','T','\0',
630        'a','l','l','W','o','r','k','s','p','a','c','e','s','\0',
631        '0','\0'
632    };
633    static boolean prepareIsIceWM() {
634        /*
635         * Choose something innocuous: "AWT_ICEWM_TEST allWorkspaces 0".
636         * IceWM expects "class\0option\0arg\0" with zero bytes as delimiters.
637         */
638
639        if (!XA_ICEWM_WINOPTHINT.isInterned()) {
640            if (log.isLoggable(PlatformLogger.Level.FINER)) {
641                log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
642            }
643            return false;
644        }
645
646        XToolkit.awtLock();
647        try {
648            XErrorHandlerUtil.WITH_XERROR_HANDLER(XErrorHandler.VerifyChangePropertyHandler.getInstance());
649            XlibWrapper.XChangePropertyS(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
650                                         XA_ICEWM_WINOPTHINT.getAtom(),
651                                         XA_ICEWM_WINOPTHINT.getAtom(),
652                                         8, XConstants.PropModeReplace,
653                                         new String(opt));
654            XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
655
656            if ((XErrorHandlerUtil.saved_error != null) &&
657                (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) {
658                log.finer("Erorr getting XA_ICEWM_WINOPTHINT property");
659                return false;
660            }
661            log.finer("Prepared for IceWM detection");
662            return true;
663        } finally {
664            XToolkit.awtUnlock();
665        }
666    }
667
668    /*
669     * Is IceWM running?
670     *
671     * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a
672     * false positive will be reported.
673     */
674    static boolean isIceWM() {
675        if (!XA_ICEWM_WINOPTHINT.isInterned()) {
676            if (log.isLoggable(PlatformLogger.Level.FINER)) {
677                log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT);
678            }
679            return false;
680        }
681
682        WindowPropertyGetter getter =
683            new WindowPropertyGetter(XToolkit.getDefaultRootWindow(),
684                                     XA_ICEWM_WINOPTHINT, 0, 0xFFFF,
685                                     true, XA_ICEWM_WINOPTHINT);
686        try {
687            int status = getter.execute();
688            boolean res = (status == XConstants.Success && getter.getActualType() != 0);
689            if (log.isLoggable(PlatformLogger.Level.FINER)) {
690                log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res);
691            }
692            return !res || isNetWMName("IceWM");
693        } finally {
694            getter.dispose();
695        }
696    }
697
698    /*
699     * Is OpenLook WM running?
700     *
701     * This one is pretty lame, but the only property peculiar to OLWM is
702     * _SUN_WM_PROTOCOLS(ATOM[]).  Fortunately, olwm deletes it on exit.
703     */
704    static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false);
705    static boolean isOpenLook() {
706        if (!XA_SUN_WM_PROTOCOLS.isInterned()) {
707            return false;
708        }
709
710        XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit.getDefaultRootWindow());
711        return (list.length != 0);
712    }
713
714    /*
715     * Temporary error handler that checks if selecting for
716     * SubstructureRedirect failed.
717     */
718    private static boolean winmgr_running = false;
719    private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() {
720        @Override
721        public int handleError(long display, XErrorEvent err) {
722            if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) &&
723                (err.get_error_code() == XConstants.BadAccess))
724            {
725                winmgr_running = true;
726                return 0;
727            }
728            return super.handleError(display, err);
729        }
730    };
731
732    /*
733     * Make an educated guess about running window manager.
734     * XXX: ideally, we should detect wm restart.
735     */
736    static int awt_wmgr = XWM.UNDETERMINED_WM;
737    static XWM wm;
738    static XWM getWM() {
739        if (wm == null) {
740            wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
741        }
742        return wm;
743    }
744    static int getWMID() {
745        if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
746            insLog.finest("awt_wmgr = " + awt_wmgr);
747        }
748        /*
749         * Ideally, we should support cases when a different WM is started
750         * during a Java app lifetime.
751         */
752
753        if (awt_wmgr != XWM.UNDETERMINED_WM) {
754            return awt_wmgr;
755        }
756
757        XSetWindowAttributes substruct = new XSetWindowAttributes();
758        XToolkit.awtLock();
759        try {
760            if (isNoWM()) {
761                awt_wmgr = XWM.NO_WM;
762                return awt_wmgr;
763            }
764
765            // Initialize _NET protocol - used to detect Window Manager.
766            // Later, WM will initialize its own version of protocol
767            XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol();
768            l_net_protocol.detect();
769            if (log.isLoggable(PlatformLogger.Level.FINE) && l_net_protocol.active()) {
770                log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName());
771            }
772            XWINProtocol win = g_win_protocol = new XWINProtocol();
773            win.detect();
774
775            /* actual check for IceWM to follow below */
776            boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */
777
778            /*
779             * Ok, some WM is out there.  Check which one by testing for
780             * "distinguishing" atoms.
781             */
782            if (isEnlightenment()) {
783                awt_wmgr = XWM.ENLIGHTEN_WM;
784            } else if (isMetacity()) {
785                awt_wmgr = XWM.METACITY_WM;
786            } else if (isMutter()) {
787                awt_wmgr = XWM.MUTTER_WM;
788            } else if (isSawfish()) {
789                awt_wmgr = XWM.SAWFISH_WM;
790            } else if (isKDE2()) {
791                awt_wmgr =XWM.KDE2_WM;
792            } else if (isCompiz()) {
793                awt_wmgr = XWM.COMPIZ_WM;
794            } else if (isLookingGlass()) {
795                awt_wmgr = LG3D_WM;
796            } else if (isCWM()) {
797                awt_wmgr = CWM_WM;
798            } else if (doIsIceWM && isIceWM()) {
799                awt_wmgr = XWM.ICE_WM;
800            } else if (isUnityCompiz()) {
801                awt_wmgr = XWM.UNITY_COMPIZ_WM;
802            }
803            /*
804             * We don't check for legacy WM when we already know that WM
805             * supports WIN or _NET wm spec.
806             */
807            else if (l_net_protocol.active()) {
808                awt_wmgr = XWM.OTHER_WM;
809            } else if (win.active()) {
810                awt_wmgr = XWM.OTHER_WM;
811            }
812            /*
813             * Check for legacy WMs.
814             */
815            else if (isCDE()) { /* XXX: must come before isMotif */
816                awt_wmgr = XWM.CDE_WM;
817            } else if (isMotif()) {
818                awt_wmgr = XWM.MOTIF_WM;
819            } else if (isOpenLook()) {
820                awt_wmgr = XWM.OPENLOOK_WM;
821            } else {
822                awt_wmgr = XWM.OTHER_WM;
823            }
824
825            return awt_wmgr;
826        } finally {
827            XToolkit.awtUnlock();
828            substruct.dispose();
829        }
830    }
831
832
833/*****************************************************************************\
834 *
835 * Size and decoration hints ...
836 *
837\*****************************************************************************/
838
839
840    /*
841     * Remove size hints specified by the mask.
842     * XXX: Why do we need this in the first place???
843     */
844    static void removeSizeHints(XDecoratedPeer window, long mask) {
845        mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize;
846
847        XToolkit.awtLock();
848        try {
849            XSizeHints hints = window.getHints();
850            if ((hints.get_flags() & mask) == 0) {
851                return;
852            }
853
854            hints.set_flags(hints.get_flags() & ~mask);
855            if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
856                insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags()));
857            }
858            XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(),
859                                          window.getWindow(),
860                                          hints.pData);
861        } finally {
862            XToolkit.awtUnlock();
863        }
864    }
865
866    /*
867     * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken
868     * to be subtracted from the decorations.  Normalize decoration spec
869     * so that we can map motif decor to something else bit-by-bit in the
870     * rest of the code.
871     */
872    static int normalizeMotifDecor(int decorations) {
873        if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) {
874            return decorations;
875        }
876        int d = MWMConstants.MWM_DECOR_BORDER | MWMConstants.MWM_DECOR_RESIZEH
877            | MWMConstants.MWM_DECOR_TITLE
878            | MWMConstants.MWM_DECOR_MENU | MWMConstants.MWM_DECOR_MINIMIZE
879            | MWMConstants.MWM_DECOR_MAXIMIZE;
880        d &= ~decorations;
881        return d;
882    }
883
884    /*
885     * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken
886     * to be subtracted from the functions.  Normalize function spec
887     * so that we can map motif func to something else bit-by-bit in the
888     * rest of the code.
889     */
890    static int normalizeMotifFunc(int functions) {
891        if ((functions & MWMConstants.MWM_FUNC_ALL) == 0) {
892            return functions;
893        }
894        int f = MWMConstants.MWM_FUNC_RESIZE |
895                MWMConstants.MWM_FUNC_MOVE |
896                MWMConstants.MWM_FUNC_MAXIMIZE |
897                MWMConstants.MWM_FUNC_MINIMIZE |
898                MWMConstants.MWM_FUNC_CLOSE;
899        f &= ~functions;
900        return f;
901    }
902
903    /*
904     * Infer OL properties from MWM decorations.
905     * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones.
906     */
907    static void setOLDecor(XWindow window, boolean resizable, int decorations) {
908        if (window == null) {
909            return;
910        }
911
912        XAtomList decorDel = new XAtomList();
913        decorations = normalizeMotifDecor(decorations);
914        if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
915            insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations));
916        }
917        if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) {
918            decorDel.add(XA_OL_DECOR_HEADER);
919        }
920        if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) {
921            decorDel.add(XA_OL_DECOR_RESIZE);
922        }
923        if ((decorations & (MWMConstants.MWM_DECOR_MENU |
924                            MWMConstants.MWM_DECOR_MAXIMIZE |
925                            MWMConstants.MWM_DECOR_MINIMIZE)) == 0)
926        {
927            decorDel.add(XA_OL_DECOR_CLOSE);
928        }
929        if (decorDel.size() == 0) {
930            insLog.finer("Deleting OL_DECOR");
931            XA_OL_DECOR_DEL.DeleteProperty(window);
932        } else {
933            if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
934                insLog.finer("Setting OL_DECOR to " + decorDel);
935            }
936            XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel);
937        }
938    }
939
940    /*
941     * Set MWM decorations.  Set MWM functions depending on resizability.
942     */
943    static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) {
944        /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */
945        if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0
946            && (decorations != MWMConstants.MWM_DECOR_ALL))
947        {
948            decorations = normalizeMotifDecor(decorations);
949        }
950        if ((functions & MWMConstants.MWM_FUNC_ALL) != 0
951            && (functions != MWMConstants.MWM_FUNC_ALL))
952        {
953            functions = normalizeMotifFunc(functions);
954        }
955
956        PropMwmHints hints = window.getMWMHints();
957        hints.set_flags(hints.get_flags() |
958                        MWMConstants.MWM_HINTS_FUNCTIONS |
959                        MWMConstants.MWM_HINTS_DECORATIONS);
960        hints.set_functions(functions);
961        hints.set_decorations(decorations);
962
963        if (stateLog.isLoggable(PlatformLogger.Level.FINER)) {
964            stateLog.finer("Setting MWM_HINTS to " + hints);
965        }
966        window.setMWMHints(hints);
967    }
968
969    /*
970     * Under some window managers if shell is already mapped, we MUST
971     * unmap and later remap in order to effect the changes we make in the
972     * window manager decorations.
973     *
974     * N.B.  This unmapping / remapping of the shell exposes a bug in
975     * X/Motif or the Motif Window Manager.  When you attempt to map a
976     * widget which is positioned (partially) off-screen, the window is
977     * relocated to be entirely on screen. Good idea.  But if both the x
978     * and the y coordinates are less than the origin (0,0), the first
979     * (re)map will move the window to the origin, and any subsequent
980     * (re)map will relocate the window at some other point on the screen.
981     * I have written a short Motif test program to discover this bug.
982     * This should occur infrequently and it does not cause any real
983     * problem.  So for now we'll let it be.
984     */
985    static boolean needRemap(XDecoratedPeer window) {
986        // Don't remap EmbeddedFrame,
987        // e.g. for TrayIcon it causes problems.
988        return !window.isEmbedded();
989    }
990
991    /*
992     * Set decoration hints on the shell to wdata->decor adjusted
993     * appropriately if not resizable.
994     */
995    static void setShellDecor(XDecoratedPeer window) {
996        int decorations = window.getDecorations();
997        int functions = window.getFunctions();
998        boolean resizable = window.isResizable();
999
1000        if (!resizable) {
1001            if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0) {
1002                decorations |= MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE;
1003            } else {
1004                decorations &= ~(MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE);
1005            }
1006        }
1007        setMotifDecor(window, resizable, decorations, functions);
1008        setOLDecor(window, resizable, decorations);
1009
1010        /* Some WMs need remap to redecorate the window */
1011        if (window.isShowing() && needRemap(window)) {
1012            /*
1013             * Do the re/mapping at the Xlib level.  Since we essentially
1014             * work around a WM bug we don't want this hack to be exposed
1015             * to Intrinsics (i.e. don't mess with grabs, callbacks etc).
1016             */
1017            window.xSetVisible(false);
1018            XToolkit.XSync();
1019            window.xSetVisible(true);
1020        }
1021    }
1022
1023    /*
1024     * Make specified shell resizable.
1025     */
1026    static void setShellResizable(XDecoratedPeer window) {
1027        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
1028            insLog.fine("Setting shell resizable " + window);
1029        }
1030        XToolkit.awtLock();
1031        try {
1032            Rectangle shellBounds;
1033            if (getWMID() != UNITY_COMPIZ_WM) {
1034                shellBounds = window.getShellBounds();
1035                shellBounds.translate(-window.currentInsets.left,
1036                                      -window.currentInsets.top);
1037            } else {
1038                shellBounds = window.getDimensions().getScreenBounds();
1039            }
1040            window.updateSizeHints(window.getDimensions());
1041            requestWMExtents(window.getWindow());
1042            XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(),
1043                                          window.getShell(),
1044                                          window.scaleUp(shellBounds.x),
1045                                          window.scaleUp(shellBounds.y),
1046                                          window.scaleUp(shellBounds.width),
1047                                          window.scaleUp(shellBounds.height));
1048            /* REMINDER: will need to revisit when setExtendedStateBounds is added */
1049            //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced.
1050            //We need to update frame's minimum size, not to reset it
1051            removeSizeHints(window, XUtilConstants.PMaxSize);
1052            window.updateMinimumSize();
1053
1054            /* Restore decorations */
1055            setShellDecor(window);
1056        } finally {
1057            XToolkit.awtUnlock();
1058        }
1059    }
1060
1061    /*
1062     * Make specified shell non-resizable.
1063     * If justChangeSize is false, update decorations as well.
1064     * @param shellBounds bounds of the shell window
1065     */
1066    static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds,
1067                                     boolean justChangeSize)
1068    {
1069        if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
1070            insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions +
1071                        ", shellBounds " + shellBounds +", just change size: " + justChangeSize);
1072        }
1073        XToolkit.awtLock();
1074        try {
1075            /* Fix min/max size hints at the specified values */
1076            if (!shellBounds.isEmpty()) {
1077                window.updateSizeHints(newDimensions);
1078                requestWMExtents(window.getWindow());
1079                XToolkit.XSync();
1080                XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(),
1081                                              window.getShell(),
1082                                              window.scaleUp(shellBounds.x),
1083                                              window.scaleUp(shellBounds.y),
1084                                              window.scaleUp(shellBounds.width),
1085                                              window.scaleUp(shellBounds.height));
1086            }
1087            if (!justChangeSize) {  /* update decorations */
1088                setShellDecor(window);
1089            }
1090        } finally {
1091            XToolkit.awtUnlock();
1092        }
1093    }
1094
1095/*****************************************************************************\
1096 * Protocols support
1097 */
1098    private HashMap<Class<?>, Collection<?>> protocolsMap = new HashMap<Class<?>, Collection<?>>();
1099    /**
1100     * Returns all protocols supporting given protocol interface
1101     */
1102    <T> Collection<T> getProtocols(Class<T> protocolInterface) {
1103        @SuppressWarnings("unchecked")
1104        Collection<T> res = (Collection<T>) protocolsMap.get(protocolInterface);
1105        if (res != null) {
1106            return res;
1107        } else {
1108            return new LinkedList<T>();
1109        }
1110    }
1111
1112    private <T> void addProtocol(Class<T> protocolInterface, T protocol) {
1113        Collection<T> protocols = getProtocols(protocolInterface);
1114        protocols.add(protocol);
1115        protocolsMap.put(protocolInterface, protocols);
1116    }
1117
1118    boolean supportsDynamicLayout() {
1119        int wm = getWMID();
1120        switch (wm) {
1121          case XWM.ENLIGHTEN_WM:
1122          case XWM.KDE2_WM:
1123          case XWM.SAWFISH_WM:
1124          case XWM.ICE_WM:
1125          case XWM.METACITY_WM:
1126              return true;
1127          case XWM.OPENLOOK_WM:
1128          case XWM.MOTIF_WM:
1129          case XWM.CDE_WM:
1130              return false;
1131          default:
1132              return false;
1133        }
1134    }
1135
1136
1137    /**
1138     * Check if state is supported.
1139     * Note that a compound state is always reported as not supported.
1140     * Note also that MAXIMIZED_BOTH is considered not a compound state.
1141     * Therefore, a compound state is just ICONIFIED | anything else.
1142     *
1143     */
1144    @SuppressWarnings("fallthrough")
1145    boolean supportsExtendedState(int state) {
1146        switch (state) {
1147          case Frame.MAXIMIZED_VERT:
1148          case Frame.MAXIMIZED_HORIZ:
1149              /*
1150               * WMs that talk NET/WIN protocol, but do not support
1151               * unidirectional maximization.
1152               */
1153              if (getWMID() == METACITY_WM) {
1154                  /* "This is a deliberate policy decision." -hp */
1155                  return false;
1156              }
1157              /* FALLTROUGH */
1158          case Frame.MAXIMIZED_BOTH:
1159              for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1160                  if (proto.supportsState(state)) {
1161                      return true;
1162                  }
1163              }
1164              /* FALLTROUGH */
1165          default:
1166              return false;
1167        }
1168    }
1169
1170/*****************************************************************************\
1171 *
1172 * Reading state from different protocols
1173 *
1174\*****************************************************************************/
1175
1176
1177    int getExtendedState(XWindowPeer window) {
1178        int state = 0;
1179        for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1180            state |= proto.getState(window);
1181        }
1182        if (state != 0) {
1183            return state;
1184        } else {
1185            return Frame.NORMAL;
1186        }
1187    }
1188
1189/*****************************************************************************\
1190 *
1191 * Notice window state change when WM changes a property on the window ...
1192 *
1193\*****************************************************************************/
1194
1195
1196    /*
1197     * Check if property change is a window state protocol message.
1198     */
1199    boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) {
1200        if (!window.isShowing()) {
1201            stateLog.finer("Window is not showing");
1202            return false;
1203        }
1204
1205        int wm_state = window.getWMState();
1206        if (wm_state == XUtilConstants.WithdrawnState) {
1207            stateLog.finer("WithdrawnState");
1208            return false;
1209        } else {
1210            if (stateLog.isLoggable(PlatformLogger.Level.FINER)) {
1211                stateLog.finer("Window WM_STATE is " + wm_state);
1212            }
1213        }
1214        boolean is_state_change = false;
1215        if (e.get_atom() == XA_WM_STATE.getAtom()) {
1216            is_state_change = true;
1217        }
1218
1219        for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1220            is_state_change |= proto.isStateChange(e);
1221            if (stateLog.isLoggable(PlatformLogger.Level.FINEST)) {
1222                stateLog.finest(proto + ": is state changed = " + is_state_change);
1223            }
1224        }
1225        return is_state_change;
1226    }
1227
1228    /*
1229     * Returns current state (including extended) of a given window.
1230     */
1231    int getState(XDecoratedPeer window) {
1232        int res = 0;
1233        final int wm_state = window.getWMState();
1234        if (wm_state == XUtilConstants.IconicState) {
1235            res = Frame.ICONIFIED;
1236        } else {
1237            res = Frame.NORMAL;
1238        }
1239        res |= getExtendedState(window);
1240        return res;
1241    }
1242
1243/*****************************************************************************\
1244 *
1245 * Setting/changing window state ...
1246 *
1247\*****************************************************************************/
1248
1249    /**
1250     * Moves window to the specified layer, layer is one of the constants defined
1251     * in XLayerProtocol
1252     */
1253    void setLayer(XWindowPeer window, int layer) {
1254        for (XLayerProtocol proto : getProtocols(XLayerProtocol.class)) {
1255            if (proto.supportsLayer(layer)) {
1256                proto.setLayer(window, layer);
1257            }
1258        }
1259        XToolkit.XSync();
1260    }
1261
1262    void setExtendedState(XWindowPeer window, int state) {
1263        for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1264            if (proto.supportsState(state)) {
1265                proto.setState(window, state);
1266                break;
1267            }
1268        }
1269
1270        if (!window.isShowing()) {
1271            /*
1272             * Purge KWM bits.
1273             * Not really tested with KWM, only with WindowMaker.
1274             */
1275            XToolkit.awtLock();
1276            try {
1277                XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1278                                            window.getWindow(),
1279                                            XA_KWM_WIN_ICONIFIED.getAtom());
1280                XlibWrapper.XDeleteProperty(XToolkit.getDisplay(),
1281                                            window.getWindow(),
1282                                            XA_KWM_WIN_MAXIMIZED.getAtom());
1283            }
1284            finally {
1285                XToolkit.awtUnlock();
1286            }
1287        }
1288        XToolkit.XSync();
1289    }
1290
1291
1292    /*
1293     * Work around for 4775545.
1294     *
1295     * If WM exits while the top-level is shaded, the shaded hint remains
1296     * on the top-level properties.  When WM restarts and sees the shaded
1297     * window it can reparent it into a "pre-shaded" decoration frame
1298     * (Metacity does), and our insets logic will go crazy, b/c it will
1299     * see a huge nagative bottom inset.  There's no clean solution for
1300     * this, so let's just be weasels and drop the shaded hint if we
1301     * detect that WM exited.  NB: we are in for a race condition with WM
1302     * restart here.  NB2: e.g. WindowMaker saves the state in a private
1303     * property that this code knows nothing about, so this workaround is
1304     * not effective; other WMs might play similar tricks.
1305     */
1306    void unshadeKludge(XDecoratedPeer window) {
1307        assert(window.isShowing());
1308
1309        for (XStateProtocol proto : getProtocols(XStateProtocol.class)) {
1310            proto.unshadeKludge(window);
1311        }
1312        XToolkit.XSync();
1313    }
1314
1315    static boolean inited = false;
1316    static void init() {
1317        if (inited) {
1318            return;
1319        }
1320
1321        initAtoms();
1322        getWM();
1323        inited = true;
1324    }
1325
1326    void initializeProtocols() {
1327        XNETProtocol net_protocol = g_net_protocol;
1328        if (net_protocol != null) {
1329            if (!net_protocol.active()) {
1330                net_protocol = null;
1331            } else {
1332                if (net_protocol.doStateProtocol()) {
1333                    addProtocol(XStateProtocol.class, net_protocol);
1334                }
1335                if (net_protocol.doLayerProtocol()) {
1336                    addProtocol(XLayerProtocol.class, net_protocol);
1337                }
1338            }
1339        }
1340
1341        XWINProtocol win = g_win_protocol;
1342        if (win != null) {
1343            if (win.active()) {
1344                if (win.doStateProtocol()) {
1345                    addProtocol(XStateProtocol.class, win);
1346                }
1347                if (win.doLayerProtocol()) {
1348                    addProtocol(XLayerProtocol.class, win);
1349                }
1350            }
1351        }
1352    }
1353
1354    HashMap<Class<?>, Insets> storedInsets = new HashMap<>();
1355    Insets guessInsets(XDecoratedPeer window) {
1356        Insets res = storedInsets.get(window.getClass());
1357        if (res == null) {
1358            switch (WMID) {
1359              case ENLIGHTEN_WM:
1360                  res = new Insets(19, 4, 4, 4);
1361                  break;
1362              case CDE_WM:
1363                  res = new Insets(28, 6, 6, 6);
1364                  break;
1365              case NO_WM:
1366              case LG3D_WM:
1367                  res = zeroInsets;
1368                  break;
1369              case UNITY_COMPIZ_WM:
1370                  res = new Insets(28, 1, 1, 1);
1371                  break;
1372              case MOTIF_WM:
1373              case OPENLOOK_WM:
1374              default:
1375                  res = defaultInsets;
1376            }
1377        }
1378        if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1379            insLog.finest("WM guessed insets: " + res);
1380        }
1381        return res;
1382    }
1383    /*
1384     * Some buggy WMs ignore window gravity when processing
1385     * ConfigureRequest and position window as if the gravity is Static.
1386     * We work around this in MWindowPeer.pReshape().
1387     *
1388     * Starting with 1.5 we have introduced an Environment variable
1389     * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java
1390     * explicitly that the WM has this behaviour, example is FVWM.
1391     */
1392
1393    static int awtWMStaticGravity = -1;
1394    static boolean configureGravityBuggy() {
1395
1396        if (awtWMStaticGravity == -1) {
1397            awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0;
1398        }
1399
1400        if (awtWMStaticGravity == 1) {
1401            return true;
1402        }
1403
1404        switch(getWMID()) {
1405          case XWM.ICE_WM:
1406              /*
1407               * See bug #228981 at IceWM's SourceForge pages.
1408               * Latest stable version 1.0.8-6 still has this problem.
1409               */
1410              /**
1411               * Version 1.2.2 doesn't have this problem
1412               */
1413              // Detect IceWM version
1414              if (g_net_protocol != null) {
1415                  String wm_name = g_net_protocol.getWMName();
1416                  Pattern pat = Pattern.compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$");
1417                  try {
1418                      Matcher match = pat.matcher(wm_name);
1419                      if (match.matches()) {
1420                          int v1 = Integer.parseInt(match.group(1));
1421                          int v2 = Integer.parseInt(match.group(2));
1422                          int v3 = Integer.parseInt(match.group(3));
1423                          return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >=2))));
1424                      }
1425                  } catch (Exception e) {
1426                      return true;
1427                  }
1428              }
1429              return true;
1430          case XWM.ENLIGHTEN_WM:
1431              /* At least E16 is buggy. */
1432              return true;
1433          default:
1434              return false;
1435        }
1436    }
1437
1438    /*
1439     * @return if WM implements the insets property - returns insets with values
1440     * specified in that property, null otherwise.
1441     */
1442    public static Insets getInsetsFromExtents(long window) {
1443        if (window == XConstants.None) {
1444            return null;
1445        }
1446        XNETProtocol net_protocol = getWM().getNETProtocol();
1447        if (net_protocol != null && net_protocol.active()) {
1448            Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS);
1449            if (insLog.isLoggable(PlatformLogger.Level.FINE)) {
1450                insLog.fine("_NET_FRAME_EXTENTS: {0}", insets);
1451            }
1452
1453            if (insets != null) {
1454                return insets;
1455            }
1456        }
1457        switch(getWMID()) {
1458          case XWM.KDE2_WM:
1459              return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT);
1460          case XWM.ENLIGHTEN_WM:
1461              return getInsetsFromProp(window, XA_E_FRAME_SIZE);
1462          default:
1463              return null;
1464        }
1465    }
1466
1467    /**
1468     * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom }
1469     * and converts it to Insets object.
1470     */
1471    public static Insets getInsetsFromProp(long window, XAtom atom) {
1472        if (window == XConstants.None) {
1473            return null;
1474        }
1475
1476        WindowPropertyGetter getter =
1477            new WindowPropertyGetter(window, atom,
1478                                     0, 4, false, XAtom.XA_CARDINAL);
1479        try {
1480            if (getter.execute() != XConstants.Success
1481                || getter.getData() == 0
1482                || getter.getActualType() != XAtom.XA_CARDINAL
1483                || getter.getActualFormat() != 32)
1484            {
1485                return null;
1486            } else {
1487                return new Insets((int)Native.getCard32(getter.getData(), 2), // top
1488                                  (int)Native.getCard32(getter.getData(), 0), // left
1489                                  (int)Native.getCard32(getter.getData(), 3), // bottom
1490                                  (int)Native.getCard32(getter.getData(), 1)); // right
1491            }
1492        } finally {
1493            getter.dispose();
1494        }
1495    }
1496
1497    /**
1498     * Asks WM to fill Frame Extents (insets) for the window.
1499     */
1500    public static void requestWMExtents(long window) {
1501        if (window == XConstants.None) { // not initialized
1502            return;
1503        }
1504
1505        log.fine("Requesting FRAME_EXTENTS");
1506
1507        XClientMessageEvent msg = new XClientMessageEvent();
1508        msg.zero();
1509        msg.set_type(XConstants.ClientMessage);
1510        msg.set_display(XToolkit.getDisplay());
1511        msg.set_window(window);
1512        msg.set_format(32);
1513        XToolkit.awtLock();
1514        try {
1515            XNETProtocol net_protocol = getWM().getNETProtocol();
1516            if (net_protocol != null && net_protocol.active()) {
1517                msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom());
1518                XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1519                                       false,
1520                                       XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1521                                       msg.getPData());
1522            }
1523            if (getWMID() == XWM.KDE2_WM) {
1524                msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom());
1525                XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(),
1526                                       false,
1527                                       XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask,
1528                                       msg.getPData());
1529            }
1530            // XXX: should we wait for response? XIfEvent() would be useful here :)
1531        } finally {
1532            XToolkit.awtUnlock();
1533            msg.dispose();
1534        }
1535    }
1536
1537    /* syncTopLEvelPos() is necessary to insure that the window manager has in
1538     * fact moved us to our final position relative to the reParented WM window.
1539     * We have noted a timing window which our shell has not been moved so we
1540     * screw up the insets thinking they are 0,0.  Wait (for a limited period of
1541     * time to let the WM hava a chance to move us.
1542     * @param window window ID of the shell, assuming it is the window
1543     * which will NOT have zero coordinates after the complete
1544     * reparenting
1545     */
1546    boolean syncTopLevelPos(long window, XWindowAttributes attrs) {
1547        int tries = 0;
1548        XToolkit.awtLock();
1549        try {
1550            do {
1551                XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData);
1552                if (attrs.get_x() != 0 || attrs.get_y() != 0) {
1553                    return true;
1554                }
1555                tries++;
1556                XToolkit.XSync();
1557            } while (tries < 50);
1558        }
1559        finally {
1560            XToolkit.awtUnlock();
1561        }
1562        return false;
1563    }
1564
1565    Insets getInsets(XDecoratedPeer win, long window, long parent) {
1566        /*
1567         * Unfortunately the concept of "insets" borrowed to AWT
1568         * from Win32 is *absolutely*, *unbelievably* foreign to
1569         * X11.  Few WMs provide the size of frame decor
1570         * (i.e. insets) in a property they set on the client
1571         * window, so we check if we can get away with just
1572         * peeking at it.  [Future versions of wm-spec might add a
1573         * standardized hint for this].
1574         *
1575         * Otherwise we do some special casing.  Actually the
1576         * fallback code ("default" case) seems to cover most of
1577         * the existing WMs (modulo Reparent/Configure order
1578         * perhaps?).
1579         *
1580         * Fallback code tries to account for the two most common cases:
1581         *
1582         * . single reparenting
1583         *       parent window is the WM frame
1584         *       [twm, olwm, sawfish]
1585         *
1586         * . double reparenting
1587         *       parent is a lining exactly the size of the client
1588         *       grandpa is the WM frame
1589         *       [mwm, e!, kwin, fvwm2 ... ]
1590         */
1591        Insets correctWM = XWM.getInsetsFromExtents(window);
1592        if (insLog.isLoggable(PlatformLogger.Level.FINER)) {
1593            insLog.finer("Got insets from property: {0}", correctWM);
1594        }
1595
1596        if (correctWM == null) {
1597            correctWM = new Insets(0,0,0,0);
1598
1599            correctWM.top = -1;
1600            correctWM.left = -1;
1601
1602            XWindowAttributes lwinAttr = new XWindowAttributes();
1603            XWindowAttributes pattr = new XWindowAttributes();
1604            try {
1605                switch (XWM.getWMID()) {
1606                  /* should've been done in awt_wm_getInsetsFromProp */
1607                  case XWM.ENLIGHTEN_WM: {
1608                      /* enlightenment does double reparenting */
1609                      syncTopLevelPos(parent, lwinAttr);
1610                      correctWM.left = lwinAttr.get_x();
1611                      correctWM.top = lwinAttr.get_y();
1612                      /*
1613                       * Now get the actual dimensions of the parent window
1614                       * resolve the difference.  We can't rely on the left
1615                       * to be equal to right or bottom...  Enlightment
1616                       * breaks that assumption.
1617                       */
1618                      XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1619                                                       XlibUtil.getParentWindow(parent),
1620                                                       pattr.pData);
1621                      correctWM.right = pattr.get_width() -
1622                          (lwinAttr.get_width() + correctWM.left);
1623                      correctWM.bottom = pattr.get_height() -
1624                          (lwinAttr.get_height() + correctWM.top);
1625
1626                      break;
1627                  }
1628                  case XWM.ICE_WM: // for 1.2.2.
1629                  case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */
1630                  case XWM.CDE_WM:
1631                  case XWM.MOTIF_WM: {
1632                      /* these are double reparenting too */
1633                      if (syncTopLevelPos(parent, lwinAttr)) {
1634                          correctWM.top = lwinAttr.get_y();
1635                          correctWM.left = lwinAttr.get_x();
1636                          correctWM.right = correctWM.left;
1637                          correctWM.bottom = correctWM.left;
1638                      } else {
1639                          return null;
1640                      }
1641                      break;
1642                  }
1643                  case XWM.SAWFISH_WM:
1644                  case XWM.OPENLOOK_WM: {
1645                      /* single reparenting */
1646                      syncTopLevelPos(window, lwinAttr);
1647                      correctWM.top    = lwinAttr.get_y();
1648                      correctWM.left   = lwinAttr.get_x();
1649                      correctWM.right  = correctWM.left;
1650                      correctWM.bottom = correctWM.left;
1651                      break;
1652                  }
1653                  case XWM.OTHER_WM:
1654                  default: {                /* this is very similar to the E! case above */
1655                      if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1656                          insLog.finest("Getting correct insets for OTHER_WM/default, parent: {0}", parent);
1657                      }
1658                      syncTopLevelPos(parent, lwinAttr);
1659                      int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1660                                                                    window, lwinAttr.pData);
1661                      status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1662                                                                parent, pattr.pData);
1663                      if (lwinAttr.get_root() == parent) {
1664                          insLog.finest("our parent is root so insets should be zero");
1665                          correctWM = new Insets(0, 0, 0, 0);
1666                          break;
1667                      }
1668
1669                      /*
1670                       * Check for double-reparenting WM.
1671                       *
1672                       * If the parent is exactly the same size as the
1673                       * top-level assume taht it's the "lining" window and
1674                       * that the grandparent is the actual frame (NB: we
1675                       * have already handled undecorated windows).
1676                       *
1677                       * XXX: what about timing issues that syncTopLevelPos
1678                       * is supposed to work around?
1679                       */
1680                      if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0
1681                          && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width()
1682                          && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height())
1683                      {
1684                          if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1685                              insLog.finest("Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}",
1686                                        lwinAttr, pattr, parent, window);
1687                          }
1688                          lwinAttr.set_x(pattr.get_x());
1689                          lwinAttr.set_y(pattr.get_y());
1690                          lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width());
1691
1692                          final long grand_parent = XlibUtil.getParentWindow(parent);
1693
1694                          if (grand_parent == lwinAttr.get_root()) {
1695                              // This is not double-reparenting in a
1696                              // general sense - we simply don't have
1697                              // correct insets - return null to try to
1698                              // get insets later
1699                              return null;
1700                          } else {
1701                              parent = grand_parent;
1702                              XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(),
1703                                                               parent,
1704                                                               pattr.pData);
1705                          }
1706                      }
1707                      /*
1708                       * XXX: To be absolutely correct, we'd need to take
1709                       * parent's border-width into account too, but the
1710                       * rest of the code is happily unaware about border
1711                       * widths and inner/outer distinction, so for the time
1712                       * being, just ignore it.
1713                       */
1714                      if (insLog.isLoggable(PlatformLogger.Level.FINEST)) {
1715                          insLog.finest("Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}",
1716                                    lwinAttr, pattr, parent, window);
1717                      }
1718                      correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(),
1719                                             lwinAttr.get_x() + lwinAttr.get_border_width(),
1720                                             pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()),
1721                                             pattr.get_width() -  (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width()));
1722                      break;
1723                  } /* default */
1724                } /* switch (runningWM) */
1725            } finally {
1726                lwinAttr.dispose();
1727                pattr.dispose();
1728            }
1729        }
1730
1731        correctWM.top = win.scaleUp(correctWM.top);
1732        correctWM.bottom = win.scaleUp(correctWM.bottom);
1733        correctWM.left = win.scaleUp(correctWM.left);
1734        correctWM.right = win.scaleUp(correctWM.right);
1735
1736        if (storedInsets.get(win.getClass()) == null) {
1737            storedInsets.put(win.getClass(), correctWM);
1738        }
1739        return correctWM;
1740    }
1741    boolean isDesktopWindow( long w ) {
1742        if (g_net_protocol != null) {
1743            XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w );
1744            return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") );
1745        } else {
1746            return false;
1747        }
1748    }
1749
1750    public XNETProtocol getNETProtocol() {
1751        return g_net_protocol;
1752    }
1753
1754    /**
1755     * Sets _NET_WN_ICON property on the window using the arrays of
1756     * raster-data for icons. If icons is null, removes _NET_WM_ICON
1757     * property.
1758     * This method invokes XNETProtocol.setWMIcon() for WMs that
1759     * support NET protocol.
1760     *
1761     * @return true if hint was modified successfully, false otherwise
1762     */
1763    public boolean setNetWMIcon(XWindowPeer window, java.util.List<IconInfo> icons) {
1764        if (g_net_protocol != null && g_net_protocol.active()) {
1765            g_net_protocol.setWMIcons(window, icons);
1766            return getWMID() != ICE_WM;
1767        }
1768        return false;
1769    }
1770}
1771