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