1 /* 2 * Copyright (c) 2002, 2016, 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 package sun.awt.X11; 26 27 import java.awt.*; 28 29 import java.awt.event.ComponentEvent; 30 import java.awt.event.InvocationEvent; 31 import java.awt.event.WindowEvent; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.Map; 35 36 import sun.awt.IconInfo; 37 import sun.util.logging.PlatformLogger; 38 39 import sun.awt.AWTAccessor; 40 import sun.awt.SunToolkit; 41 42 abstract class XDecoratedPeer extends XWindowPeer { 43 private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XDecoratedPeer"); 44 private static final PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XDecoratedPeer"); 45 private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.X11.focus.XDecoratedPeer"); 46 private static final PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XDecoratedPeer"); 47 48 // Set to true when we get the first ConfigureNotify after being 49 // reparented - indicates that WM has adopted the top-level. 50 boolean configure_seen; 51 boolean insets_corrected; 52 53 XIconWindow iconWindow; 54 WindowDimensions dimensions; 55 XContentWindow content; 56 Insets currentInsets; 57 XFocusProxyWindow focusProxy; 58 static final Map<Class<?>,Insets> lastKnownInsets = 59 Collections.synchronizedMap(new HashMap<>()); 60 61 XDecoratedPeer(Window target) { 62 super(target); 63 } 64 65 XDecoratedPeer(XCreateWindowParams params) { 66 super(params); 67 } 68 69 public long getShell() { 70 return window; 71 } 72 73 public long getContentWindow() { 74 return (content == null) ? window : content.getWindow(); 75 } 76 77 void preInit(XCreateWindowParams params) { 78 super.preInit(params); 79 winAttr.initialFocus = true; 80 81 currentInsets = new Insets(0,0,0,0); 82 if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) { 83 currentInsets = lastKnownInsets.get(getClass()); 84 } 85 applyGuessedInsets(); 86 87 Rectangle bounds = (Rectangle)params.get(BOUNDS); 88 dimensions = new WindowDimensions(bounds, getRealInsets(), false); 89 params.put(BOUNDS, dimensions.getClientRect()); 90 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 91 insLog.fine("Initial dimensions {0}", dimensions); 92 } 93 94 // Deny default processing of these events on the shell - proxy will take care of 95 // them instead 96 Long eventMask = (Long)params.get(EVENT_MASK); 97 params.add(EVENT_MASK, Long.valueOf(eventMask.longValue() & ~(XConstants.FocusChangeMask | XConstants.KeyPressMask | XConstants.KeyReleaseMask))); 98 } 99 100 void postInit(XCreateWindowParams params) { 101 // The size hints must be set BEFORE mapping the window (see 6895647) 102 updateSizeHints(dimensions); 103 104 // The super method maps the window if it's visible on the shared level 105 super.postInit(params); 106 107 // The lines that follow need to be in a postInit, so they 108 // happen after the X window is created. 109 initResizability(); 110 XWM.requestWMExtents(getWindow()); 111 112 content = XContentWindow.createContent(this); 113 114 if (warningWindow != null) { 115 warningWindow.toFront(); 116 } 117 focusProxy = createFocusProxy(); 118 } 119 120 void setIconHints(java.util.List<IconInfo> icons) { 121 if (!XWM.getWM().setNetWMIcon(this, icons)) { 122 if (icons.size() > 0) { 123 if (iconWindow == null) { 124 iconWindow = new XIconWindow(this); 125 } 126 iconWindow.setIconImages(icons); 127 } 128 } 129 } 130 131 public void updateMinimumSize() { 132 super.updateMinimumSize(); 133 updateMinSizeHints(); 134 } 135 136 private void updateMinSizeHints() { 137 if (isResizable()) { 138 Dimension minimumSize = getTargetMinimumSize(); 139 if (minimumSize != null) { 140 Insets insets = getRealInsets(); 141 int minWidth = minimumSize.width - insets.left - insets.right; 142 int minHeight = minimumSize.height - insets.top - insets.bottom; 143 if (minWidth < 0) minWidth = 0; 144 if (minHeight < 0) minHeight = 0; 145 setSizeHints(XUtilConstants.PMinSize | (isLocationByPlatform()?0:(XUtilConstants.PPosition | XUtilConstants.USPosition)), 146 getX(), getY(), minWidth, minHeight); 147 if (isVisible()) { 148 Rectangle bounds = getShellBounds(); 149 int nw = (bounds.width < minWidth) ? minWidth : bounds.width; 150 int nh = (bounds.height < minHeight) ? minHeight : bounds.height; 151 if (nw != bounds.width || nh != bounds.height) { 152 setShellSize(new Rectangle(0, 0, nw, nh)); 153 } 154 } 155 } else { 156 boolean isMinSizeSet = isMinSizeSet(); 157 XWM.removeSizeHints(this, XUtilConstants.PMinSize); 158 /* Some WMs need remap to redecorate the window */ 159 if (isMinSizeSet && isShowing() && XWM.needRemap(this)) { 160 /* 161 * Do the re/mapping at the Xlib level. Since we essentially 162 * work around a WM bug we don't want this hack to be exposed 163 * to Intrinsics (i.e. don't mess with grabs, callbacks etc). 164 */ 165 xSetVisible(false); 166 XToolkit.XSync(); 167 xSetVisible(true); 168 } 169 } 170 } 171 } 172 173 XFocusProxyWindow createFocusProxy() { 174 return new XFocusProxyWindow(this); 175 } 176 177 protected XAtomList getWMProtocols() { 178 XAtomList protocols = super.getWMProtocols(); 179 protocols.add(wm_delete_window); 180 protocols.add(wm_take_focus); 181 return protocols; 182 } 183 184 public Graphics getGraphics() { 185 AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); 186 return getGraphics(content.surfaceData, 187 compAccessor.getForeground(target), 188 compAccessor.getBackground(target), 189 compAccessor.getFont(target)); 190 } 191 192 public void setTitle(String title) { 193 if (log.isLoggable(PlatformLogger.Level.FINE)) { 194 log.fine("Title is " + title); 195 } 196 winAttr.title = title; 197 updateWMName(); 198 } 199 200 protected String getWMName() { 201 if (winAttr.title == null || winAttr.title.trim().equals("")) { 202 return " "; 203 } else { 204 return winAttr.title; 205 } 206 } 207 208 void updateWMName() { 209 super.updateWMName(); 210 String name = getWMName(); 211 XToolkit.awtLock(); 212 try { 213 if (name == null || name.trim().equals("")) { 214 name = "Java"; 215 } 216 XAtom iconNameAtom = XAtom.get(XAtom.XA_WM_ICON_NAME); 217 iconNameAtom.setProperty(getWindow(), name); 218 XAtom netIconNameAtom = XAtom.get("_NET_WM_ICON_NAME"); 219 netIconNameAtom.setPropertyUTF8(getWindow(), name); 220 } finally { 221 XToolkit.awtUnlock(); 222 } 223 } 224 225 // NOTE: This method may be called by privileged threads. 226 // DO NOT INVOKE CLIENT CODE ON THIS THREAD! 227 public void handleIconify() { 228 postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED)); 229 } 230 231 // NOTE: This method may be called by privileged threads. 232 // DO NOT INVOKE CLIENT CODE ON THIS THREAD! 233 public void handleDeiconify() { 234 postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED)); 235 } 236 237 public void handleFocusEvent(XEvent xev) { 238 super.handleFocusEvent(xev); 239 XFocusChangeEvent xfe = xev.get_xfocus(); 240 241 // If we somehow received focus events forward it instead to proxy 242 // FIXME: Shouldn't we instead check for inferrior? 243 if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { 244 focusLog.finer("Received focus event on shell: " + xfe); 245 } 246 // focusProxy.xRequestFocus(); 247 } 248 249 /*************************************************************************************** 250 * I N S E T S C O D E 251 **************************************************************************************/ 252 253 protected boolean isInitialReshape() { 254 return false; 255 } 256 257 private static Insets difference(Insets i1, Insets i2) { 258 return new Insets(i1.top-i2.top, i1.left - i2.left, i1.bottom-i2.bottom, i1.right-i2.right); 259 } 260 261 private static boolean isNull(Insets i) { 262 return (i == null) || ((i.left | i.top | i.right | i.bottom) == 0); 263 } 264 265 private static Insets copy(Insets i) { 266 return new Insets(i.top, i.left, i.bottom, i.right); 267 } 268 269 private Insets copyAndScaleDown(Insets i) { 270 return new Insets(scaleDown(i.top), scaleDown(i.left), 271 scaleDown(i.bottom), scaleDown(i.right)); 272 } 273 274 275 // insets which we get from WM (e.g from _NET_FRAME_EXTENTS) 276 private Insets wm_set_insets; 277 278 private Insets getWMSetInsets(XAtom changedAtom) { 279 if (isEmbedded()) { 280 return null; 281 } 282 283 if (wm_set_insets != null) { 284 return wm_set_insets; 285 } 286 287 if (changedAtom == null) { 288 wm_set_insets = XWM.getInsetsFromExtents(getWindow()); 289 } else { 290 wm_set_insets = XWM.getInsetsFromProp(getWindow(), changedAtom); 291 } 292 293 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 294 insLog.finer("FRAME_EXTENTS: {0}", wm_set_insets); 295 } 296 297 if (wm_set_insets != null) { 298 wm_set_insets = copyAndScaleDown(wm_set_insets); 299 } 300 return wm_set_insets; 301 } 302 303 private void resetWMSetInsets() { 304 wm_set_insets = null; 305 } 306 307 public void handlePropertyNotify(XEvent xev) { 308 super.handlePropertyNotify(xev); 309 310 XPropertyEvent ev = xev.get_xproperty(); 311 if( !insets_corrected && isReparented() && 312 XWM.getWMID() == XWM.UNITY_COMPIZ_WM) { 313 int state = XWM.getWM().getState(this); 314 if ((state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) { 315 // Stop ignoring ConfigureNotify because no extents will be sent 316 // by WM for initially maximized decorated window. 317 // Re-request window bounds to ensure actual dimensions and 318 // notify the target with the initial size. 319 insets_corrected = true; 320 XlibWrapper.XConfigureWindow(XToolkit.getDisplay(), 321 getWindow(), 0, 0); 322 } 323 } 324 if (ev.get_atom() == XWM.XA_KDE_NET_WM_FRAME_STRUT.getAtom() 325 || ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom()) 326 { 327 if (XWM.getWMID() != XWM.UNITY_COMPIZ_WM) { 328 getWMSetInsets(XAtom.get(ev.get_atom())); 329 } else { 330 if(!isReparented()) { 331 return; 332 } 333 wm_set_insets = null; 334 Insets in = getWMSetInsets(XAtom.get(ev.get_atom())); 335 if (isNull(in)) { 336 return; 337 } 338 if (!isEmbedded() && !isTargetUndecorated()) { 339 lastKnownInsets.put(getClass(), in); 340 } 341 if (!in.equals(dimensions.getInsets())) { 342 if (insets_corrected || isMaximized()) { 343 currentInsets = in; 344 insets_corrected = true; 345 // insets were changed by WM. To handle this situation 346 // re-request window bounds because the current 347 // dimensions may be not actual as well. 348 XlibWrapper.XConfigureWindow(XToolkit.getDisplay(), 349 getWindow(), 0, 0); 350 } else { 351 // recalculate dimensions when window is just created 352 // and the initially guessed insets were wrong 353 handleCorrectInsets(in); 354 } 355 } else if (!dimensions.isClientSizeSet()) { 356 insets_corrected = true; 357 // initial insets were guessed correctly. Re-request 358 // frame bounds because they may be changed by WM if the 359 // initial window position overlapped desktop's toolbars. 360 // This should initiate the final ConfigureNotify upon which 361 // the target will be notified with the final size. 362 XlibWrapper.XConfigureWindow(XToolkit.getDisplay(), 363 getWindow(), 0, 0); 364 } 365 } 366 } 367 } 368 369 long reparent_serial = 0; 370 371 public void handleReparentNotifyEvent(XEvent xev) { 372 XReparentEvent xe = xev.get_xreparent(); 373 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 374 insLog.fine(xe.toString()); 375 } 376 reparent_serial = xe.get_serial(); 377 XToolkit.awtLock(); 378 try { 379 long root = XlibWrapper.RootWindow(XToolkit.getDisplay(), getScreenNumber()); 380 381 if (isEmbedded()) { 382 setReparented(true); 383 insets_corrected = true; 384 return; 385 } 386 Component t = target; 387 if (getDecorations() == XWindowAttributesData.AWT_DECOR_NONE) { 388 setReparented(true); 389 insets_corrected = true; 390 reshape(dimensions, SET_SIZE, false); 391 } else if (xe.get_parent() == root) { 392 configure_seen = false; 393 insets_corrected = false; 394 395 /* 396 * We can be repareted to root for two reasons: 397 * . setVisible(false) 398 * . WM exited 399 */ 400 if (isVisible()) { /* WM exited */ 401 /* Work around 4775545 */ 402 XWM.getWM().unshadeKludge(this); 403 insLog.fine("- WM exited"); 404 } else { 405 insLog.fine(" - reparent due to hide"); 406 } 407 } else { /* reparented to WM frame, figure out our insets */ 408 setReparented(true); 409 insets_corrected = false; 410 if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) { 411 return; 412 } 413 414 // Check if we have insets provided by the WM 415 Insets correctWM = getWMSetInsets(null); 416 if (correctWM != null) { 417 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 418 insLog.finer("wm-provided insets {0}", correctWM); 419 } 420 // If these insets are equal to our current insets - no actions are necessary 421 Insets dimInsets = dimensions.getInsets(); 422 if (correctWM.equals(dimInsets)) { 423 insLog.finer("Insets are the same as estimated - no additional reshapes necessary"); 424 no_reparent_artifacts = true; 425 insets_corrected = true; 426 applyGuessedInsets(); 427 return; 428 } 429 } else { 430 correctWM = XWM.getWM().getInsets(this, xe.get_window(), xe.get_parent()); 431 if (correctWM != null) { 432 correctWM = copyAndScaleDown(correctWM); 433 } 434 435 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 436 if (correctWM != null) { 437 insLog.finer("correctWM {0}", correctWM); 438 } else { 439 insLog.finer("correctWM insets are not available, waiting for configureNotify"); 440 } 441 } 442 } 443 444 if (correctWM != null) { 445 handleCorrectInsets(correctWM); 446 } 447 } 448 } finally { 449 XToolkit.awtUnlock(); 450 } 451 } 452 453 protected void handleCorrectInsets(Insets correctWM) { 454 XToolkit.awtLock(); 455 try { 456 /* 457 * Ok, now see if we need adjust window size because 458 * initial insets were wrong (most likely they were). 459 */ 460 Insets correction = difference(correctWM, currentInsets); 461 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 462 insLog.finest("Corrention {0}", correction); 463 } 464 if (!isNull(correction)) { 465 currentInsets = copy(correctWM); 466 applyGuessedInsets(); 467 468 //Fix for 6318109: PIT: Min Size is not honored properly when a 469 //smaller size is specified in setSize(), XToolkit 470 //update minimum size hints 471 updateMinSizeHints(); 472 } 473 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 474 insLog.finer("Dimensions before reparent: " + dimensions); 475 } 476 477 dimensions.setInsets(getRealInsets()); 478 insets_corrected = true; 479 480 if (isMaximized()) { 481 return; 482 } 483 484 /* 485 * If this window has been sized by a pack() we need 486 * to keep the interior geometry intact. Since pack() 487 * computed width and height with wrong insets, we 488 * must adjust the target dimensions appropriately. 489 */ 490 if ((getHints().get_flags() & (XUtilConstants.USPosition | XUtilConstants.PPosition)) != 0) { 491 reshape(dimensions, SET_BOUNDS, false); 492 } else { 493 reshape(dimensions, SET_SIZE, false); 494 } 495 } finally { 496 XToolkit.awtUnlock(); 497 } 498 } 499 500 public void handleMoved(WindowDimensions dims) { 501 Point loc = dims.getLocation(); 502 AWTAccessor.getComponentAccessor().setLocation(target, loc.x, loc.y); 503 postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED)); 504 } 505 506 507 protected Insets guessInsets() { 508 if (isEmbedded() || isTargetUndecorated()) { 509 return new Insets(0, 0, 0, 0); 510 } else { 511 if (!isNull(currentInsets)) { 512 /* insets were set on wdata by System Properties */ 513 return copy(currentInsets); 514 } else { 515 Insets res = getWMSetInsets(null); 516 if (res == null) { 517 res = XWM.getWM().guessInsets(this); 518 if (res != null) { 519 res = copyAndScaleDown(res); 520 } 521 } 522 return res; 523 } 524 } 525 } 526 527 private void applyGuessedInsets() { 528 Insets guessed = guessInsets(); 529 currentInsets = copy(guessed); 530 } 531 532 public void revalidate() { 533 XToolkit.executeOnEventHandlerThread(target, new Runnable() { 534 public void run() { 535 target.invalidate(); 536 target.validate(); 537 } 538 }); 539 } 540 541 Insets getRealInsets() { 542 if (isNull(currentInsets)) { 543 applyGuessedInsets(); 544 } 545 return currentInsets; 546 } 547 548 public Insets getInsets() { 549 Insets in = copy(getRealInsets()); 550 in.top += getMenuBarHeight(); 551 if (insLog.isLoggable(PlatformLogger.Level.FINEST)) { 552 insLog.finest("Get insets returns {0}", in); 553 } 554 return in; 555 } 556 557 boolean gravityBug() { 558 return XWM.configureGravityBuggy(); 559 } 560 561 // The height of area used to display current active input method 562 int getInputMethodHeight() { 563 return 0; 564 } 565 566 void updateSizeHints(WindowDimensions dims) { 567 Rectangle rec = dims.getClientRect(); 568 checkShellRect(rec); 569 updateSizeHints(rec.x, rec.y, rec.width, rec.height); 570 } 571 572 void updateSizeHints() { 573 updateSizeHints(dimensions); 574 } 575 576 // Coordinates are that of the target 577 // Called only on Toolkit thread 578 public void reshape(WindowDimensions newDimensions, int op, 579 boolean userReshape) 580 { 581 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 582 insLog.fine("Reshaping " + this + " to " + newDimensions + " op " + op + " user reshape " + userReshape); 583 } 584 if (userReshape) { 585 // We handle only userReshape == true cases. It means that 586 // if the window manager or any other part of the windowing 587 // system sets inappropriate size for this window, we can 588 // do nothing but accept it. 589 Rectangle newBounds = newDimensions.getBounds(); 590 Insets insets = newDimensions.getInsets(); 591 // Inherit isClientSizeSet from newDimensions 592 if (newDimensions.isClientSizeSet()) { 593 newBounds = new Rectangle(newBounds.x, newBounds.y, 594 newBounds.width - insets.left - insets.right, 595 newBounds.height - insets.top - insets.bottom); 596 } 597 newDimensions = new WindowDimensions(newBounds, insets, newDimensions.isClientSizeSet()); 598 } 599 XToolkit.awtLock(); 600 try { 601 if (!isReparented() || !isVisible()) { 602 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 603 insLog.fine("- not reparented({0}) or not visible({1}), default reshape", 604 Boolean.valueOf(isReparented()), Boolean.valueOf(visible)); 605 } 606 607 // Fix for 6323293. 608 // This actually is needed to preserve compatibility with previous releases - 609 // some of licensees are expecting componentMoved event on invisible one while 610 // its location changes. 611 Point oldLocation = getLocation(); 612 613 Point newLocation = new Point(AWTAccessor.getComponentAccessor().getX(target), 614 AWTAccessor.getComponentAccessor().getY(target)); 615 616 if (!newLocation.equals(oldLocation)) { 617 handleMoved(newDimensions); 618 } 619 620 dimensions = new WindowDimensions(newDimensions); 621 updateSizeHints(dimensions); 622 Rectangle client = dimensions.getClientRect(); 623 checkShellRect(client); 624 setShellBounds(client); 625 if (content != null && 626 !content.getSize().equals(newDimensions.getSize())) 627 { 628 reconfigureContentWindow(newDimensions); 629 } 630 return; 631 } 632 633 int wm = XWM.getWMID(); 634 updateChildrenSizes(); 635 applyGuessedInsets(); 636 637 Rectangle shellRect = newDimensions.getClientRect(); 638 639 if (gravityBug()) { 640 Insets in = newDimensions.getInsets(); 641 shellRect.translate(in.left, in.top); 642 } 643 644 if ((op & NO_EMBEDDED_CHECK) == 0 && isEmbedded()) { 645 shellRect.setLocation(0, 0); 646 } 647 648 checkShellRectSize(shellRect); 649 if (!isEmbedded()) { 650 checkShellRectPos(shellRect); 651 } 652 653 op = op & ~NO_EMBEDDED_CHECK; 654 655 if (op == SET_LOCATION) { 656 setShellPosition(shellRect); 657 } else if (isResizable()) { 658 if (op == SET_BOUNDS) { 659 setShellBounds(shellRect); 660 } else { 661 setShellSize(shellRect); 662 } 663 } else { 664 XWM.setShellNotResizable(this, newDimensions, shellRect, true); 665 if (op == SET_BOUNDS) { 666 setShellPosition(shellRect); 667 } 668 } 669 670 reconfigureContentWindow(newDimensions); 671 } finally { 672 XToolkit.awtUnlock(); 673 } 674 } 675 676 /** 677 * @param x, y, width, heith - dimensions of the window with insets 678 */ 679 private void reshape(int x, int y, int width, int height, int operation, 680 boolean userReshape) 681 { 682 Rectangle newRec; 683 boolean setClient = false; 684 WindowDimensions dims = new WindowDimensions(dimensions); 685 switch (operation & (~NO_EMBEDDED_CHECK)) { 686 case SET_LOCATION: 687 // Set location always sets bounds location. However, until the window is mapped we 688 // should use client coordinates 689 dims.setLocation(x, y); 690 break; 691 case SET_SIZE: 692 // Set size sets bounds size. However, until the window is mapped we 693 // should use client coordinates 694 dims.setSize(width, height); 695 break; 696 case SET_CLIENT_SIZE: { 697 // Sets client rect size. Width and height contain insets. 698 Insets in = currentInsets; 699 width -= in.left+in.right; 700 height -= in.top+in.bottom; 701 dims.setClientSize(width, height); 702 break; 703 } 704 case SET_BOUNDS: 705 default: 706 dims.setLocation(x, y); 707 dims.setSize(width, height); 708 break; 709 } 710 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 711 insLog.fine("For the operation {0} new dimensions are {1}", 712 operationToString(operation), dims); 713 } 714 715 reshape(dims, operation, userReshape); 716 } 717 718 // This method gets overriden in XFramePeer & XDialogPeer. 719 abstract boolean isTargetUndecorated(); 720 721 /** 722 * @see java.awt.peer.ComponentPeer#setBounds 723 */ 724 public void setBounds(int x, int y, int width, int height, int op) { 725 // TODO: Rewrite with WindowDimensions 726 reshape(x, y, width, height, op, true); 727 validateSurface(); 728 } 729 730 // Coordinates are that of the shell 731 void reconfigureContentWindow(WindowDimensions dims) { 732 if (content == null) { 733 insLog.fine("WARNING: Content window is null"); 734 return; 735 } 736 content.setContentBounds(dims); 737 } 738 739 boolean no_reparent_artifacts = false; 740 public void handleConfigureNotifyEvent(XEvent xev) { 741 if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM && !insets_corrected) { 742 return; 743 } 744 assert (SunToolkit.isAWTLockHeldByCurrentThread()); 745 XConfigureEvent xe = xev.get_xconfigure(); 746 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 747 insLog.fine("Configure notify {0}", xe); 748 } 749 750 // XXX: should really only consider synthetic events, but 751 if (isReparented()) { 752 configure_seen = true; 753 } 754 755 if (!isMaximized() 756 && (xe.get_serial() == reparent_serial || xe.get_window() != getShell()) 757 && !no_reparent_artifacts) 758 { 759 insLog.fine("- reparent artifact, skipping"); 760 return; 761 } 762 no_reparent_artifacts = false; 763 764 /** 765 * When there is a WM we receive some CN before being visible and after. 766 * We should skip all CN which are before being visible, because we assume 767 * the gravity is in action while it is not yet. 768 * 769 * When there is no WM we receive CN only _before_ being visible. 770 * We should process these CNs. 771 */ 772 if (!isVisible() && XWM.getWMID() != XWM.NO_WM) { 773 insLog.fine(" - not visible, skipping"); 774 return; 775 } 776 777 /* 778 * Some window managers configure before we are reparented and 779 * the send event flag is set! ugh... (Enlighetenment for one, 780 * possibly MWM as well). If we haven't been reparented yet 781 * this is just the WM shuffling us into position. Ignore 782 * it!!!! or we wind up in a bogus location. 783 */ 784 int runningWM = XWM.getWMID(); 785 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 786 insLog.fine("reparented={0}, visible={1}, WM={2}, decorations={3}", 787 isReparented(), isVisible(), runningWM, getDecorations()); 788 } 789 if (!isReparented() && isVisible() && runningWM != XWM.NO_WM 790 && !XWM.isNonReparentingWM() 791 && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) { 792 insLog.fine("- visible but not reparented, skipping"); 793 return; 794 } 795 //Last chance to correct insets 796 if (!insets_corrected && getDecorations() != XWindowAttributesData.AWT_DECOR_NONE) { 797 long parent = XlibUtil.getParentWindow(window); 798 Insets correctWM = (parent != -1) ? XWM.getWM().getInsets(this, window, parent) : null; 799 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 800 if (correctWM != null) { 801 insLog.finer("Configure notify - insets : " + correctWM); 802 } else { 803 insLog.finer("Configure notify - insets are still not available"); 804 } 805 } 806 if (correctWM != null) { 807 handleCorrectInsets(copyAndScaleDown(correctWM)); 808 } else { 809 //Only one attempt to correct insets is made (to lower risk) 810 //if insets are still not available we simply set the flag 811 insets_corrected = true; 812 } 813 } 814 815 updateChildrenSizes(); 816 817 Point newLocation = getNewLocation(xe, currentInsets.left, currentInsets.top); 818 WindowDimensions newDimensions = 819 new WindowDimensions(newLocation, 820 new Dimension(scaleDown(xe.get_width()), 821 scaleDown(xe.get_height())), 822 copy(currentInsets), true); 823 824 if (insLog.isLoggable(PlatformLogger.Level.FINER)) { 825 insLog.finer("Insets are {0}, new dimensions {1}", 826 currentInsets, newDimensions); 827 } 828 829 checkIfOnNewScreen(newDimensions.getBounds()); 830 831 Point oldLocation = getLocation(); 832 dimensions = newDimensions; 833 if (!newLocation.equals(oldLocation)) { 834 handleMoved(newDimensions); 835 } 836 reconfigureContentWindow(newDimensions); 837 updateChildrenSizes(); 838 839 repositionSecurityWarning(); 840 } 841 842 private void checkShellRectSize(Rectangle shellRect) { 843 shellRect.width = Math.max(MIN_SIZE, shellRect.width); 844 shellRect.height = Math.max(MIN_SIZE, shellRect.height); 845 } 846 847 private void checkShellRectPos(Rectangle shellRect) { 848 int wm = XWM.getWMID(); 849 if (wm == XWM.MOTIF_WM || wm == XWM.CDE_WM) { 850 if (shellRect.x == 0 && shellRect.y == 0) { 851 shellRect.x = shellRect.y = 1; 852 } 853 } 854 } 855 856 private void checkShellRect(Rectangle shellRect) { 857 checkShellRectSize(shellRect); 858 checkShellRectPos(shellRect); 859 } 860 861 public void setShellBounds(Rectangle rec) { 862 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 863 insLog.fine("Setting shell bounds on " + this + " to " + rec); 864 } 865 XToolkit.awtLock(); 866 try { 867 updateSizeHints(rec.x, rec.y, rec.width, rec.height); 868 XlibWrapper.XMoveResizeWindow(XToolkit.getDisplay(), getShell(), 869 scaleUp(rec.x), scaleUp(rec.y), 870 scaleUp(rec.width), scaleUp(rec.height)); 871 } 872 finally { 873 XToolkit.awtUnlock(); 874 } 875 } 876 public void setShellSize(Rectangle rec) { 877 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 878 insLog.fine("Setting shell size on " + this + " to " + rec); 879 } 880 XToolkit.awtLock(); 881 try { 882 updateSizeHints(rec.x, rec.y, rec.width, rec.height); 883 XlibWrapper.XResizeWindow(XToolkit.getDisplay(), getShell(), 884 scaleUp(rec.width), scaleUp(rec.height)); 885 } 886 finally { 887 XToolkit.awtUnlock(); 888 } 889 } 890 public void setShellPosition(Rectangle rec) { 891 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 892 insLog.fine("Setting shell position on " + this + " to " + rec); 893 } 894 XToolkit.awtLock(); 895 try { 896 updateSizeHints(rec.x, rec.y, rec.width, rec.height); 897 XlibWrapper.XMoveWindow(XToolkit.getDisplay(), getShell(), 898 scaleUp(rec.x), scaleUp(rec.y)); 899 } 900 finally { 901 XToolkit.awtUnlock(); 902 } 903 } 904 905 void initResizability() { 906 setResizable(winAttr.initialResizability); 907 } 908 public void setResizable(boolean resizable) { 909 int fs = winAttr.functions; 910 if (!isResizable() && resizable) { 911 currentInsets = new Insets(0, 0, 0, 0); 912 resetWMSetInsets(); 913 if (!isEmbedded()) { 914 setReparented(false); 915 } 916 winAttr.isResizable = resizable; 917 if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) { 918 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE); 919 } else { 920 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE); 921 } 922 winAttr.functions = fs; 923 XWM.setShellResizable(this); 924 } else if (isResizable() && !resizable) { 925 currentInsets = new Insets(0, 0, 0, 0); 926 resetWMSetInsets(); 927 if (!isEmbedded()) { 928 setReparented(false); 929 } 930 winAttr.isResizable = resizable; 931 if ((fs & MWMConstants.MWM_FUNC_ALL) != 0) { 932 fs |= (MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE); 933 } else { 934 fs &= ~(MWMConstants.MWM_FUNC_RESIZE | MWMConstants.MWM_FUNC_MAXIMIZE); 935 } 936 winAttr.functions = fs; 937 XWM.setShellNotResizable(this, dimensions, dimensions.getBounds(), false); 938 } 939 } 940 941 Rectangle getShellBounds() { 942 return dimensions.getClientRect(); 943 } 944 945 public Rectangle getBounds() { 946 return dimensions.getBounds(); 947 } 948 949 public Dimension getSize() { 950 return dimensions.getSize(); 951 } 952 953 public int getX() { 954 return dimensions.getLocation().x; 955 } 956 957 public int getY() { 958 return dimensions.getLocation().y; 959 } 960 961 public Point getLocation() { 962 return dimensions.getLocation(); 963 } 964 965 public int getAbsoluteX() { 966 // NOTE: returning this peer's location which is shell location 967 return dimensions.getScreenBounds().x; 968 } 969 970 public int getAbsoluteY() { 971 // NOTE: returning this peer's location which is shell location 972 return dimensions.getScreenBounds().y; 973 } 974 975 public int getWidth() { 976 return getSize().width; 977 } 978 979 public int getHeight() { 980 return getSize().height; 981 } 982 983 public final WindowDimensions getDimensions() { 984 return dimensions; 985 } 986 987 public Point getLocationOnScreen() { 988 XToolkit.awtLock(); 989 try { 990 if (configure_seen) { 991 return toGlobal(0,0); 992 } else { 993 Point location = target.getLocation(); 994 if (insLog.isLoggable(PlatformLogger.Level.FINE)) { 995 insLog.fine("getLocationOnScreen {0} not reparented: {1} ", 996 this, location); 997 } 998 return location; 999 } 1000 } finally { 1001 XToolkit.awtUnlock(); 1002 } 1003 } 1004 1005 1006 /*************************************************************************************** 1007 * END OF I N S E T S C O D E 1008 **************************************************************************************/ 1009 1010 protected boolean isEventDisabled(XEvent e) { 1011 switch (e.get_type()) { 1012 // Do not generate MOVED/RESIZED events since we generate them by ourselves 1013 case XConstants.ConfigureNotify: 1014 return true; 1015 case XConstants.EnterNotify: 1016 case XConstants.LeaveNotify: 1017 // Disable crossing event on outer borders of Frame so 1018 // we receive only one set of cross notifications(first set is from content window) 1019 return true; 1020 default: 1021 return super.isEventDisabled(e); 1022 } 1023 } 1024 1025 int getDecorations() { 1026 return winAttr.decorations; 1027 } 1028 1029 int getFunctions() { 1030 return winAttr.functions; 1031 } 1032 1033 public void setVisible(boolean vis) { 1034 if (log.isLoggable(PlatformLogger.Level.FINER)) { 1035 log.finer("Setting {0} to visible {1}", this, Boolean.valueOf(vis)); 1036 } 1037 if (vis && !isVisible()) { 1038 XWM.setShellDecor(this); 1039 super.setVisible(vis); 1040 if (winAttr.isResizable) { 1041 //Fix for 4320050: Minimum size for java.awt.Frame is not being enforced. 1042 //We need to update frame's minimum size, not to reset it 1043 XWM.removeSizeHints(this, XUtilConstants.PMaxSize); 1044 updateMinimumSize(); 1045 } 1046 } else { 1047 super.setVisible(vis); 1048 } 1049 } 1050 1051 protected void suppressWmTakeFocus(boolean doSuppress) { 1052 XAtomList protocols = getWMProtocols(); 1053 if (doSuppress) { 1054 protocols.remove(wm_take_focus); 1055 } else { 1056 protocols.add(wm_take_focus); 1057 } 1058 wm_protocols.setAtomListProperty(this, protocols); 1059 } 1060 1061 public void dispose() { 1062 if (content != null) { 1063 content.destroy(); 1064 } 1065 focusProxy.destroy(); 1066 1067 if (iconWindow != null) { 1068 iconWindow.destroy(); 1069 } 1070 1071 super.dispose(); 1072 } 1073 1074 public void handleClientMessage(XEvent xev) { 1075 super.handleClientMessage(xev); 1076 XClientMessageEvent cl = xev.get_xclient(); 1077 if ((wm_protocols != null) && (cl.get_message_type() == wm_protocols.getAtom())) { 1078 if (cl.get_data(0) == wm_delete_window.getAtom()) { 1079 handleQuit(); 1080 } else if (cl.get_data(0) == wm_take_focus.getAtom()) { 1081 handleWmTakeFocus(cl); 1082 } 1083 } 1084 } 1085 1086 private void handleWmTakeFocus(XClientMessageEvent cl) { 1087 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1088 focusLog.fine("WM_TAKE_FOCUS on {0}", this); 1089 } 1090 1091 if (XWM.getWMID() == XWM.UNITY_COMPIZ_WM) { 1092 // JDK-8159460 1093 Window focusedWindow = XKeyboardFocusManagerPeer.getInstance() 1094 .getCurrentFocusedWindow(); 1095 Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow); 1096 if (activeWindow != target) { 1097 requestWindowFocus(cl.get_data(1), true); 1098 } else { 1099 WindowEvent we = new WindowEvent(focusedWindow, 1100 WindowEvent.WINDOW_GAINED_FOCUS); 1101 sendEvent(we); 1102 } 1103 } else { 1104 requestWindowFocus(cl.get_data(1), true); 1105 } 1106 } 1107 1108 /** 1109 * Requests focus to this decorated top-level by requesting X input focus 1110 * to the shell window. 1111 */ 1112 protected void requestXFocus(long time, boolean timeProvided) { 1113 // We have proxied focus mechanism - instead of shell the focus is held 1114 // by "proxy" - invisible mapped window. When we want to set X input focus to 1115 // toplevel set it on proxy instead. 1116 if (focusProxy == null) { 1117 if (focusLog.isLoggable(PlatformLogger.Level.WARNING)) { 1118 focusLog.warning("Focus proxy is null for " + this); 1119 } 1120 } else { 1121 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1122 focusLog.fine("Requesting focus to proxy: " + focusProxy); 1123 } 1124 if (timeProvided) { 1125 focusProxy.xRequestFocus(time); 1126 } else { 1127 focusProxy.xRequestFocus(); 1128 } 1129 } 1130 } 1131 1132 XFocusProxyWindow getFocusProxy() { 1133 return focusProxy; 1134 } 1135 1136 public void handleQuit() { 1137 postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING)); 1138 } 1139 1140 final void dumpMe() { 1141 System.err.println(">>> Peer: " + x + ", " + y + ", " + width + ", " + height); 1142 } 1143 1144 final void dumpTarget() { 1145 AWTAccessor.ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); 1146 int getWidth = compAccessor.getWidth(target); 1147 int getHeight = compAccessor.getHeight(target); 1148 int getTargetX = compAccessor.getX(target); 1149 int getTargetY = compAccessor.getY(target); 1150 System.err.println(">>> Target: " + getTargetX + ", " + getTargetY + ", " + getWidth + ", " + getHeight); 1151 } 1152 1153 final void dumpShell() { 1154 dumpWindow("Shell", getShell()); 1155 } 1156 final void dumpContent() { 1157 dumpWindow("Content", getContentWindow()); 1158 } 1159 final void dumpParent() { 1160 long parent = XlibUtil.getParentWindow(getShell()); 1161 if (parent != 0) 1162 { 1163 dumpWindow("Parent", parent); 1164 } 1165 else 1166 { 1167 System.err.println(">>> NO PARENT"); 1168 } 1169 } 1170 1171 final void dumpWindow(String id, long window) { 1172 XWindowAttributes pattr = new XWindowAttributes(); 1173 try { 1174 XToolkit.awtLock(); 1175 try { 1176 int status = 1177 XlibWrapper.XGetWindowAttributes(XToolkit.getDisplay(), 1178 window, pattr.pData); 1179 } 1180 finally { 1181 XToolkit.awtUnlock(); 1182 } 1183 System.err.println(">>>> " + id + ": " + pattr.get_x() 1184 + ", " + pattr.get_y() + ", " + pattr.get_width() 1185 + ", " + pattr.get_height()); 1186 } finally { 1187 pattr.dispose(); 1188 } 1189 } 1190 1191 final void dumpAll() { 1192 dumpTarget(); 1193 dumpMe(); 1194 dumpParent(); 1195 dumpShell(); 1196 dumpContent(); 1197 } 1198 1199 boolean isMaximized() { 1200 return false; 1201 } 1202 1203 @Override 1204 boolean isOverrideRedirect() { 1205 return Window.Type.POPUP.equals(getWindowType()); 1206 } 1207 1208 public boolean requestWindowFocus(long time, boolean timeProvided) { 1209 focusLog.fine("Request for decorated window focus"); 1210 // If this is Frame or Dialog we can't assure focus request success - but we still can try 1211 // If this is Window and its owner Frame is active we can be sure request succedded. 1212 Window focusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow(); 1213 Window activeWindow = XWindowPeer.getDecoratedOwner(focusedWindow); 1214 1215 if (focusLog.isLoggable(PlatformLogger.Level.FINER)) { 1216 focusLog.finer("Current window is: active={0}, focused={1}", 1217 Boolean.valueOf(target == activeWindow), 1218 Boolean.valueOf(target == focusedWindow)); 1219 } 1220 1221 XWindowPeer toFocus = this; 1222 while (toFocus.nextTransientFor != null) { 1223 toFocus = toFocus.nextTransientFor; 1224 } 1225 if (toFocus == null || !toFocus.focusAllowedFor()) { 1226 // This might change when WM will have property to determine focus policy. 1227 // Right now, because policy is unknown we can't be sure we succedded 1228 return false; 1229 } 1230 if (this == toFocus) { 1231 if (isWMStateNetHidden()) { 1232 focusLog.fine("The window is unmapped, so rejecting the request"); 1233 return false; 1234 } 1235 if (target == activeWindow && target != focusedWindow) { 1236 // Happens when an owned window is currently focused 1237 focusLog.fine("Focus is on child window - transferring it back to the owner"); 1238 handleWindowFocusInSync(-1); 1239 return true; 1240 } 1241 Window realNativeFocusedWindow = XWindowPeer.getNativeFocusedWindow(); 1242 if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { 1243 focusLog.finest("Real native focused window: " + realNativeFocusedWindow + 1244 "\nKFM's focused window: " + focusedWindow); 1245 } 1246 1247 // A workaround for Metacity. See 6522725, 6613426, 7147075. 1248 if (target == realNativeFocusedWindow && XWM.getWMID() == XWM.METACITY_WM) { 1249 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1250 focusLog.fine("The window is already natively focused."); 1251 } 1252 return true; 1253 } 1254 } 1255 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1256 focusLog.fine("Requesting focus to " + (this == toFocus ? "this window" : toFocus)); 1257 } 1258 1259 if (timeProvided) { 1260 toFocus.requestXFocus(time); 1261 } else { 1262 toFocus.requestXFocus(); 1263 } 1264 return (this == toFocus); 1265 } 1266 1267 XWindowPeer actualFocusedWindow = null; 1268 void setActualFocusedWindow(XWindowPeer actualFocusedWindow) { 1269 synchronized(getStateLock()) { 1270 this.actualFocusedWindow = actualFocusedWindow; 1271 } 1272 } 1273 1274 boolean requestWindowFocus(XWindowPeer actualFocusedWindow, 1275 long time, boolean timeProvided) 1276 { 1277 setActualFocusedWindow(actualFocusedWindow); 1278 return requestWindowFocus(time, timeProvided); 1279 } 1280 public void handleWindowFocusIn(long serial) { 1281 if (null == actualFocusedWindow) { 1282 super.handleWindowFocusIn(serial); 1283 } else { 1284 /* 1285 * Fix for 6314575. 1286 * If this is a result of clicking on one of the Frame's component 1287 * then 'actualFocusedWindow' shouldn't be focused. A decision of focusing 1288 * it or not should be made after the appropriate Java mouse event (if any) 1289 * is handled by the component where 'actualFocusedWindow' value may be reset. 1290 * 1291 * The fix is based on the empiric fact consisting in that the component 1292 * receives native mouse event nearly at the same time the Frame receives 1293 * WM_TAKE_FOCUS (when FocusIn is generated via XSetInputFocus call) but 1294 * definetely before the Frame gets FocusIn event (when this method is called). 1295 */ 1296 postEvent(new InvocationEvent(target, new Runnable() { 1297 public void run() { 1298 XWindowPeer fw = null; 1299 synchronized (getStateLock()) { 1300 fw = actualFocusedWindow; 1301 actualFocusedWindow = null; 1302 if (null == fw || !fw.isVisible() || !fw.isFocusableWindow()) { 1303 fw = XDecoratedPeer.this; 1304 } 1305 } 1306 fw.handleWindowFocusIn_Dispatch(); 1307 } 1308 })); 1309 } 1310 } 1311 1312 public void handleWindowFocusOut(Window oppositeWindow, long serial) { 1313 Window actualFocusedWindow = XKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow(); 1314 1315 // If the actual focused window is not this decorated window then retain it. 1316 if (actualFocusedWindow != null && actualFocusedWindow != target) { 1317 Window owner = XWindowPeer.getDecoratedOwner(actualFocusedWindow); 1318 1319 if (owner != null && owner == target) { 1320 setActualFocusedWindow(AWTAccessor.getComponentAccessor().getPeer(actualFocusedWindow)); 1321 } 1322 } 1323 super.handleWindowFocusOut(oppositeWindow, serial); 1324 } 1325 }