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