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