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