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