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)) 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 XErrorHandlerUtil.WITH_XERROR_HANDLER(detectWMHandler); 286 XlibWrapper.XChangeWindowAttributes(XToolkit.getDisplay(), 287 XToolkit.getDefaultRootWindow(), 288 XConstants.CWEventMask, 289 substruct.pData); 290 XErrorHandlerUtil.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 XErrorHandlerUtil.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 XErrorHandlerUtil.RESTORE_XERROR_HANDLER(); 626 627 if ((XErrorHandlerUtil.saved_error != null) && 628 (XErrorHandlerUtil.saved_error.get_error_code() != XConstants.Success)) { 629 log.finer("Erorr getting XA_ICEWM_WINOPTHINT property"); 630 return false; 631 } 632 log.finer("Prepared for IceWM detection"); 633 return true; 634 } finally { 635 XToolkit.awtUnlock(); 636 } 637 } 638 639 /* 640 * Is IceWM running? 641 * 642 * Note well: Only call this if awt_wm_prepareIsIceWM succeeded, or a 643 * false positive will be reported. 644 */ 645 static boolean isIceWM() { 646 if (!XA_ICEWM_WINOPTHINT.isInterned()) { 647 log.finer("{0} is not interned", XA_ICEWM_WINOPTHINT); 648 return false; 649 } 650 651 WindowPropertyGetter getter = 652 new WindowPropertyGetter(XToolkit.getDefaultRootWindow(), 653 XA_ICEWM_WINOPTHINT, 0, 0xFFFF, 654 true, XA_ICEWM_WINOPTHINT); 655 try { 656 int status = getter.execute(); 657 boolean res = (status == XConstants.Success && getter.getActualType() != 0); 658 log.finer("Status getting XA_ICEWM_WINOPTHINT: " + !res); 659 return !res || isNetWMName("IceWM"); 660 } finally { 661 getter.dispose(); 662 } 663 } 664 665 /* 666 * Is OpenLook WM running? 667 * 668 * This one is pretty lame, but the only property peculiar to OLWM is 669 * _SUN_WM_PROTOCOLS(ATOM[]). Fortunately, olwm deletes it on exit. 670 */ 671 static final XAtom XA_SUN_WM_PROTOCOLS = new XAtom("_SUN_WM_PROTOCOLS", false); 672 static boolean isOpenLook() { 673 if (!XA_SUN_WM_PROTOCOLS.isInterned()) { 674 return false; 675 } 676 677 XAtom[] list = XA_SUN_WM_PROTOCOLS.getAtomListProperty(XToolkit.getDefaultRootWindow()); 678 return (list.length != 0); 679 } 680 681 /* 682 * Temporary error handler that checks if selecting for 683 * SubstructureRedirect failed. 684 */ 685 private static boolean winmgr_running = false; 686 private static XErrorHandler detectWMHandler = new XErrorHandler.XBaseErrorHandler() { 687 @Override 688 public int handleError(long display, XErrorEvent err) { 689 if ((err.get_request_code() == XProtocolConstants.X_ChangeWindowAttributes) && 690 (err.get_error_code() == XConstants.BadAccess)) 691 { 692 winmgr_running = true; 693 return 0; 694 } 695 return super.handleError(display, err); 696 } 697 }; 698 699 /* 700 * Make an educated guess about running window manager. 701 * XXX: ideally, we should detect wm restart. 702 */ 703 static int awt_wmgr = XWM.UNDETERMINED_WM; 704 static XWM wm; 705 static XWM getWM() { 706 if (wm == null) { 707 wm = new XWM(awt_wmgr = getWMID()/*XWM.OTHER_WM*/); 708 } 709 return wm; 710 } 711 static int getWMID() { 712 if (insLog.isLoggable(PlatformLogger.FINEST)) { 713 insLog.finest("awt_wmgr = " + awt_wmgr); 714 } 715 /* 716 * Ideally, we should support cases when a different WM is started 717 * during a Java app lifetime. 718 */ 719 720 if (awt_wmgr != XWM.UNDETERMINED_WM) { 721 return awt_wmgr; 722 } 723 724 XSetWindowAttributes substruct = new XSetWindowAttributes(); 725 XToolkit.awtLock(); 726 try { 727 if (isNoWM()) { 728 awt_wmgr = XWM.NO_WM; 729 return awt_wmgr; 730 } 731 732 // Initialize _NET protocol - used to detect Window Manager. 733 // Later, WM will initialize its own version of protocol 734 XNETProtocol l_net_protocol = g_net_protocol = new XNETProtocol(); 735 l_net_protocol.detect(); 736 if (log.isLoggable(PlatformLogger.FINE) && l_net_protocol.active()) { 737 log.fine("_NET_WM_NAME is " + l_net_protocol.getWMName()); 738 } 739 XWINProtocol win = g_win_protocol = new XWINProtocol(); 740 win.detect(); 741 742 /* actual check for IceWM to follow below */ 743 boolean doIsIceWM = prepareIsIceWM(); /* and let IceWM to act */ 744 745 /* 746 * Ok, some WM is out there. Check which one by testing for 747 * "distinguishing" atoms. 748 */ 749 if (isEnlightenment()) { 750 awt_wmgr = XWM.ENLIGHTEN_WM; 751 } else if (isMetacity()) { 752 awt_wmgr = XWM.METACITY_WM; 753 } else if (isMutter()) { 754 awt_wmgr = XWM.MUTTER_WM; 755 } else if (isSawfish()) { 756 awt_wmgr = XWM.SAWFISH_WM; 757 } else if (isKDE2()) { 758 awt_wmgr =XWM.KDE2_WM; 759 } else if (isCompiz()) { 760 awt_wmgr = XWM.COMPIZ_WM; 761 } else if (isLookingGlass()) { 762 awt_wmgr = LG3D_WM; 763 } else if (isCWM()) { 764 awt_wmgr = CWM_WM; 765 } else if (doIsIceWM && isIceWM()) { 766 awt_wmgr = XWM.ICE_WM; 767 } 768 /* 769 * We don't check for legacy WM when we already know that WM 770 * supports WIN or _NET wm spec. 771 */ 772 else if (l_net_protocol.active()) { 773 awt_wmgr = XWM.OTHER_WM; 774 } else if (win.active()) { 775 awt_wmgr = XWM.OTHER_WM; 776 } 777 /* 778 * Check for legacy WMs. 779 */ 780 else if (isCDE()) { /* XXX: must come before isMotif */ 781 awt_wmgr = XWM.CDE_WM; 782 } else if (isMotif()) { 783 awt_wmgr = XWM.MOTIF_WM; 784 } else if (isOpenLook()) { 785 awt_wmgr = XWM.OPENLOOK_WM; 786 } else { 787 awt_wmgr = XWM.OTHER_WM; 788 } 789 790 return awt_wmgr; 791 } finally { 792 XToolkit.awtUnlock(); 793 substruct.dispose(); 794 } 795 } 796 797 798 /*****************************************************************************\ 799 * 800 * Size and decoration hints ... 801 * 802 \*****************************************************************************/ 803 804 805 /* 806 * Remove size hints specified by the mask. 807 * XXX: Why do we need this in the first place??? 808 */ 809 static void removeSizeHints(XDecoratedPeer window, long mask) { 810 mask &= XUtilConstants.PMaxSize | XUtilConstants.PMinSize; 811 812 XToolkit.awtLock(); 813 try { 814 XSizeHints hints = window.getHints(); 815 if ((hints.get_flags() & mask) == 0) { 816 return; 817 } 818 819 hints.set_flags(hints.get_flags() & ~mask); 820 if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags())); 821 XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(), 822 window.getWindow(), 823 hints.pData); 824 } finally { 825 XToolkit.awtUnlock(); 826 } 827 } 828 829 /* 830 * If MWM_DECOR_ALL bit is set, then the rest of the bit-mask is taken 831 * to be subtracted from the decorations. Normalize decoration spec 832 * so that we can map motif decor to something else bit-by-bit in the 833 * rest of the code. 834 */ 835 static int normalizeMotifDecor(int decorations) { 836 if ((decorations & MWMConstants.MWM_DECOR_ALL) == 0) { 837 return decorations; 838 } 839 int d = MWMConstants.MWM_DECOR_BORDER | MWMConstants.MWM_DECOR_RESIZEH 840 | MWMConstants.MWM_DECOR_TITLE 841 | MWMConstants.MWM_DECOR_MENU | MWMConstants.MWM_DECOR_MINIMIZE 842 | MWMConstants.MWM_DECOR_MAXIMIZE; 843 d &= ~decorations; 844 return d; 845 } 846 847 /* 848 * If MWM_FUNC_ALL bit is set, then the rest of the bit-mask is taken 849 * to be subtracted from the functions. Normalize function spec 850 * so that we can map motif func to something else bit-by-bit in the 851 * rest of the code. 852 */ 853 static int normalizeMotifFunc(int functions) { 854 if ((functions & MWMConstants.MWM_FUNC_ALL) == 0) { 855 return functions; 856 } 857 int f = MWMConstants.MWM_FUNC_RESIZE | 858 MWMConstants.MWM_FUNC_MOVE | 859 MWMConstants.MWM_FUNC_MAXIMIZE | 860 MWMConstants.MWM_FUNC_MINIMIZE | 861 MWMConstants.MWM_FUNC_CLOSE; 862 f &= ~functions; 863 return f; 864 } 865 866 /* 867 * Infer OL properties from MWM decorations. 868 * Use _OL_DECOR_DEL(ATOM[]) to remove unwanted ones. 869 */ 870 static void setOLDecor(XWindow window, boolean resizable, int decorations) { 871 if (window == null) { 872 return; 873 } 874 875 XAtomList decorDel = new XAtomList(); 876 decorations = normalizeMotifDecor(decorations); 877 if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting OL_DECOR to " + Integer.toBinaryString(decorations)); 878 if ((decorations & MWMConstants.MWM_DECOR_TITLE) == 0) { 879 decorDel.add(XA_OL_DECOR_HEADER); 880 } 881 if ((decorations & (MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE)) == 0) { 882 decorDel.add(XA_OL_DECOR_RESIZE); 883 } 884 if ((decorations & (MWMConstants.MWM_DECOR_MENU | 885 MWMConstants.MWM_DECOR_MAXIMIZE | 886 MWMConstants.MWM_DECOR_MINIMIZE)) == 0) 887 { 888 decorDel.add(XA_OL_DECOR_CLOSE); 889 } 890 if (decorDel.size() == 0) { 891 insLog.finer("Deleting OL_DECOR"); 892 XA_OL_DECOR_DEL.DeleteProperty(window); 893 } else { 894 if (insLog.isLoggable(PlatformLogger.FINER)) insLog.finer("Setting OL_DECOR to " + decorDel); 895 XA_OL_DECOR_DEL.setAtomListProperty(window, decorDel); 896 } 897 } 898 899 /* 900 * Set MWM decorations. Set MWM functions depending on resizability. 901 */ 902 static void setMotifDecor(XWindow window, boolean resizable, int decorations, int functions) { 903 /* Apparently some WMs don't implement MWM_*_ALL semantic correctly */ 904 if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0 905 && (decorations != MWMConstants.MWM_DECOR_ALL)) 906 { 907 decorations = normalizeMotifDecor(decorations); 908 } 909 if ((functions & MWMConstants.MWM_FUNC_ALL) != 0 910 && (functions != MWMConstants.MWM_FUNC_ALL)) 911 { 912 functions = normalizeMotifFunc(functions); 913 } 914 915 PropMwmHints hints = window.getMWMHints(); 916 hints.set_flags(hints.get_flags() | 917 MWMConstants.MWM_HINTS_FUNCTIONS | 918 MWMConstants.MWM_HINTS_DECORATIONS); 919 hints.set_functions(functions); 920 hints.set_decorations(decorations); 921 922 if (stateLog.isLoggable(PlatformLogger.FINER)) stateLog.finer("Setting MWM_HINTS to " + hints); 923 window.setMWMHints(hints); 924 } 925 926 /* 927 * Under some window managers if shell is already mapped, we MUST 928 * unmap and later remap in order to effect the changes we make in the 929 * window manager decorations. 930 * 931 * N.B. This unmapping / remapping of the shell exposes a bug in 932 * X/Motif or the Motif Window Manager. When you attempt to map a 933 * widget which is positioned (partially) off-screen, the window is 934 * relocated to be entirely on screen. Good idea. But if both the x 935 * and the y coordinates are less than the origin (0,0), the first 936 * (re)map will move the window to the origin, and any subsequent 937 * (re)map will relocate the window at some other point on the screen. 938 * I have written a short Motif test program to discover this bug. 939 * This should occur infrequently and it does not cause any real 940 * problem. So for now we'll let it be. 941 */ 942 static boolean needRemap(XDecoratedPeer window) { 943 // Don't remap EmbeddedFrame, 944 // e.g. for TrayIcon it causes problems. 945 return !window.isEmbedded(); 946 } 947 948 /* 949 * Set decoration hints on the shell to wdata->decor adjusted 950 * appropriately if not resizable. 951 */ 952 static void setShellDecor(XDecoratedPeer window) { 953 int decorations = window.getDecorations(); 954 int functions = window.getFunctions(); 955 boolean resizable = window.isResizable(); 956 957 if (!resizable) { 958 if ((decorations & MWMConstants.MWM_DECOR_ALL) != 0) { 959 decorations |= MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE; 960 } else { 961 decorations &= ~(MWMConstants.MWM_DECOR_RESIZEH | MWMConstants.MWM_DECOR_MAXIMIZE); 962 } 963 } 964 setMotifDecor(window, resizable, decorations, functions); 965 setOLDecor(window, resizable, decorations); 966 967 /* Some WMs need remap to redecorate the window */ 968 if (window.isShowing() && needRemap(window)) { 969 /* 970 * Do the re/mapping at the Xlib level. Since we essentially 971 * work around a WM bug we don't want this hack to be exposed 972 * to Intrinsics (i.e. don't mess with grabs, callbacks etc). 973 */ 974 window.xSetVisible(false); 975 XToolkit.XSync(); 976 window.xSetVisible(true); 977 } 978 } 979 980 /* 981 * Make specified shell resizable. 982 */ 983 static void setShellResizable(XDecoratedPeer window) { 984 if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting shell resizable " + window); 985 XToolkit.awtLock(); 986 try { 987 Rectangle shellBounds = window.getShellBounds(); 988 shellBounds.translate(-window.currentInsets.left, -window.currentInsets.top); 989 window.updateSizeHints(window.getDimensions()); 990 requestWMExtents(window.getWindow()); 991 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(), 992 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height); 993 /* REMINDER: will need to revisit when setExtendedStateBounds is added */ 994 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced. 995 //We need to update frame's minimum size, not to reset it 996 removeSizeHints(window, XUtilConstants.PMaxSize); 997 window.updateMinimumSize(); 998 999 /* Restore decorations */ 1000 setShellDecor(window); 1001 } finally { 1002 XToolkit.awtUnlock(); 1003 } 1004 } 1005 1006 /* 1007 * Make specified shell non-resizable. 1008 * If justChangeSize is false, update decorations as well. 1009 * @param shellBounds bounds of the shell window 1010 */ 1011 static void setShellNotResizable(XDecoratedPeer window, WindowDimensions newDimensions, Rectangle shellBounds, 1012 boolean justChangeSize) 1013 { 1014 if (insLog.isLoggable(PlatformLogger.FINE)) insLog.fine("Setting non-resizable shell " + window + ", dimensions " + newDimensions + 1015 ", shellBounds " + shellBounds +", just change size: " + justChangeSize); 1016 XToolkit.awtLock(); 1017 try { 1018 /* Fix min/max size hints at the specified values */ 1019 if (!shellBounds.isEmpty()) { 1020 window.updateSizeHints(newDimensions); 1021 requestWMExtents(window.getWindow()); 1022 XToolkit.XSync(); 1023 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), window.getShell(), 1024 shellBounds.x, shellBounds.y, shellBounds.width, shellBounds.height); 1025 } 1026 if (!justChangeSize) { /* update decorations */ 1027 setShellDecor(window); 1028 } 1029 } finally { 1030 XToolkit.awtUnlock(); 1031 } 1032 } 1033 1034 /*****************************************************************************\ 1035 * Protocols support 1036 */ 1037 private HashMap<Class<?>, Collection<?>> protocolsMap = new HashMap<Class<?>, Collection<?>>(); 1038 /** 1039 * Returns all protocols supporting given protocol interface 1040 */ 1041 <T> Collection<T> getProtocols(Class<T> protocolInterface) { 1042 Collection<T> res = (Collection<T>) protocolsMap.get(protocolInterface); 1043 if (res != null) { 1044 return res; 1045 } else { 1046 return new LinkedList<T>(); 1047 } 1048 } 1049 1050 private <T> void addProtocol(Class<T> protocolInterface, T protocol) { 1051 Collection<T> protocols = getProtocols(protocolInterface); 1052 protocols.add(protocol); 1053 protocolsMap.put(protocolInterface, protocols); 1054 } 1055 1056 boolean supportsDynamicLayout() { 1057 int wm = getWMID(); 1058 switch (wm) { 1059 case XWM.ENLIGHTEN_WM: 1060 case XWM.KDE2_WM: 1061 case XWM.SAWFISH_WM: 1062 case XWM.ICE_WM: 1063 case XWM.METACITY_WM: 1064 return true; 1065 case XWM.OPENLOOK_WM: 1066 case XWM.MOTIF_WM: 1067 case XWM.CDE_WM: 1068 return false; 1069 default: 1070 return false; 1071 } 1072 } 1073 1074 1075 /** 1076 * Check if state is supported. 1077 * Note that a compound state is always reported as not supported. 1078 * Note also that MAXIMIZED_BOTH is considered not a compound state. 1079 * Therefore, a compound state is just ICONIFIED | anything else. 1080 * 1081 */ 1082 boolean supportsExtendedState(int state) { 1083 switch (state) { 1084 case Frame.MAXIMIZED_VERT: 1085 case Frame.MAXIMIZED_HORIZ: 1086 /* 1087 * WMs that talk NET/WIN protocol, but do not support 1088 * unidirectional maximization. 1089 */ 1090 if (getWMID() == METACITY_WM) { 1091 /* "This is a deliberate policy decision." -hp */ 1092 return false; 1093 } 1094 /* FALLTROUGH */ 1095 case Frame.MAXIMIZED_BOTH: 1096 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1097 if (proto.supportsState(state)) { 1098 return true; 1099 } 1100 } 1101 default: 1102 return false; 1103 } 1104 } 1105 1106 /*****************************************************************************\ 1107 * 1108 * Reading state from different protocols 1109 * 1110 \*****************************************************************************/ 1111 1112 1113 int getExtendedState(XWindowPeer window) { 1114 int state = 0; 1115 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1116 state |= proto.getState(window); 1117 } 1118 if (state != 0) { 1119 return state; 1120 } else { 1121 return Frame.NORMAL; 1122 } 1123 } 1124 1125 /*****************************************************************************\ 1126 * 1127 * Notice window state change when WM changes a property on the window ... 1128 * 1129 \*****************************************************************************/ 1130 1131 1132 /* 1133 * Check if property change is a window state protocol message. 1134 */ 1135 boolean isStateChange(XDecoratedPeer window, XPropertyEvent e) { 1136 if (!window.isShowing()) { 1137 stateLog.finer("Window is not showing"); 1138 return false; 1139 } 1140 1141 int wm_state = window.getWMState(); 1142 if (wm_state == XUtilConstants.WithdrawnState) { 1143 stateLog.finer("WithdrawnState"); 1144 return false; 1145 } else { 1146 stateLog.finer("Window WM_STATE is " + wm_state); 1147 } 1148 boolean is_state_change = false; 1149 if (e.get_atom() == XA_WM_STATE.getAtom()) { 1150 is_state_change = true; 1151 } 1152 1153 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1154 is_state_change |= proto.isStateChange(e); 1155 stateLog.finest(proto + ": is state changed = " + is_state_change); 1156 } 1157 return is_state_change; 1158 } 1159 1160 /* 1161 * Returns current state (including extended) of a given window. 1162 */ 1163 int getState(XDecoratedPeer window) { 1164 int res = 0; 1165 final int wm_state = window.getWMState(); 1166 if (wm_state == XUtilConstants.IconicState) { 1167 res = Frame.ICONIFIED; 1168 } else { 1169 res = Frame.NORMAL; 1170 } 1171 res |= getExtendedState(window); 1172 return res; 1173 } 1174 1175 /*****************************************************************************\ 1176 * 1177 * Setting/changing window state ... 1178 * 1179 \*****************************************************************************/ 1180 1181 /** 1182 * Moves window to the specified layer, layer is one of the constants defined 1183 * in XLayerProtocol 1184 */ 1185 void setLayer(XWindowPeer window, int layer) { 1186 for (XLayerProtocol proto : getProtocols(XLayerProtocol.class)) { 1187 if (proto.supportsLayer(layer)) { 1188 proto.setLayer(window, layer); 1189 } 1190 } 1191 XToolkit.XSync(); 1192 } 1193 1194 void setExtendedState(XWindowPeer window, int state) { 1195 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1196 if (proto.supportsState(state)) { 1197 proto.setState(window, state); 1198 break; 1199 } 1200 } 1201 1202 if (!window.isShowing()) { 1203 /* 1204 * Purge KWM bits. 1205 * Not really tested with KWM, only with WindowMaker. 1206 */ 1207 XToolkit.awtLock(); 1208 try { 1209 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(), 1210 window.getWindow(), 1211 XA_KWM_WIN_ICONIFIED.getAtom()); 1212 XlibWrapper.XDeleteProperty(XToolkit.getDisplay(), 1213 window.getWindow(), 1214 XA_KWM_WIN_MAXIMIZED.getAtom()); 1215 } 1216 finally { 1217 XToolkit.awtUnlock(); 1218 } 1219 } 1220 XToolkit.XSync(); 1221 } 1222 1223 1224 /* 1225 * Work around for 4775545. 1226 * 1227 * If WM exits while the top-level is shaded, the shaded hint remains 1228 * on the top-level properties. When WM restarts and sees the shaded 1229 * window it can reparent it into a "pre-shaded" decoration frame 1230 * (Metacity does), and our insets logic will go crazy, b/c it will 1231 * see a huge nagative bottom inset. There's no clean solution for 1232 * this, so let's just be weasels and drop the shaded hint if we 1233 * detect that WM exited. NB: we are in for a race condition with WM 1234 * restart here. NB2: e.g. WindowMaker saves the state in a private 1235 * property that this code knows nothing about, so this workaround is 1236 * not effective; other WMs might play similar tricks. 1237 */ 1238 void unshadeKludge(XDecoratedPeer window) { 1239 assert(window.isShowing()); 1240 1241 for (XStateProtocol proto : getProtocols(XStateProtocol.class)) { 1242 proto.unshadeKludge(window); 1243 } 1244 XToolkit.XSync(); 1245 } 1246 1247 static boolean inited = false; 1248 static void init() { 1249 if (inited) { 1250 return; 1251 } 1252 1253 initAtoms(); 1254 getWM(); 1255 inited = true; 1256 } 1257 1258 void initializeProtocols() { 1259 XNETProtocol net_protocol = g_net_protocol; 1260 if (net_protocol != null) { 1261 if (!net_protocol.active()) { 1262 net_protocol = null; 1263 } else { 1264 if (net_protocol.doStateProtocol()) { 1265 addProtocol(XStateProtocol.class, net_protocol); 1266 } 1267 if (net_protocol.doLayerProtocol()) { 1268 addProtocol(XLayerProtocol.class, net_protocol); 1269 } 1270 } 1271 } 1272 1273 XWINProtocol win = g_win_protocol; 1274 if (win != null) { 1275 if (win.active()) { 1276 if (win.doStateProtocol()) { 1277 addProtocol(XStateProtocol.class, win); 1278 } 1279 if (win.doLayerProtocol()) { 1280 addProtocol(XLayerProtocol.class, win); 1281 } 1282 } 1283 } 1284 } 1285 1286 HashMap storedInsets = new HashMap(); 1287 Insets guessInsets(XDecoratedPeer window) { 1288 Insets res = (Insets)storedInsets.get(window.getClass()); 1289 if (res == null) { 1290 switch (WMID) { 1291 case ENLIGHTEN_WM: 1292 res = new Insets(19, 4, 4, 4); 1293 break; 1294 case CDE_WM: 1295 res = new Insets(28, 6, 6, 6); 1296 break; 1297 case NO_WM: 1298 case LG3D_WM: 1299 res = zeroInsets; 1300 break; 1301 case MOTIF_WM: 1302 case OPENLOOK_WM: 1303 default: 1304 res = defaultInsets; 1305 } 1306 } 1307 if (insLog.isLoggable(PlatformLogger.FINEST)) insLog.finest("WM guessed insets: " + res); 1308 return res; 1309 } 1310 /* 1311 * Some buggy WMs ignore window gravity when processing 1312 * ConfigureRequest and position window as if the gravity is Static. 1313 * We work around this in MWindowPeer.pReshape(). 1314 * 1315 * Starting with 1.5 we have introduced an Environment variable 1316 * _JAVA_AWT_WM_STATIC_GRAVITY that can be set to indicate to Java 1317 * explicitly that the WM has this behaviour, example is FVWM. 1318 */ 1319 1320 static int awtWMStaticGravity = -1; 1321 static boolean configureGravityBuggy() { 1322 1323 if (awtWMStaticGravity == -1) { 1324 awtWMStaticGravity = (XToolkit.getEnv("_JAVA_AWT_WM_STATIC_GRAVITY") != null) ? 1 : 0; 1325 } 1326 1327 if (awtWMStaticGravity == 1) { 1328 return true; 1329 } 1330 1331 switch(getWMID()) { 1332 case XWM.ICE_WM: 1333 /* 1334 * See bug #228981 at IceWM's SourceForge pages. 1335 * Latest stable version 1.0.8-6 still has this problem. 1336 */ 1337 /** 1338 * Version 1.2.2 doesn't have this problem 1339 */ 1340 // Detect IceWM version 1341 if (g_net_protocol != null) { 1342 String wm_name = g_net_protocol.getWMName(); 1343 Pattern pat = Pattern.compile("^IceWM (\\d+)\\.(\\d+)\\.(\\d+).*$"); 1344 try { 1345 Matcher match = pat.matcher(wm_name); 1346 if (match.matches()) { 1347 int v1 = Integer.parseInt(match.group(1)); 1348 int v2 = Integer.parseInt(match.group(2)); 1349 int v3 = Integer.parseInt(match.group(3)); 1350 return !(v1 > 1 || (v1 == 1 && (v2 > 2 || (v2 == 2 && v3 >=2)))); 1351 } 1352 } catch (Exception e) { 1353 return true; 1354 } 1355 } 1356 return true; 1357 case XWM.ENLIGHTEN_WM: 1358 /* At least E16 is buggy. */ 1359 return true; 1360 default: 1361 return false; 1362 } 1363 } 1364 1365 /* 1366 * @return if WM implements the insets property - returns insets with values 1367 * specified in that property, null otherwise. 1368 */ 1369 public static Insets getInsetsFromExtents(long window) { 1370 if (window == XConstants.None) { 1371 return null; 1372 } 1373 XNETProtocol net_protocol = getWM().getNETProtocol(); 1374 if (net_protocol != null && net_protocol.active()) { 1375 Insets insets = getInsetsFromProp(window, XA_NET_FRAME_EXTENTS); 1376 insLog.fine("_NET_FRAME_EXTENTS: {0}", insets); 1377 1378 if (insets != null) { 1379 return insets; 1380 } 1381 } 1382 switch(getWMID()) { 1383 case XWM.KDE2_WM: 1384 return getInsetsFromProp(window, XA_KDE_NET_WM_FRAME_STRUT); 1385 case XWM.ENLIGHTEN_WM: 1386 return getInsetsFromProp(window, XA_E_FRAME_SIZE); 1387 default: 1388 return null; 1389 } 1390 } 1391 1392 /** 1393 * Helper function reads property of type CARDINAL[4] = { left, right, top, bottom } 1394 * and converts it to Insets object. 1395 */ 1396 public static Insets getInsetsFromProp(long window, XAtom atom) { 1397 if (window == XConstants.None) { 1398 return null; 1399 } 1400 1401 WindowPropertyGetter getter = 1402 new WindowPropertyGetter(window, atom, 1403 0, 4, false, XAtom.XA_CARDINAL); 1404 try { 1405 if (getter.execute() != XConstants.Success 1406 || getter.getData() == 0 1407 || getter.getActualType() != XAtom.XA_CARDINAL 1408 || getter.getActualFormat() != 32) 1409 { 1410 return null; 1411 } else { 1412 return new Insets((int)Native.getCard32(getter.getData(), 2), // top 1413 (int)Native.getCard32(getter.getData(), 0), // left 1414 (int)Native.getCard32(getter.getData(), 3), // bottom 1415 (int)Native.getCard32(getter.getData(), 1)); // right 1416 } 1417 } finally { 1418 getter.dispose(); 1419 } 1420 } 1421 1422 /** 1423 * Asks WM to fill Frame Extents (insets) for the window. 1424 */ 1425 public static void requestWMExtents(long window) { 1426 if (window == XConstants.None) { // not initialized 1427 return; 1428 } 1429 1430 log.fine("Requesting FRAME_EXTENTS"); 1431 1432 XClientMessageEvent msg = new XClientMessageEvent(); 1433 msg.zero(); 1434 msg.set_type(XConstants.ClientMessage); 1435 msg.set_display(XToolkit.getDisplay()); 1436 msg.set_window(window); 1437 msg.set_format(32); 1438 XToolkit.awtLock(); 1439 try { 1440 XNETProtocol net_protocol = getWM().getNETProtocol(); 1441 if (net_protocol != null && net_protocol.active()) { 1442 msg.set_message_type(XA_NET_REQUEST_FRAME_EXTENTS.getAtom()); 1443 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), 1444 false, 1445 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask, 1446 msg.getPData()); 1447 } 1448 if (getWMID() == XWM.KDE2_WM) { 1449 msg.set_message_type(XA_KDE_NET_WM_FRAME_STRUT.getAtom()); 1450 XlibWrapper.XSendEvent(XToolkit.getDisplay(), XToolkit.getDefaultRootWindow(), 1451 false, 1452 XConstants.SubstructureRedirectMask | XConstants.SubstructureNotifyMask, 1453 msg.getPData()); 1454 } 1455 // XXX: should we wait for response? XIfEvent() would be useful here :) 1456 } finally { 1457 XToolkit.awtUnlock(); 1458 msg.dispose(); 1459 } 1460 } 1461 1462 /* syncTopLEvelPos() is necessary to insure that the window manager has in 1463 * fact moved us to our final position relative to the reParented WM window. 1464 * We have noted a timing window which our shell has not been moved so we 1465 * screw up the insets thinking they are 0,0. Wait (for a limited period of 1466 * time to let the WM hava a chance to move us. 1467 * @param window window ID of the shell, assuming it is the window 1468 * which will NOT have zero coordinates after the complete 1469 * reparenting 1470 */ 1471 boolean syncTopLevelPos(long window, XWindowAttributes attrs) { 1472 int tries = 0; 1473 XToolkit.awtLock(); 1474 try { 1475 do { 1476 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), window, attrs.pData); 1477 if (attrs.get_x() != 0 || attrs.get_y() != 0) { 1478 return true; 1479 } 1480 tries++; 1481 XToolkit.XSync(); 1482 } while (tries < 50); 1483 } 1484 finally { 1485 XToolkit.awtUnlock(); 1486 } 1487 return false; 1488 } 1489 1490 Insets getInsets(XDecoratedPeer win, long window, long parent) { 1491 /* 1492 * Unfortunately the concept of "insets" borrowed to AWT 1493 * from Win32 is *absolutely*, *unbelievably* foreign to 1494 * X11. Few WMs provide the size of frame decor 1495 * (i.e. insets) in a property they set on the client 1496 * window, so we check if we can get away with just 1497 * peeking at it. [Future versions of wm-spec might add a 1498 * standardized hint for this]. 1499 * 1500 * Otherwise we do some special casing. Actually the 1501 * fallback code ("default" case) seems to cover most of 1502 * the existing WMs (modulo Reparent/Configure order 1503 * perhaps?). 1504 * 1505 * Fallback code tries to account for the two most common cases: 1506 * 1507 * . single reparenting 1508 * parent window is the WM frame 1509 * [twm, olwm, sawfish] 1510 * 1511 * . double reparenting 1512 * parent is a lining exactly the size of the client 1513 * grandpa is the WM frame 1514 * [mwm, e!, kwin, fvwm2 ... ] 1515 */ 1516 Insets correctWM = XWM.getInsetsFromExtents(window); 1517 insLog.finer("Got insets from property: {0}", correctWM); 1518 1519 if (correctWM == null) { 1520 correctWM = new Insets(0,0,0,0); 1521 1522 correctWM.top = -1; 1523 correctWM.left = -1; 1524 1525 XWindowAttributes lwinAttr = new XWindowAttributes(); 1526 XWindowAttributes pattr = new XWindowAttributes(); 1527 try { 1528 switch (XWM.getWMID()) { 1529 /* should've been done in awt_wm_getInsetsFromProp */ 1530 case XWM.ENLIGHTEN_WM: { 1531 /* enlightenment does double reparenting */ 1532 syncTopLevelPos(parent, lwinAttr); 1533 correctWM.left = lwinAttr.get_x(); 1534 correctWM.top = lwinAttr.get_y(); 1535 /* 1536 * Now get the actual dimensions of the parent window 1537 * resolve the difference. We can't rely on the left 1538 * to be equal to right or bottom... Enlightment 1539 * breaks that assumption. 1540 */ 1541 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1542 XlibUtil.getParentWindow(parent), 1543 pattr.pData); 1544 correctWM.right = pattr.get_width() - 1545 (lwinAttr.get_width() + correctWM.left); 1546 correctWM.bottom = pattr.get_height() - 1547 (lwinAttr.get_height() + correctWM.top); 1548 1549 break; 1550 } 1551 case XWM.ICE_WM: // for 1.2.2. 1552 case XWM.KDE2_WM: /* should've been done in getInsetsFromProp */ 1553 case XWM.CDE_WM: 1554 case XWM.MOTIF_WM: { 1555 /* these are double reparenting too */ 1556 if (syncTopLevelPos(parent, lwinAttr)) { 1557 correctWM.top = lwinAttr.get_y(); 1558 correctWM.left = lwinAttr.get_x(); 1559 correctWM.right = correctWM.left; 1560 correctWM.bottom = correctWM.left; 1561 } else { 1562 return null; 1563 } 1564 break; 1565 } 1566 case XWM.SAWFISH_WM: 1567 case XWM.OPENLOOK_WM: { 1568 /* single reparenting */ 1569 syncTopLevelPos(window, lwinAttr); 1570 correctWM.top = lwinAttr.get_y(); 1571 correctWM.left = lwinAttr.get_x(); 1572 correctWM.right = correctWM.left; 1573 correctWM.bottom = correctWM.left; 1574 break; 1575 } 1576 case XWM.OTHER_WM: 1577 default: { /* this is very similar to the E! case above */ 1578 insLog.finest("Getting correct insets for OTHER_WM/default, parent: {0}", parent); 1579 syncTopLevelPos(parent, lwinAttr); 1580 int status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1581 window, lwinAttr.pData); 1582 status = XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1583 parent, pattr.pData); 1584 if (lwinAttr.get_root() == parent) { 1585 insLog.finest("our parent is root so insets should be zero"); 1586 correctWM = new Insets(0, 0, 0, 0); 1587 break; 1588 } 1589 1590 /* 1591 * Check for double-reparenting WM. 1592 * 1593 * If the parent is exactly the same size as the 1594 * top-level assume taht it's the "lining" window and 1595 * that the grandparent is the actual frame (NB: we 1596 * have already handled undecorated windows). 1597 * 1598 * XXX: what about timing issues that syncTopLevelPos 1599 * is supposed to work around? 1600 */ 1601 if (lwinAttr.get_x() == 0 && lwinAttr.get_y() == 0 1602 && lwinAttr.get_width()+2*lwinAttr.get_border_width() == pattr.get_width() 1603 && lwinAttr.get_height()+2*lwinAttr.get_border_width() == pattr.get_height()) 1604 { 1605 insLog.finest("Double reparenting detected, pattr({2})={0}, lwinAttr({3})={1}", 1606 lwinAttr, pattr, parent, window); 1607 lwinAttr.set_x(pattr.get_x()); 1608 lwinAttr.set_y(pattr.get_y()); 1609 lwinAttr.set_border_width(lwinAttr.get_border_width()+pattr.get_border_width()); 1610 1611 final long grand_parent = XlibUtil.getParentWindow(parent); 1612 1613 if (grand_parent == lwinAttr.get_root()) { 1614 // This is not double-reparenting in a 1615 // general sense - we simply don't have 1616 // correct insets - return null to try to 1617 // get insets later 1618 return null; 1619 } else { 1620 parent = grand_parent; 1621 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1622 parent, 1623 pattr.pData); 1624 } 1625 } 1626 /* 1627 * XXX: To be absolutely correct, we'd need to take 1628 * parent's border-width into account too, but the 1629 * rest of the code is happily unaware about border 1630 * widths and inner/outer distinction, so for the time 1631 * being, just ignore it. 1632 */ 1633 insLog.finest("Attrs before calculation: pattr({2})={0}, lwinAttr({3})={1}", 1634 lwinAttr, pattr, parent, window); 1635 correctWM = new Insets(lwinAttr.get_y() + lwinAttr.get_border_width(), 1636 lwinAttr.get_x() + lwinAttr.get_border_width(), 1637 pattr.get_height() - (lwinAttr.get_y() + lwinAttr.get_height() + 2*lwinAttr.get_border_width()), 1638 pattr.get_width() - (lwinAttr.get_x() + lwinAttr.get_width() + 2*lwinAttr.get_border_width())); 1639 break; 1640 } /* default */ 1641 } /* switch (runningWM) */ 1642 } finally { 1643 lwinAttr.dispose(); 1644 pattr.dispose(); 1645 } 1646 } 1647 if (storedInsets.get(win.getClass()) == null) { 1648 storedInsets.put(win.getClass(), correctWM); 1649 } 1650 return correctWM; 1651 } 1652 boolean isDesktopWindow( long w ) { 1653 if (g_net_protocol != null) { 1654 XAtomList wtype = XAtom.get("_NET_WM_WINDOW_TYPE").getAtomListPropertyList( w ); 1655 return wtype.contains( XAtom.get("_NET_WM_WINDOW_TYPE_DESKTOP") ); 1656 } else { 1657 return false; 1658 } 1659 } 1660 1661 public XNETProtocol getNETProtocol() { 1662 return g_net_protocol; 1663 } 1664 1665 /** 1666 * Sets _NET_WN_ICON property on the window using the arrays of 1667 * raster-data for icons. If icons is null, removes _NET_WM_ICON 1668 * property. 1669 * This method invokes XNETProtocol.setWMIcon() for WMs that 1670 * support NET protocol. 1671 * 1672 * @return true if hint was modified successfully, false otherwise 1673 */ 1674 public boolean setNetWMIcon(XWindowPeer window, java.util.List<XIconInfo> icons) { 1675 if (g_net_protocol != null && g_net_protocol.active()) { 1676 g_net_protocol.setWMIcons(window, icons); 1677 return getWMID() != ICE_WM; 1678 } 1679 return false; 1680 } 1681 }