1 /*
2  * Copyright (c) 2003, 2018, 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 jdk.internal.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 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;
toString()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 
XWM(int WMID)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     }
getID()159     int getID() {
160         return WMID;
161     }
162 
163 
normalize(Insets insets)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;
isNetWMName(String name)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 
initAtoms()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      */
isNoWM()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      */
getECommsWindowIDProperty(long window)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      */
isEnlightenment()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);
isCDE()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);
isMotif()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      */
isSawfish()563     static boolean isSawfish() {
564         return isNetWMName("Sawfish");
565     }
566 
567     /*
568      * Is KDE2 (KWin) running?
569      */
isKDE2()570     static boolean isKDE2() {
571         return isNetWMName("KWin");
572     }
573 
isCompiz()574     static boolean isCompiz() {
575         return isNetWMName("compiz");
576     }
577 
isUnityCompiz()578     static boolean isUnityCompiz() {
579         return isNetWMName("Compiz");
580     }
581 
isLookingGlass()582     static boolean isLookingGlass() {
583         return isNetWMName("LG3D");
584     }
585 
isCWM()586     static boolean isCWM() {
587         return isNetWMName("CWM");
588     }
589 
590     /*
591      * Is Metacity running?
592      */
isMetacity()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 
isMutter()601     static boolean isMutter() {
602         return isNetWMName("Mutter") || isNetWMName("GNOME Shell");
603     }
604 
605     static int awtWMNonReparenting = -1;
isNonReparentingWM()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     };
prepareIsIceWM()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      */
isIceWM()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);
isOpenLook()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;
getWM()738     static XWM getWM() {
739         if (wm == null) {
740             wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/);
741         }
742         return wm;
743     }
getWMID()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      */
removeSizeHints(XDecoratedPeer window, long mask)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      */
normalizeMotifDecor(int decorations)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      */
normalizeMotifFunc(int functions)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      */
setOLDecor(XWindow window, boolean resizable, int decorations)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      */
setMotifDecor(XWindow window, boolean resizable, int decorations, int functions)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      */
needRemap(XDecoratedPeer window)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      */
setShellDecor(XDecoratedPeer window)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      */
setShellResizable(XDecoratedPeer window)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      */
setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds, boolean justChangeSize)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      */
getProtocols(Class<T> protocolInterface)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 
addProtocol(Class<T> protocolInterface, T protocol)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 
supportsDynamicLayout()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")
supportsExtendedState(int state)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 
getExtendedState(XWindowPeer window)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      */
isStateChange(XDecoratedPeer window, XPropertyEvent e)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      */
getState(XDecoratedPeer window)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      */
setLayer(XWindowPeer window, int layer)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 
setExtendedState(XWindowPeer window, int state)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      */
unshadeKludge(XDecoratedPeer window)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;
init()1316     static void init() {
1317         if (inited) {
1318             return;
1319         }
1320 
1321         initAtoms();
1322         getWM();
1323         inited = true;
1324     }
1325 
initializeProtocols()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<>();
guessInsets(XDecoratedPeer window)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;
configureGravityBuggy()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      */
getInsetsFromExtents(long window)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      */
getInsetsFromProp(long window, XAtom atom)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      */
requestWMExtents(long window)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      */
syncTopLevelPos(long window, XWindowAttributes attrs)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 
getInsets(XDecoratedPeer win, long window, long parent)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     }
isDesktopWindow( long w )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 
getNETProtocol()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      */
setNetWMIcon(XWindowPeer window, java.util.List<IconInfo> icons)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