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