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