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