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