1 /* 2 * Copyright (c) 2011, 2012, 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 package sun.lwawt; 27 28 import java.awt.*; 29 import java.awt.event.*; 30 import java.awt.image.BufferedImage; 31 import java.awt.peer.*; 32 import java.util.List; 33 34 import javax.swing.*; 35 36 import sun.awt.*; 37 import sun.java2d.*; 38 import sun.java2d.loops.Blit; 39 import sun.java2d.loops.CompositeType; 40 import sun.java2d.pipe.Region; 41 import sun.util.logging.PlatformLogger; 42 43 public class LWWindowPeer 44 extends LWContainerPeer<Window, JComponent> 45 implements WindowPeer, FramePeer, DialogPeer, FullScreenCapable 46 { 47 public static enum PeerType { 48 SIMPLEWINDOW, 49 FRAME, 50 DIALOG, 51 EMBEDDEDFRAME 52 } 53 54 private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer"); 55 56 private PlatformWindow platformWindow; 57 58 // Window bounds reported by the native system (as opposed to 59 // regular bounds inherited from LWComponentPeer which are 60 // requested by user and may haven't been applied yet because 61 // of asynchronous requests to the windowing system) 62 private int sysX; 63 private int sysY; 64 private int sysW; 65 private int sysH; 66 67 private static final int MINIMUM_WIDTH = 1; 68 private static final int MINIMUM_HEIGHT = 1; 69 70 private Insets insets = new Insets(0, 0, 0, 0); 71 72 private GraphicsDevice graphicsDevice; 73 private GraphicsConfiguration graphicsConfig; 74 75 private SurfaceData surfaceData; 76 private final Object surfaceDataLock = new Object(); 77 78 private int backBufferCount; 79 private BufferCapabilities backBufferCaps; 80 81 // The back buffer is used for two purposes: 82 // 1. To render all the lightweight peers 83 // 2. To provide user with a BufferStrategy 84 // Need to check if a single back buffer can be used for both 85 // TODO: VolatileImage 86 // private VolatileImage backBuffer; 87 private volatile BufferedImage backBuffer; 88 89 private volatile int windowState = Frame.NORMAL; 90 91 // check that the mouse is over the window 92 private volatile boolean isMouseOver = false; 93 94 // A peer where the last mouse event came to. Used by cursor manager to 95 // find the component under cursor 96 private static volatile LWComponentPeer lastCommonMouseEventPeer = null; 97 98 // A peer where the last mouse event came to. Used to generate 99 // MOUSE_ENTERED/EXITED notifications 100 private volatile LWComponentPeer lastMouseEventPeer; 101 102 // Peers where all dragged/released events should come to, 103 // depending on what mouse button is being dragged according to Cocoa 104 private static LWComponentPeer mouseDownTarget[] = new LWComponentPeer[3]; 105 106 // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events 107 // on MOUSE_RELEASE. Click events are only generated if there were no drag 108 // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button 109 private static int mouseClickButtons = 0; 110 111 private volatile boolean isOpaque = true; 112 113 private static final Font DEFAULT_FONT = new Font("Lucida Grande", Font.PLAIN, 13); 114 115 private static LWWindowPeer grabbingWindow; 116 117 private volatile boolean skipNextFocusChange; 118 119 private static final Color nonOpaqueBackground = new Color(0, 0, 0, 0); 120 121 private volatile boolean textured; 122 123 /** 124 * Current modal blocker or null. 125 * 126 * Synchronization: peerTreeLock. 127 */ 128 private LWWindowPeer blocker; 129 130 public LWWindowPeer(Window target, PlatformComponent platformComponent, 131 PlatformWindow platformWindow) 132 { 133 super(target, platformComponent); 134 this.platformWindow = platformWindow; 135 136 Window owner = target.getOwner(); 137 LWWindowPeer ownerPeer = (owner != null) ? (LWWindowPeer)owner.getPeer() : null; 138 PlatformWindow ownerDelegate = (ownerPeer != null) ? ownerPeer.getPlatformWindow() : null; 139 140 // The delegate.initialize() needs a non-null GC on X11. 141 GraphicsConfiguration gc = getTarget().getGraphicsConfiguration(); 142 synchronized (getStateLock()) { 143 // graphicsConfig should be updated according to the real window 144 // bounds when the window is shown, see 4868278 145 this.graphicsConfig = gc; 146 } 147 148 if (!target.isFontSet()) { 149 target.setFont(DEFAULT_FONT); 150 } 151 152 if (!target.isBackgroundSet()) { 153 target.setBackground(SystemColor.window); 154 } else { 155 // first we check if user provided alpha for background. This is 156 // similar to what Apple's Java do. 157 // Since JDK7 we should rely on setOpacity() only. 158 // this.opacity = c.getAlpha(); 159 } 160 161 if (!target.isForegroundSet()) { 162 target.setForeground(SystemColor.windowText); 163 // we should not call setForeground because it will call a repaint 164 // which the peer may not be ready to do yet. 165 } 166 167 platformWindow.initialize(target, this, ownerDelegate); 168 } 169 170 @Override 171 void initializeImpl() { 172 super.initializeImpl(); 173 if (getTarget() instanceof Frame) { 174 setTitle(((Frame) getTarget()).getTitle()); 175 setState(((Frame) getTarget()).getExtendedState()); 176 } else if (getTarget() instanceof Dialog) { 177 setTitle(((Dialog) getTarget()).getTitle()); 178 } 179 180 setAlwaysOnTop(getTarget().isAlwaysOnTop()); 181 updateMinimumSize(); 182 183 final Shape shape = getTarget().getShape(); 184 if (shape != null) { 185 applyShape(Region.getInstance(shape, null)); 186 } 187 188 final float opacity = getTarget().getOpacity(); 189 if (opacity < 1.0f) { 190 setOpacity(opacity); 191 } 192 193 setOpaque(getTarget().isOpaque()); 194 195 updateInsets(platformWindow.getInsets()); 196 if (getSurfaceData() == null) { 197 replaceSurfaceData(false); 198 } 199 } 200 201 // Just a helper method 202 public PlatformWindow getPlatformWindow() { 203 return platformWindow; 204 } 205 206 @Override 207 protected LWWindowPeer getWindowPeerOrSelf() { 208 return this; 209 } 210 211 @Override 212 protected void initializeContainerPeer() { 213 // No-op as LWWindowPeer doesn't have any containerPeer 214 } 215 216 // ---- PEER METHODS ---- // 217 218 @Override 219 protected void disposeImpl() { 220 SurfaceData oldData = getSurfaceData(); 221 synchronized (surfaceDataLock){ 222 surfaceData = null; 223 } 224 if (oldData != null) { 225 oldData.invalidate(); 226 } 227 if (isGrabbing()) { 228 ungrab(); 229 } 230 destroyBuffers(); 231 platformWindow.dispose(); 232 super.disposeImpl(); 233 } 234 235 @Override 236 protected void setVisibleImpl(final boolean visible) { 237 super.setVisibleImpl(visible); 238 // TODO: update graphicsConfig, see 4868278 239 platformWindow.setVisible(visible); 240 if (isSimpleWindow()) { 241 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 242 243 if (visible) { 244 if (!getTarget().isAutoRequestFocus()) { 245 return; 246 } else { 247 requestWindowFocus(CausedFocusEvent.Cause.ACTIVATION); 248 } 249 // Focus the owner in case this window is focused. 250 } else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) { 251 // Transfer focus to the owner. 252 LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this); 253 if (owner != null) { 254 owner.requestWindowFocus(CausedFocusEvent.Cause.ACTIVATION); 255 } 256 } 257 } 258 } 259 260 @Override 261 public GraphicsConfiguration getGraphicsConfiguration() { 262 return graphicsConfig; 263 } 264 265 @Override 266 public boolean updateGraphicsData(GraphicsConfiguration gc) { 267 setGraphicsConfig(gc); 268 return false; 269 } 270 271 protected final Graphics getOnscreenGraphics(Color fg, Color bg, Font f) { 272 if (getSurfaceData() == null) { 273 return null; 274 } 275 if (fg == null) { 276 fg = SystemColor.windowText; 277 } 278 if (bg == null) { 279 bg = SystemColor.window; 280 } 281 if (f == null) { 282 f = DEFAULT_FONT; 283 } 284 return platformWindow.transformGraphics(new SunGraphics2D(getSurfaceData(), fg, bg, f)); 285 } 286 287 @Override 288 public void createBuffers(int numBuffers, BufferCapabilities caps) 289 throws AWTException 290 { 291 try { 292 // Assume this method is never called with numBuffers <= 1, as 0 is 293 // unsupported, and 1 corresponds to a SingleBufferStrategy which 294 // doesn't depend on the peer. Screen is considered as a separate 295 // "buffer", that's why numBuffers - 1 296 assert numBuffers > 1; 297 298 replaceSurfaceData(numBuffers - 1, caps, false); 299 } catch (InvalidPipeException z) { 300 throw new AWTException(z.toString()); 301 } 302 } 303 304 @Override 305 public final Image getBackBuffer() { 306 synchronized (getStateLock()) { 307 return backBuffer; 308 } 309 } 310 311 @Override 312 public void flip(int x1, int y1, int x2, int y2, 313 BufferCapabilities.FlipContents flipAction) 314 { 315 platformWindow.flip(x1, y1, x2, y2, flipAction); 316 } 317 318 @Override 319 public final void destroyBuffers() { 320 final Image oldBB = getBackBuffer(); 321 synchronized (getStateLock()) { 322 backBuffer = null; 323 } 324 if (oldBB != null) { 325 oldBB.flush(); 326 } 327 } 328 329 @Override 330 public void setBounds(int x, int y, int w, int h, int op) { 331 if ((op & SET_CLIENT_SIZE) != 0) { 332 // SET_CLIENT_SIZE is only applicable to window peers, so handle it here 333 // instead of pulling 'insets' field up to LWComponentPeer 334 // no need to add insets since Window's notion of width and height includes insets. 335 op &= ~SET_CLIENT_SIZE; 336 op |= SET_SIZE; 337 } 338 339 if (w < MINIMUM_WIDTH) { 340 w = MINIMUM_WIDTH; 341 } 342 if (h < MINIMUM_HEIGHT) { 343 h = MINIMUM_HEIGHT; 344 } 345 346 if (graphicsConfig instanceof TextureSizeConstraining) { 347 final int maxW = ((TextureSizeConstraining)graphicsConfig).getMaxTextureWidth(); 348 final int maxH = ((TextureSizeConstraining)graphicsConfig).getMaxTextureHeight(); 349 350 if (w > maxW) { 351 w = maxW; 352 } 353 if (h > maxH) { 354 h = maxH; 355 } 356 } 357 358 // Don't post ComponentMoved/Resized and Paint events 359 // until we've got a notification from the delegate 360 setBounds(x, y, w, h, op, false, false); 361 // Get updated bounds, so we don't have to handle 'op' here manually 362 Rectangle r = getBounds(); 363 platformWindow.setBounds(r.x, r.y, r.width, r.height); 364 } 365 366 @Override 367 public Point getLocationOnScreen() { 368 return platformWindow.getLocationOnScreen(); 369 } 370 371 /** 372 * Overridden from LWContainerPeer to return the correct insets. 373 * Insets are queried from the delegate and are kept up to date by 374 * requiering when needed (i.e. when the window geometry is changed). 375 */ 376 @Override 377 public Insets getInsets() { 378 synchronized (getStateLock()) { 379 return insets; 380 } 381 } 382 383 @Override 384 public FontMetrics getFontMetrics(Font f) { 385 // TODO: check for "use platform metrics" settings 386 return platformWindow.getFontMetrics(f); 387 } 388 389 @Override 390 public void toFront() { 391 platformWindow.toFront(); 392 } 393 394 @Override 395 public void toBack() { 396 platformWindow.toBack(); 397 } 398 399 @Override 400 public void setZOrder(ComponentPeer above) { 401 throw new RuntimeException("not implemented"); 402 } 403 404 @Override 405 public void setAlwaysOnTop(boolean value) { 406 platformWindow.setAlwaysOnTop(value); 407 } 408 409 @Override 410 public void updateFocusableWindowState() { 411 platformWindow.updateFocusableWindowState(); 412 } 413 414 @Override 415 public void setModalBlocked(Dialog blocker, boolean blocked) { 416 synchronized (getPeerTreeLock()) { 417 this.blocker = blocked ? (LWWindowPeer)blocker.getPeer() : null; 418 } 419 420 platformWindow.setModalBlocked(blocked); 421 } 422 423 @Override 424 public void updateMinimumSize() { 425 final Dimension min; 426 if (getTarget().isMinimumSizeSet()) { 427 min = getTarget().getMinimumSize(); 428 min.width = Math.max(min.width, MINIMUM_WIDTH); 429 min.height = Math.max(min.height, MINIMUM_HEIGHT); 430 } else { 431 min = new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT); 432 } 433 434 final int maxW, maxH; 435 if (graphicsConfig instanceof TextureSizeConstraining) { 436 maxW = ((TextureSizeConstraining)graphicsConfig).getMaxTextureWidth(); 437 maxH = ((TextureSizeConstraining)graphicsConfig).getMaxTextureHeight(); 438 } else { 439 maxW = maxH = Integer.MAX_VALUE; 440 } 441 442 final Dimension max; 443 if (getTarget().isMaximumSizeSet()) { 444 max = getTarget().getMaximumSize(); 445 max.width = Math.min(max.width, maxW); 446 max.height = Math.min(max.height, maxH); 447 } else { 448 max = new Dimension(maxW, maxH); 449 } 450 451 platformWindow.setSizeConstraints(min.width, min.height, max.width, max.height); 452 } 453 454 @Override 455 public void updateIconImages() { 456 getPlatformWindow().updateIconImages(); 457 } 458 459 @Override 460 public void setOpacity(float opacity) { 461 getPlatformWindow().setOpacity(opacity); 462 repaintPeer(); 463 } 464 465 @Override 466 public final void setOpaque(final boolean isOpaque) { 467 if (this.isOpaque != isOpaque) { 468 this.isOpaque = isOpaque; 469 updateOpaque(); 470 } 471 } 472 473 private void updateOpaque() { 474 getPlatformWindow().setOpaque(!isTranslucent()); 475 replaceSurfaceData(false); 476 repaintPeer(); 477 } 478 479 @Override 480 public void updateWindow() { 481 } 482 483 public final boolean isTextured() { 484 return textured; 485 } 486 487 public final void setTextured(final boolean isTextured) { 488 textured = isTextured; 489 } 490 491 public final boolean isTranslucent() { 492 synchronized (getStateLock()) { 493 /* 494 * Textured window is a special case of translucent window. 495 * The difference is only in nswindow background. So when we set 496 * texture property our peer became fully translucent. It doesn't 497 * fill background, create non opaque backbuffers and layer etc. 498 */ 499 return !isOpaque || isShaped() || isTextured(); 500 } 501 } 502 503 @Override 504 final void applyShapeImpl(final Region shape) { 505 super.applyShapeImpl(shape); 506 updateOpaque(); 507 } 508 509 @Override 510 public void repositionSecurityWarning() { 511 throw new RuntimeException("not implemented"); 512 } 513 514 // ---- FRAME PEER METHODS ---- // 515 516 @Override // FramePeer and DialogPeer 517 public void setTitle(String title) { 518 platformWindow.setTitle(title == null ? "" : title); 519 } 520 521 @Override 522 public void setMenuBar(MenuBar mb) { 523 platformWindow.setMenuBar(mb); 524 } 525 526 @Override // FramePeer and DialogPeer 527 public void setResizable(boolean resizable) { 528 platformWindow.setResizable(resizable); 529 } 530 531 @Override 532 public void setState(int state) { 533 platformWindow.setWindowState(state); 534 } 535 536 @Override 537 public int getState() { 538 return windowState; 539 } 540 541 @Override 542 public void setMaximizedBounds(Rectangle bounds) { 543 // TODO: not implemented 544 } 545 546 @Override 547 public void setBoundsPrivate(int x, int y, int width, int height) { 548 setBounds(x, y, width, height, SET_BOUNDS | NO_EMBEDDED_CHECK); 549 } 550 551 @Override 552 public Rectangle getBoundsPrivate() { 553 throw new RuntimeException("not implemented"); 554 } 555 556 // ---- DIALOG PEER METHODS ---- // 557 558 @Override 559 public void blockWindows(List<Window> windows) { 560 //TODO: LWX will probably need some collectJavaToplevels to speed this up 561 for (Window w : windows) { 562 WindowPeer wp = (WindowPeer)w.getPeer(); 563 if (wp != null) { 564 wp.setModalBlocked((Dialog)getTarget(), true); 565 } 566 } 567 } 568 569 // ---- PEER NOTIFICATIONS ---- // 570 571 public void notifyIconify(boolean iconify) { 572 //The toplevel target is Frame and states are applicable to it. 573 //Otherwise, the target is Window and it don't have state property. 574 //Hopefully, no such events are posted in the queue so consider the 575 //target as Frame in all cases. 576 577 // REMIND: should we send it anyway if the state not changed since last 578 // time? 579 WindowEvent iconifyEvent = new WindowEvent(getTarget(), 580 iconify ? WindowEvent.WINDOW_ICONIFIED 581 : WindowEvent.WINDOW_DEICONIFIED); 582 postEvent(iconifyEvent); 583 584 int newWindowState = iconify ? Frame.ICONIFIED : Frame.NORMAL; 585 postWindowStateChangedEvent(newWindowState); 586 587 // REMIND: RepaintManager doesn't repaint iconified windows and 588 // hence ignores any repaint request during deiconification. 589 // So, we need to repaint window explicitly when it becomes normal. 590 if (!iconify) { 591 repaintPeer(); 592 } 593 } 594 595 public void notifyZoom(boolean isZoomed) { 596 int newWindowState = isZoomed ? Frame.MAXIMIZED_BOTH : Frame.NORMAL; 597 postWindowStateChangedEvent(newWindowState); 598 } 599 600 /** 601 * Called by the delegate when any part of the window should be repainted. 602 */ 603 public void notifyExpose(final int x, final int y, final int w, final int h) { 604 // TODO: there's a serious problem with Swing here: it handles 605 // the exposition internally, so SwingPaintEventDispatcher always 606 // return null from createPaintEvent(). However, we flush the 607 // back buffer here unconditionally, so some flickering may appear. 608 // A possible solution is to split postPaintEvent() into two parts, 609 // and override that part which is only called after if 610 // createPaintEvent() returned non-null value and flush the buffer 611 // from the overridden method 612 flushOnscreenGraphics(); 613 repaintPeer(new Rectangle(x, y, w, h)); 614 } 615 616 /** 617 * Called by the delegate when this window is moved/resized by user. 618 * There's no notifyReshape() in LWComponentPeer as the only 619 * components which could be resized by user are top-level windows. 620 */ 621 public final void notifyReshape(int x, int y, int w, int h) { 622 boolean moved = false; 623 boolean resized = false; 624 synchronized (getStateLock()) { 625 moved = (x != sysX) || (y != sysY); 626 resized = (w != sysW) || (h != sysH); 627 sysX = x; 628 sysY = y; 629 sysW = w; 630 sysH = h; 631 } 632 633 // Check if anything changed 634 if (!moved && !resized) { 635 return; 636 } 637 // First, update peer's bounds 638 setBounds(x, y, w, h, SET_BOUNDS, false, false); 639 640 // Second, update the graphics config and surface data 641 checkIfOnNewScreen(); 642 if (resized) { 643 replaceSurfaceData(); 644 flushOnscreenGraphics(); 645 } 646 647 // Third, COMPONENT_MOVED/COMPONENT_RESIZED events 648 if (moved) { 649 handleMove(x, y, true); 650 } 651 if (resized) { 652 handleResize(w, h,true); 653 } 654 } 655 656 private void clearBackground(final int w, final int h) { 657 final Graphics g = getOnscreenGraphics(getForeground(), getBackground(), 658 getFont()); 659 if (g != null) { 660 try { 661 if (g instanceof Graphics2D) { 662 ((Graphics2D) g).setComposite(AlphaComposite.Src); 663 } 664 if (isTranslucent()) { 665 g.setColor(nonOpaqueBackground); 666 g.fillRect(0, 0, w, h); 667 } 668 if (!isTextured()) { 669 if (g instanceof SunGraphics2D) { 670 SG2DConstraint((SunGraphics2D) g, getRegion()); 671 } 672 g.setColor(getBackground()); 673 g.fillRect(0, 0, w, h); 674 } 675 } finally { 676 g.dispose(); 677 } 678 } 679 } 680 681 public void notifyUpdateCursor() { 682 getLWToolkit().getCursorManager().updateCursorLater(this); 683 } 684 685 public void notifyActivation(boolean activation) { 686 changeFocusedWindow(activation); 687 } 688 689 // MouseDown in non-client area 690 public void notifyNCMouseDown() { 691 // Ungrab except for a click on a Dialog with the grabbing owner 692 if (grabbingWindow != null && 693 grabbingWindow != getOwnerFrameDialog(this)) 694 { 695 grabbingWindow.ungrab(); 696 } 697 } 698 699 // ---- EVENTS ---- // 700 701 /* 702 * Called by the delegate to dispatch the event to Java. Event 703 * coordinates are relative to non-client window are, i.e. the top-left 704 * point of the client area is (insets.top, insets.left). 705 */ 706 public void dispatchMouseEvent(int id, long when, int button, 707 int x, int y, int screenX, int screenY, 708 int modifiers, int clickCount, boolean popupTrigger, 709 byte[] bdata) 710 { 711 // TODO: fill "bdata" member of AWTEvent 712 Rectangle r = getBounds(); 713 // findPeerAt() expects parent coordinates 714 LWComponentPeer targetPeer = findPeerAt(r.x + x, r.y + y); 715 716 if (id == MouseEvent.MOUSE_EXITED) { 717 isMouseOver = false; 718 if (lastMouseEventPeer != null) { 719 if (lastMouseEventPeer.isEnabled()) { 720 Point lp = lastMouseEventPeer.windowToLocal(x, y, 721 this); 722 postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), 723 MouseEvent.MOUSE_EXITED, when, 724 modifiers, lp.x, lp.y, screenX, 725 screenY, clickCount, popupTrigger, 726 button)); 727 } 728 729 // Sometimes we may get MOUSE_EXITED after lastCommonMouseEventPeer is switched 730 // to a peer from another window. So we must first check if this peer is 731 // the same as lastWindowPeer 732 if (lastCommonMouseEventPeer != null && lastCommonMouseEventPeer.getWindowPeerOrSelf() == this) { 733 lastCommonMouseEventPeer = null; 734 } 735 lastMouseEventPeer = null; 736 } 737 } else if(id == MouseEvent.MOUSE_ENTERED) { 738 isMouseOver = true; 739 if (targetPeer != null) { 740 if (targetPeer.isEnabled()) { 741 Point lp = targetPeer.windowToLocal(x, y, this); 742 postEvent(new MouseEvent(targetPeer.getTarget(), 743 MouseEvent.MOUSE_ENTERED, when, 744 modifiers, lp.x, lp.y, screenX, 745 screenY, clickCount, popupTrigger, 746 button)); 747 } 748 lastCommonMouseEventPeer = targetPeer; 749 lastMouseEventPeer = targetPeer; 750 } 751 } else { 752 PlatformWindow topmostPlatforWindow = 753 platformWindow.getTopmostPlatformWindowUnderMouse(); 754 755 LWWindowPeer topmostWindowPeer = 756 topmostPlatforWindow != null ? topmostPlatforWindow.getPeer() : null; 757 758 // topmostWindowPeer == null condition is added for the backward 759 // compatibility with applets. It can be removed when the 760 // getTopmostPlatformWindowUnderMouse() method will be properly 761 // implemented in CPlatformEmbeddedFrame class 762 if (topmostWindowPeer == this || topmostWindowPeer == null) { 763 generateMouseEnterExitEventsForComponents(when, button, x, y, 764 screenX, screenY, modifiers, clickCount, popupTrigger, 765 targetPeer); 766 } else { 767 LWComponentPeer topmostTargetPeer = 768 topmostWindowPeer != null ? topmostWindowPeer.findPeerAt(r.x + x, r.y + y) : null; 769 topmostWindowPeer.generateMouseEnterExitEventsForComponents(when, button, x, y, 770 screenX, screenY, modifiers, clickCount, popupTrigger, 771 topmostTargetPeer); 772 } 773 774 // TODO: fill "bdata" member of AWTEvent 775 776 int eventButtonMask = (button > 0)? MouseEvent.getMaskForButton(button) : 0; 777 int otherButtonsPressed = modifiers & ~eventButtonMask; 778 779 // For pressed/dragged/released events OS X treats other 780 // mouse buttons as if they were BUTTON2, so we do the same 781 int targetIdx = (button > 3) ? MouseEvent.BUTTON2 - 1 : button - 1; 782 783 // MOUSE_ENTERED/EXITED are generated for the components strictly under 784 // mouse even when dragging. That's why we first update lastMouseEventPeer 785 // based on initial targetPeer value and only then recalculate targetPeer 786 // for MOUSE_DRAGGED/RELEASED events 787 if (id == MouseEvent.MOUSE_PRESSED) { 788 789 // Ungrab only if this window is not an owned window of the grabbing one. 790 if (!isGrabbing() && grabbingWindow != null && 791 grabbingWindow != getOwnerFrameDialog(this)) 792 { 793 grabbingWindow.ungrab(); 794 } 795 if (otherButtonsPressed == 0) { 796 mouseClickButtons = eventButtonMask; 797 } else { 798 mouseClickButtons |= eventButtonMask; 799 } 800 801 // The window should be focused on mouse click. If it gets activated by the native platform, 802 // this request will be no op. It will take effect when: 803 // 1. A simple not focused window is clicked. 804 // 2. An active but not focused owner frame/dialog is clicked. 805 // The mouse event then will trigger a focus request "in window" to the component, so the window 806 // should gain focus before. 807 requestWindowFocus(CausedFocusEvent.Cause.MOUSE_EVENT); 808 809 mouseDownTarget[targetIdx] = targetPeer; 810 } else if (id == MouseEvent.MOUSE_DRAGGED) { 811 // Cocoa dragged event has the information about which mouse 812 // button is being dragged. Use it to determine the peer that 813 // should receive the dragged event. 814 targetPeer = mouseDownTarget[targetIdx]; 815 mouseClickButtons &= ~modifiers; 816 } else if (id == MouseEvent.MOUSE_RELEASED) { 817 // TODO: currently, mouse released event goes to the same component 818 // that received corresponding mouse pressed event. For most cases, 819 // it's OK, however, we need to make sure that our behavior is consistent 820 // with 1.6 for cases where component in question have been 821 // hidden/removed in between of mouse pressed/released events. 822 targetPeer = mouseDownTarget[targetIdx]; 823 824 if ((modifiers & eventButtonMask) == 0) { 825 mouseDownTarget[targetIdx] = null; 826 } 827 828 // mouseClickButtons is updated below, after MOUSE_CLICK is sent 829 } 830 831 if (targetPeer == null) { 832 //TODO This can happen if this window is invisible. this is correct behavior in this case? 833 targetPeer = this; 834 } 835 836 837 Point lp = targetPeer.windowToLocal(x, y, this); 838 if (targetPeer.isEnabled()) { 839 MouseEvent event = new MouseEvent(targetPeer.getTarget(), id, 840 when, modifiers, lp.x, lp.y, 841 screenX, screenY, clickCount, 842 popupTrigger, button); 843 postEvent(event); 844 } 845 846 if (id == MouseEvent.MOUSE_RELEASED) { 847 if ((mouseClickButtons & eventButtonMask) != 0 848 && targetPeer.isEnabled()) { 849 postEvent(new MouseEvent(targetPeer.getTarget(), 850 MouseEvent.MOUSE_CLICKED, 851 when, modifiers, 852 lp.x, lp.y, screenX, screenY, 853 clickCount, popupTrigger, button)); 854 } 855 mouseClickButtons &= ~eventButtonMask; 856 } 857 } 858 notifyUpdateCursor(); 859 } 860 861 private void generateMouseEnterExitEventsForComponents(long when, 862 int button, int x, int y, int screenX, int screenY, 863 int modifiers, int clickCount, boolean popupTrigger, 864 LWComponentPeer targetPeer) { 865 866 if (!isMouseOver || targetPeer == lastMouseEventPeer) { 867 return; 868 } 869 870 // Generate Mouse Exit for components 871 if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { 872 Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); 873 postEvent(new MouseEvent(lastMouseEventPeer.getTarget(), 874 MouseEvent.MOUSE_EXITED, 875 when, modifiers, 876 oldp.x, oldp.y, screenX, screenY, 877 clickCount, popupTrigger, button)); 878 } 879 lastCommonMouseEventPeer = targetPeer; 880 lastMouseEventPeer = targetPeer; 881 882 // Generate Mouse Enter for components 883 if (targetPeer != null && targetPeer.isEnabled()) { 884 Point newp = targetPeer.windowToLocal(x, y, this); 885 postEvent(new MouseEvent(targetPeer.getTarget(), 886 MouseEvent.MOUSE_ENTERED, 887 when, modifiers, 888 newp.x, newp.y, screenX, screenY, 889 clickCount, popupTrigger, button)); 890 } 891 } 892 893 public void dispatchMouseWheelEvent(long when, int x, int y, int modifiers, 894 int scrollType, int scrollAmount, 895 int wheelRotation, double preciseWheelRotation, 896 byte[] bdata) 897 { 898 // TODO: could we just use the last mouse event target here? 899 Rectangle r = getBounds(); 900 // findPeerAt() expects parent coordinates 901 final LWComponentPeer targetPeer = findPeerAt(r.x + x, r.y + y); 902 if (targetPeer == null || !targetPeer.isEnabled()) { 903 return; 904 } 905 906 Point lp = targetPeer.windowToLocal(x, y, this); 907 // TODO: fill "bdata" member of AWTEvent 908 // TODO: screenX/screenY 909 postEvent(new MouseWheelEvent(targetPeer.getTarget(), 910 MouseEvent.MOUSE_WHEEL, 911 when, modifiers, 912 lp.x, lp.y, 913 0, 0, /* screenX, Y */ 914 0 /* clickCount */, false /* popupTrigger */, 915 scrollType, scrollAmount, 916 wheelRotation, preciseWheelRotation)); 917 } 918 919 /* 920 * Called by the delegate when a key is pressed. 921 */ 922 public void dispatchKeyEvent(int id, long when, int modifiers, 923 int keyCode, char keyChar, int keyLocation) 924 { 925 LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 926 Component focusOwner = kfmPeer.getCurrentFocusOwner(); 927 928 if (focusOwner == null) { 929 focusOwner = kfmPeer.getCurrentFocusedWindow(); 930 if (focusOwner == null) { 931 focusOwner = this.getTarget(); 932 } 933 } 934 postEvent(new KeyEvent(focusOwner, id, when, modifiers, keyCode, keyChar, keyLocation)); 935 } 936 937 938 // ---- UTILITY METHODS ---- // 939 940 private void postWindowStateChangedEvent(int newWindowState) { 941 if (getTarget() instanceof Frame) { 942 AWTAccessor.getFrameAccessor().setExtendedState( 943 (Frame)getTarget(), newWindowState); 944 } 945 WindowEvent stateChangedEvent = new WindowEvent(getTarget(), 946 WindowEvent.WINDOW_STATE_CHANGED, 947 windowState, newWindowState); 948 postEvent(stateChangedEvent); 949 windowState = newWindowState; 950 } 951 952 private static int getGraphicsConfigScreen(GraphicsConfiguration gc) { 953 // TODO: this method can be implemented in a more 954 // efficient way by forwarding to the delegate 955 GraphicsDevice gd = gc.getDevice(); 956 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 957 GraphicsDevice[] gds = ge.getScreenDevices(); 958 for (int i = 0; i < gds.length; i++) { 959 if (gds[i] == gd) { 960 return i; 961 } 962 } 963 // Should never happen if gc is a screen device config 964 return 0; 965 } 966 967 /* 968 * This method is called when window's graphics config is changed from 969 * the app code (e.g. when the window is made non-opaque) or when 970 * the window is moved to another screen by user. 971 * 972 * Returns true if the graphics config has been changed, false otherwise. 973 */ 974 private boolean setGraphicsConfig(GraphicsConfiguration gc) { 975 synchronized (getStateLock()) { 976 if (graphicsConfig == gc) { 977 return false; 978 } 979 // If window's graphics config is changed from the app code, the 980 // config correspond to the same device as before; when the window 981 // is moved by user, graphicsDevice is updated in checkIfOnNewScreen(). 982 // In either case, there's nothing to do with screenOn here 983 graphicsConfig = gc; 984 } 985 // SurfaceData is replaced later in updateGraphicsData() 986 return true; 987 } 988 989 private void checkIfOnNewScreen() { 990 GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice(); 991 synchronized (getStateLock()) { 992 if (graphicsDevice == newGraphicsDevice) { 993 return; 994 } 995 graphicsDevice = newGraphicsDevice; 996 } 997 998 // TODO: DisplayChangedListener stuff 999 final GraphicsConfiguration newGC = newGraphicsDevice.getDefaultConfiguration(); 1000 1001 if (!setGraphicsConfig(newGC)) return; 1002 1003 SunToolkit.executeOnEventHandlerThread(getTarget(), new Runnable() { 1004 public void run() { 1005 AWTAccessor.getComponentAccessor().setGraphicsConfiguration(getTarget(), newGC); 1006 } 1007 }); 1008 } 1009 1010 /* 1011 * May be called by delegate to provide SD to Java2D code. 1012 */ 1013 public SurfaceData getSurfaceData() { 1014 synchronized (surfaceDataLock) { 1015 return surfaceData; 1016 } 1017 } 1018 1019 private void replaceSurfaceData() { 1020 replaceSurfaceData(true); 1021 } 1022 1023 private void replaceSurfaceData(boolean blit) { 1024 replaceSurfaceData(backBufferCount, backBufferCaps, blit); 1025 } 1026 1027 private void replaceSurfaceData(int newBackBufferCount, 1028 BufferCapabilities newBackBufferCaps, 1029 boolean blit) { 1030 synchronized (surfaceDataLock) { 1031 final SurfaceData oldData = getSurfaceData(); 1032 surfaceData = platformWindow.replaceSurfaceData(); 1033 // TODO: volatile image 1034 // VolatileImage oldBB = backBuffer; 1035 BufferedImage oldBB = backBuffer; 1036 backBufferCount = newBackBufferCount; 1037 backBufferCaps = newBackBufferCaps; 1038 final Rectangle size = getSize(); 1039 if (getSurfaceData() != null && oldData != getSurfaceData()) { 1040 clearBackground(size.width, size.height); 1041 } 1042 1043 if (blit) { 1044 blitSurfaceData(oldData, getSurfaceData()); 1045 } 1046 1047 if (oldData != null && oldData != getSurfaceData()) { 1048 // TODO: drop oldData for D3D/WGL pipelines 1049 // This can only happen when this peer is being created 1050 oldData.flush(); 1051 } 1052 1053 // TODO: volatile image 1054 // backBuffer = (VolatileImage)delegate.createBackBuffer(); 1055 backBuffer = (BufferedImage) platformWindow.createBackBuffer(); 1056 if (backBuffer != null) { 1057 Graphics g = backBuffer.getGraphics(); 1058 try { 1059 Rectangle r = getBounds(); 1060 if (g instanceof Graphics2D) { 1061 ((Graphics2D) g).setComposite(AlphaComposite.Src); 1062 } 1063 g.setColor(nonOpaqueBackground); 1064 g.fillRect(0, 0, r.width, r.height); 1065 if (g instanceof SunGraphics2D) { 1066 SG2DConstraint((SunGraphics2D) g, getRegion()); 1067 } 1068 if (!isTextured()) { 1069 g.setColor(getBackground()); 1070 g.fillRect(0, 0, r.width, r.height); 1071 } 1072 if (oldBB != null) { 1073 // Draw the old back buffer to the new one 1074 g.drawImage(oldBB, 0, 0, null); 1075 oldBB.flush(); 1076 } 1077 } finally { 1078 g.dispose(); 1079 } 1080 } 1081 } 1082 } 1083 1084 private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) { 1085 //TODO blit. proof-of-concept 1086 if (src != dst && src != null && dst != null 1087 && !(dst instanceof NullSurfaceData) 1088 && !(src instanceof NullSurfaceData) 1089 && src.getSurfaceType().equals(dst.getSurfaceType())) { 1090 final Rectangle size = getSize(); 1091 final Blit blit = Blit.locate(src.getSurfaceType(), 1092 CompositeType.Src, 1093 dst.getSurfaceType()); 1094 if (blit != null) { 1095 blit.Blit(src, dst, AlphaComposite.Src, 1096 getRegion(), 0, 0, 0, 0, size.width, size.height); 1097 } 1098 } 1099 } 1100 1101 public int getBackBufferCount() { 1102 return backBufferCount; 1103 } 1104 1105 public BufferCapabilities getBackBufferCaps() { 1106 return backBufferCaps; 1107 } 1108 1109 /* 1110 * Request the window insets from the delegate and compares it 1111 * with the current one. This method is mostly called by the 1112 * delegate, e.g. when the window state is changed and insets 1113 * should be recalculated. 1114 * 1115 * This method may be called on the toolkit thread. 1116 */ 1117 public boolean updateInsets(Insets newInsets) { 1118 boolean changed = false; 1119 synchronized (getStateLock()) { 1120 changed = (insets.equals(newInsets)); 1121 insets = newInsets; 1122 } 1123 1124 if (changed) { 1125 replaceSurfaceData(); 1126 repaintPeer(); 1127 } 1128 1129 return changed; 1130 } 1131 1132 public static LWWindowPeer getWindowUnderCursor() { 1133 return lastCommonMouseEventPeer != null ? lastCommonMouseEventPeer.getWindowPeerOrSelf() : null; 1134 } 1135 1136 public static LWComponentPeer<?, ?> getPeerUnderCursor() { 1137 return lastCommonMouseEventPeer; 1138 } 1139 1140 /* 1141 * Requests platform to set native focus on a frame/dialog. 1142 * In case of a simple window, triggers appropriate java focus change. 1143 */ 1144 public boolean requestWindowFocus(CausedFocusEvent.Cause cause) { 1145 if (focusLog.isLoggable(PlatformLogger.FINE)) { 1146 focusLog.fine("requesting native focus to " + this); 1147 } 1148 1149 if (!focusAllowedFor()) { 1150 focusLog.fine("focus is not allowed"); 1151 return false; 1152 } 1153 1154 if (platformWindow.rejectFocusRequest(cause)) { 1155 return false; 1156 } 1157 1158 Window currentActive = KeyboardFocusManager. 1159 getCurrentKeyboardFocusManager().getActiveWindow(); 1160 1161 // Make the owner active window. 1162 if (isSimpleWindow()) { 1163 LWWindowPeer owner = getOwnerFrameDialog(this); 1164 1165 // If owner is not natively active, request native 1166 // activation on it w/o sending events up to java. 1167 if (owner != null && !owner.platformWindow.isActive()) { 1168 if (focusLog.isLoggable(PlatformLogger.FINE)) { 1169 focusLog.fine("requesting native focus to the owner " + owner); 1170 } 1171 LWWindowPeer currentActivePeer = (currentActive != null ? 1172 (LWWindowPeer)currentActive.getPeer() : null); 1173 1174 // Ensure the opposite is natively active and suppress sending events. 1175 if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) { 1176 if (focusLog.isLoggable(PlatformLogger.FINE)) { 1177 focusLog.fine("the opposite is " + currentActivePeer); 1178 } 1179 currentActivePeer.skipNextFocusChange = true; 1180 } 1181 owner.skipNextFocusChange = true; 1182 1183 owner.platformWindow.requestWindowFocus(); 1184 } 1185 1186 // DKFM will synthesize all the focus/activation events correctly. 1187 changeFocusedWindow(true); 1188 return true; 1189 1190 // In case the toplevel is active but not focused, change focus directly, 1191 // as requesting native focus on it will not have effect. 1192 } else if (getTarget() == currentActive && !getTarget().hasFocus()) { 1193 1194 changeFocusedWindow(true); 1195 return true; 1196 } 1197 return platformWindow.requestWindowFocus(); 1198 } 1199 1200 private boolean focusAllowedFor() { 1201 Window window = getTarget(); 1202 // TODO: check if modal blocked 1203 return window.isVisible() && window.isEnabled() && isFocusableWindow(); 1204 } 1205 1206 private boolean isFocusableWindow() { 1207 boolean focusable = getTarget().isFocusableWindow(); 1208 if (isSimpleWindow()) { 1209 LWWindowPeer ownerPeer = getOwnerFrameDialog(this); 1210 if (ownerPeer == null) { 1211 return false; 1212 } 1213 return focusable && ownerPeer.getTarget().isFocusableWindow(); 1214 } 1215 return focusable; 1216 } 1217 1218 public boolean isSimpleWindow() { 1219 Window window = getTarget(); 1220 return !(window instanceof Dialog || window instanceof Frame); 1221 } 1222 1223 /* 1224 * Changes focused window on java level. 1225 */ 1226 private void changeFocusedWindow(boolean becomesFocused) { 1227 if (focusLog.isLoggable(PlatformLogger.FINE)) { 1228 focusLog.fine((becomesFocused?"gaining":"loosing") + " focus window: " + this); 1229 } 1230 if (skipNextFocusChange) { 1231 focusLog.fine("skipping focus change"); 1232 skipNextFocusChange = false; 1233 return; 1234 } 1235 if (!isFocusableWindow() && becomesFocused) { 1236 focusLog.fine("the window is not focusable"); 1237 return; 1238 } 1239 if (becomesFocused) { 1240 synchronized (getPeerTreeLock()) { 1241 if (blocker != null) { 1242 if (focusLog.isLoggable(PlatformLogger.FINEST)) { 1243 focusLog.finest("the window is blocked by " + blocker); 1244 } 1245 return; 1246 } 1247 } 1248 } 1249 1250 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 1251 Window oppositeWindow = becomesFocused ? kfmPeer.getCurrentFocusedWindow() : null; 1252 1253 // Note, the method is not called: 1254 // - when the opposite (gaining focus) window is an owned/owner window. 1255 // - for a simple window in any case. 1256 if (!becomesFocused && 1257 (isGrabbing() || getOwnerFrameDialog(grabbingWindow) == this)) 1258 { 1259 focusLog.fine("ungrabbing on " + grabbingWindow); 1260 // ungrab a simple window if its owner looses activation. 1261 grabbingWindow.ungrab(); 1262 } 1263 1264 kfmPeer.setCurrentFocusedWindow(becomesFocused ? getTarget() : null); 1265 1266 int eventID = becomesFocused ? WindowEvent.WINDOW_GAINED_FOCUS : WindowEvent.WINDOW_LOST_FOCUS; 1267 WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, oppositeWindow, System.currentTimeMillis()); 1268 1269 // TODO: wrap in SequencedEvent 1270 postEvent(windowEvent); 1271 } 1272 1273 static LWWindowPeer getOwnerFrameDialog(LWWindowPeer peer) { 1274 Window owner = (peer != null ? peer.getTarget().getOwner() : null); 1275 while (owner != null && !(owner instanceof Frame || owner instanceof Dialog)) { 1276 owner = owner.getOwner(); 1277 } 1278 return owner != null ? (LWWindowPeer)owner.getPeer() : null; 1279 } 1280 1281 /** 1282 * Returns the foremost modal blocker of this window, or null. 1283 */ 1284 public LWWindowPeer getBlocker() { 1285 synchronized (getPeerTreeLock()) { 1286 LWWindowPeer blocker = this.blocker; 1287 if (blocker == null) { 1288 return null; 1289 } 1290 while (blocker.blocker != null) { 1291 blocker = blocker.blocker; 1292 } 1293 return blocker; 1294 } 1295 } 1296 1297 public void enterFullScreenMode() { 1298 platformWindow.enterFullScreenMode(); 1299 } 1300 1301 public void exitFullScreenMode() { 1302 platformWindow.exitFullScreenMode(); 1303 } 1304 1305 public long getLayerPtr() { 1306 return getPlatformWindow().getLayerPtr(); 1307 } 1308 1309 void grab() { 1310 if (grabbingWindow != null && !isGrabbing()) { 1311 grabbingWindow.ungrab(); 1312 } 1313 grabbingWindow = this; 1314 } 1315 1316 void ungrab() { 1317 if (isGrabbing()) { 1318 grabbingWindow = null; 1319 postEvent(new UngrabEvent(getTarget())); 1320 } 1321 } 1322 1323 private boolean isGrabbing() { 1324 return this == grabbingWindow; 1325 } 1326 1327 @Override 1328 public String toString() { 1329 return super.toString() + " [target is " + getTarget() + "]"; 1330 } 1331 }