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