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