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