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