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