rev 60071 : 8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI)
Reviewed-by: XXX

   1 /*
   2  * Copyright (c) 1996, 2020, 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.awt.windows;
  27 
  28 import java.awt.AWTEvent;
  29 import java.awt.AWTEventMulticaster;
  30 import java.awt.Color;
  31 import java.awt.Component;
  32 import java.awt.Container;
  33 import java.awt.Dialog;
  34 import java.awt.Dimension;
  35 import java.awt.Graphics;
  36 import java.awt.GraphicsConfiguration;
  37 import java.awt.GraphicsDevice;
  38 import java.awt.GraphicsEnvironment;
  39 import java.awt.Image;
  40 import java.awt.Insets;
  41 import java.awt.KeyboardFocusManager;
  42 import java.awt.Rectangle;
  43 import java.awt.Shape;
  44 import java.awt.SystemColor;
  45 import java.awt.Window;
  46 import java.awt.event.FocusEvent;
  47 import java.awt.event.WindowEvent;
  48 import java.awt.event.WindowListener;
  49 import java.awt.geom.AffineTransform;
  50 import java.awt.image.DataBufferInt;
  51 import java.awt.peer.WindowPeer;
  52 import java.beans.PropertyChangeEvent;
  53 import java.beans.PropertyChangeListener;
  54 import java.util.LinkedList;
  55 import java.util.List;
  56 
  57 import sun.awt.AWTAccessor;
  58 import sun.awt.AppContext;
  59 import sun.awt.DisplayChangedListener;
  60 import sun.awt.SunToolkit;
  61 import sun.awt.Win32GraphicsConfig;
  62 import sun.awt.Win32GraphicsDevice;
  63 import sun.awt.Win32GraphicsEnvironment;
  64 import sun.java2d.SunGraphicsEnvironment;
  65 import sun.java2d.pipe.Region;
  66 import sun.util.logging.PlatformLogger;
  67 


  68 public class WWindowPeer extends WPanelPeer implements WindowPeer,
  69        DisplayChangedListener
  70 {
  71 
  72     private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.windows.WWindowPeer");
  73     private static final PlatformLogger screenLog = PlatformLogger.getLogger("sun.awt.windows.screen.WWindowPeer");
  74 
  75     // we can't use WDialogPeer as blocker may be an instance of WPrintDialogPeer that
  76     // extends WWindowPeer, not WDialogPeer
  77     private WWindowPeer modalBlocker = null;
  78 
  79     private boolean isOpaque;
  80 
  81     private TranslucentWindowPainter painter;
  82 
  83     /*
  84      * A key used for storing a list of active windows in AppContext. The value
  85      * is a list of windows, sorted by the time of activation: later a window is
  86      * activated, greater its index is in the list.
  87      */
  88     private static final StringBuffer ACTIVE_WINDOWS_KEY =
  89         new StringBuffer("active_windows_list");
  90 
  91     /*
  92      * Listener for 'activeWindow' KFM property changes. It is added to each
  93      * AppContext KFM. See ActiveWindowListener inner class below.
  94      */
  95     private static PropertyChangeListener activeWindowListener =
  96         new ActiveWindowListener();
  97 
  98     /*
  99      * The object is a listener for the AppContext.GUI_DISPOSED property.
 100      */
 101     private static final PropertyChangeListener guiDisposedListener =
 102         new GuiDisposedListener();
 103 
 104     /*
 105      * Called (on the Toolkit thread) before the appropriate
 106      * WindowStateEvent is posted to the EventQueue.
 107      */
 108     private WindowListener windowListener;
 109     private float scaleX;
 110     private float scaleY;
 111 
 112     /**
 113      * Initialize JNI field IDs
 114      */
 115     private static native void initIDs();
 116     static {
 117         initIDs();
 118     }
 119 
 120     // WComponentPeer overrides
 121     @Override
 122     @SuppressWarnings("unchecked")
 123     protected void disposeImpl() {
 124         AppContext appContext = SunToolkit.targetToAppContext(target);
 125         synchronized (appContext) {
 126             List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY);
 127             if (l != null) {
 128                 l.remove(this);
 129             }
 130         }
 131 
 132         // Remove ourself from the Map of DisplayChangeListeners
 133         GraphicsConfiguration gc = getGraphicsConfiguration();
 134         ((Win32GraphicsDevice)gc.getDevice()).removeDisplayChangedListener(this);
 135 
 136         synchronized (getStateLock()) {
 137             TranslucentWindowPainter currentPainter = painter;
 138             if (currentPainter != null) {
 139                 currentPainter.flush();
 140                 // don't set the current one to null here; reduces the chances of
 141                 // MT issues (like NPEs)
 142             }
 143         }
 144 
 145         super.disposeImpl();
 146     }
 147 
 148     // WindowPeer implementation
 149 
 150     @Override
 151     public void toFront() {
 152         updateFocusableWindowState();
 153         _toFront();
 154     }
 155     private native void _toFront();
 156 
 157     @Override
 158     public native void toBack();
 159 
 160     private native void setAlwaysOnTopNative(boolean value);
 161 
 162     public void setAlwaysOnTop(boolean value) {
 163         if ((value && ((Window)target).isVisible()) || !value) {
 164             setAlwaysOnTopNative(value);
 165         }
 166     }
 167 
 168     @Override
 169     public void updateAlwaysOnTopState() {
 170         setAlwaysOnTop(((Window)target).isAlwaysOnTop());
 171     }
 172 
 173     @Override
 174     public void updateFocusableWindowState() {
 175         setFocusableWindow(((Window)target).isFocusableWindow());
 176     }
 177     native void setFocusableWindow(boolean value);
 178 
 179     // FramePeer & DialogPeer partial shared implementation
 180 
 181     public void setTitle(String title) {
 182         // allow a null title to pass as an empty string.
 183         if (title == null) {
 184             title = "";
 185         }
 186         _setTitle(title);
 187     }
 188     private native void _setTitle(String title);
 189 
 190     public void setResizable(boolean resizable) {
 191         _setResizable(resizable);
 192     }
 193 
 194     private native void _setResizable(boolean resizable);
 195 
 196     // Toolkit & peer internals
 197 
 198     WWindowPeer(Window target) {
 199         super(target);
 200     }
 201 
 202     @Override
 203     void initialize() {
 204         super.initialize();
 205 
 206         updateInsets(insets_);
 207 
 208         if (!((Window) target).isFontSet()) {
 209             ((Window) target).setFont(defaultFont);
 210             setFont(defaultFont);
 211         }
 212         if (!((Window) target).isForegroundSet()) {
 213             ((Window) target).setForeground(SystemColor.windowText);
 214         }
 215         if (!((Window) target).isBackgroundSet()) {
 216             ((Window) target).setBackground(SystemColor.window);
 217         }
 218 
 219         // Express our interest in display changes
 220         GraphicsConfiguration gc = getGraphicsConfiguration();
 221         Win32GraphicsDevice gd = (Win32GraphicsDevice) gc.getDevice();
 222         gd.addDisplayChangedListener(this);
 223         scaleX = gd.getDefaultScaleX();
 224         scaleY = gd.getDefaultScaleY();
 225 
 226         initActiveWindowsTracking((Window)target);
 227 
 228         updateIconImages();
 229 
 230         Shape shape = ((Window)target).getShape();
 231         if (shape != null) {
 232             applyShape(Region.getInstance(shape, null));
 233         }
 234 
 235         float opacity = ((Window)target).getOpacity();
 236         if (opacity < 1.0f) {
 237             setOpacity(opacity);
 238         }
 239 
 240         synchronized (getStateLock()) {
 241             // default value of a boolean field is 'false', so set isOpaque to
 242             // true here explicitly
 243             this.isOpaque = true;
 244             setOpaque(((Window)target).isOpaque());
 245         }
 246     }
 247 
 248     native void createAwtWindow(WComponentPeer parent);
 249 
 250     private volatile Window.Type windowType = Window.Type.NORMAL;
 251 
 252     // This method must be called for Window, Dialog, and Frame before creating
 253     // the hwnd
 254     void preCreate(WComponentPeer parent) {
 255         windowType = ((Window)target).getType();
 256     }
 257 
 258     @Override
 259     void create(WComponentPeer parent) {
 260         preCreate(parent);
 261         createAwtWindow(parent);
 262     }
 263 
 264     @Override
 265     final WComponentPeer getNativeParent() {
 266         final Container owner = ((Window) target).getOwner();
 267         return (WComponentPeer) WToolkit.targetToPeer(owner);
 268     }
 269 
 270     // should be overriden in WDialogPeer
 271     protected void realShow() {
 272         super.show();
 273     }
 274 
 275     @Override
 276     public void show() {
 277         updateFocusableWindowState();
 278 
 279         boolean alwaysOnTop = ((Window)target).isAlwaysOnTop();
 280 
 281         // Fix for 4868278.
 282         // If we create a window with a specific GraphicsConfig, and then move it with
 283         // setLocation() or setBounds() to another one before its peer has been created,
 284         // then calling Window.getGraphicsConfig() returns wrong config. That may lead
 285         // to some problems like wrong-placed tooltips. It is caused by calling
 286         // super.displayChanged() in WWindowPeer.displayChanged() regardless of whether
 287         // GraphicsDevice was really changed, or not. So we need to track it here.
 288         updateGC();
 289 
 290         realShow();
 291         updateMinimumSize();
 292 
 293         if (((Window)target).isAlwaysOnTopSupported() && alwaysOnTop) {
 294             setAlwaysOnTop(alwaysOnTop);
 295         }
 296 
 297         synchronized (getStateLock()) {
 298             if (!isOpaque) {
 299                 updateWindow(true);
 300             }
 301         }
 302 
 303         // See https://javafx-jira.kenai.com/browse/RT-32570
 304         WComponentPeer owner = getNativeParent();
 305         if (owner != null && owner.isLightweightFramePeer()) {
 306             Rectangle b = getBounds();
 307             handleExpose(0, 0, b.width, b.height);
 308         }
 309     }
 310 





 311     // Synchronize the insets members (here & in helper) with actual window
 312     // state.
 313     native void updateInsets(Insets i);
 314 
 315     static native int getSysMinWidth();
 316     static native int getSysMinHeight();
 317     static native int getSysIconWidth();
 318     static native int getSysIconHeight();
 319     static native int getSysSmIconWidth();
 320     static native int getSysSmIconHeight();
 321     /**windows/classes/sun/awt/windows/
 322      * Creates native icon from specified raster data and updates
 323      * icon for window and all descendant windows that inherit icon.
 324      * Raster data should be passed in the ARGB form.
 325      * Note that raster data format was changed to provide support
 326      * for XP icons with alpha-channel
 327      */
 328     native void setIconImagesData(int[] iconRaster, int w, int h,
 329                                   int[] smallIconRaster, int smw, int smh);
 330 
 331     synchronized native void reshapeFrame(int x, int y, int width, int height);
 332 
 333     native Dimension getNativeWindowSize();
 334 
 335     public Dimension getScaledWindowSize() {
 336         return getNativeWindowSize();
 337     }
 338 
 339     public boolean requestWindowFocus(FocusEvent.Cause cause) {
 340         if (!focusAllowedFor()) {
 341             return false;
 342         }
 343         return requestWindowFocus(cause == FocusEvent.Cause.MOUSE_EVENT);
 344     }
 345     private native boolean requestWindowFocus(boolean isMouseEventCause);
 346 
 347     public boolean focusAllowedFor() {
 348         Window window = (Window)this.target;
 349         if (!window.isVisible() ||
 350             !window.isEnabled() ||
 351             !window.isFocusableWindow())
 352         {
 353             return false;
 354         }
 355         if (isModalBlocked()) {
 356             return false;
 357         }
 358         return true;
 359     }
 360 
 361     @Override
 362     void hide() {
 363         WindowListener listener = windowListener;
 364         if (listener != null) {
 365             // We're not getting WINDOW_CLOSING from the native code when hiding
 366             // the window programmatically. So, create it and notify the listener.
 367             listener.windowClosing(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING));
 368         }
 369         super.hide();
 370     }
 371 
 372     // WARNING: it's called on the Toolkit thread!
 373     @Override
 374     void preprocessPostEvent(AWTEvent event) {
 375         if (event instanceof WindowEvent) {
 376             WindowListener listener = windowListener;
 377             if (listener != null) {
 378                 switch(event.getID()) {
 379                     case WindowEvent.WINDOW_CLOSING:
 380                         listener.windowClosing((WindowEvent)event);
 381                         break;
 382                     case WindowEvent.WINDOW_ICONIFIED:
 383                         listener.windowIconified((WindowEvent)event);
 384                         break;
 385                 }
 386             }
 387         }
 388     }
 389 
 390     synchronized void addWindowListener(WindowListener l) {
 391         windowListener = AWTEventMulticaster.add(windowListener, l);
 392     }
 393 
 394     synchronized void removeWindowListener(WindowListener l) {
 395         windowListener = AWTEventMulticaster.remove(windowListener, l);
 396     }
 397 
 398     @Override
 399     public void updateMinimumSize() {
 400         Dimension minimumSize = null;
 401         if (((Component)target).isMinimumSizeSet()) {
 402             minimumSize = ((Component)target).getMinimumSize();
 403         }
 404         if (minimumSize != null) {
 405             int w = Math.max(minimumSize.width, scaleDownX(getSysMinWidth()));
 406             int h = Math.max(minimumSize.height, scaleDownY(getSysMinHeight()));
 407             setMinSize(w, h);

 408         } else {
 409             setMinSize(0, 0);
 410         }
 411     }
 412 
 413     @Override
 414     public void updateIconImages() {
 415         java.util.List<Image> imageList = ((Window)target).getIconImages();
 416         if (imageList == null || imageList.size() == 0) {
 417             setIconImagesData(null, 0, 0, null, 0, 0);
 418         } else {
 419             int w = getSysIconWidth();
 420             int h = getSysIconHeight();
 421             int smw = getSysSmIconWidth();
 422             int smh = getSysSmIconHeight();
 423             AffineTransform tx = getGraphicsConfiguration().getDefaultTransform();
 424             w = Region.clipScale(w, tx.getScaleX());
 425             h = Region.clipScale(h, tx.getScaleY());
 426             smw = Region.clipScale(smw, tx.getScaleX());
 427             smh = Region.clipScale(smh, tx.getScaleY());
 428             DataBufferInt iconData = SunToolkit.getScaledIconData(imageList,
 429                                                                   w, h);
 430             DataBufferInt iconSmData = SunToolkit.getScaledIconData(imageList,
 431                                                                     smw, smh);
 432             if (iconData != null && iconSmData != null) {
 433                 setIconImagesData(iconData.getData(), w, h,
 434                                   iconSmData.getData(), smw, smh);
 435             } else {
 436                 setIconImagesData(null, 0, 0, null, 0, 0);
 437             }
 438         }
 439     }
 440 
 441     native void setMinSize(int width, int height);
 442 
 443 /*
 444  * ---- MODALITY SUPPORT ----
 445  */
 446 
 447     /**
 448      * Some modality-related code here because WFileDialogPeer, WPrintDialogPeer and
 449      *   WPageDialogPeer are descendants of WWindowPeer, not WDialogPeer
 450      */
 451 
 452     public boolean isModalBlocked() {
 453         return modalBlocker != null;
 454     }
 455 
 456      @Override
 457     public void setModalBlocked(Dialog dialog, boolean blocked) {
 458         synchronized (((Component)getTarget()).getTreeLock()) // State lock should always be after awtLock
 459         {
 460             // use WWindowPeer instead of WDialogPeer because of FileDialogs and PrintDialogs
 461             WWindowPeer blockerPeer = AWTAccessor.getComponentAccessor()
 462                                                  .getPeer(dialog);
 463             if (blocked)
 464             {
 465                 modalBlocker = blockerPeer;
 466                 // handle native dialogs separately, as they may have not
 467                 // got HWND yet; modalEnable/modalDisable is called from
 468                 // their setHWnd() methods
 469                 if (blockerPeer instanceof WFileDialogPeer) {
 470                     ((WFileDialogPeer)blockerPeer).blockWindow(this);
 471                 } else if (blockerPeer instanceof WPrintDialogPeer) {
 472                     ((WPrintDialogPeer)blockerPeer).blockWindow(this);
 473                 } else {
 474                     modalDisable(dialog, blockerPeer.getHWnd());
 475                 }
 476             } else {
 477                 modalBlocker = null;
 478                 if (blockerPeer instanceof WFileDialogPeer) {
 479                     ((WFileDialogPeer)blockerPeer).unblockWindow(this);
 480                 } else if (blockerPeer instanceof WPrintDialogPeer) {
 481                     ((WPrintDialogPeer)blockerPeer).unblockWindow(this);
 482                 } else {
 483                     modalEnable(dialog);
 484                 }
 485             }
 486         }
 487     }
 488 
 489     native void modalDisable(Dialog blocker, long blockerHWnd);
 490     native void modalEnable(Dialog blocker);
 491 
 492     /*
 493      * Returns all the ever active windows from the current AppContext.
 494      * The list is sorted by the time of activation, so the latest
 495      * active window is always at the end.
 496      */
 497     @SuppressWarnings("unchecked")
 498     public static long[] getActiveWindowHandles(Component target) {
 499         AppContext appContext = SunToolkit.targetToAppContext(target);
 500         if (appContext == null) return null;
 501         synchronized (appContext) {
 502             List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY);
 503             if (l == null) {
 504                 return null;
 505             }
 506             long[] result = new long[l.size()];
 507             for (int j = 0; j < l.size(); j++) {
 508                 result[j] = l.get(j).getHWnd();
 509             }
 510             return result;
 511         }
 512     }
 513 
 514 /*
 515  * ----DISPLAY CHANGE SUPPORT----
 516  */
 517 
 518     /*
 519      * Called from native code when we have been dragged onto another screen.
 520      */
 521     void draggedToNewScreen() {
 522         displayChanged();
 523     }
 524 
 525     public void updateGC() {
 526         int scrn = getScreenImOn();
 527         if (screenLog.isLoggable(PlatformLogger.Level.FINER)) {
 528             log.finer("Screen number: " + scrn);
 529         }
 530 
 531         // get current GD
 532         Win32GraphicsDevice oldDev = winGraphicsConfig.getDevice();
 533 
 534         Win32GraphicsDevice newDev;
 535         GraphicsDevice[] devs = GraphicsEnvironment
 536             .getLocalGraphicsEnvironment()
 537             .getScreenDevices();
 538         // Occasionally during device addition/removal getScreenImOn can return
 539         // a non-existing screen number. Use the default device in this case.
 540         if (scrn >= devs.length) {
 541             newDev = (Win32GraphicsDevice)GraphicsEnvironment
 542                 .getLocalGraphicsEnvironment().getDefaultScreenDevice();
 543         } else {
 544             newDev = (Win32GraphicsDevice)devs[scrn];
 545         }
 546 
 547         // Set winGraphicsConfig to the default GC for the monitor this Window
 548         // is now mostly on.
 549         winGraphicsConfig = (Win32GraphicsConfig)newDev
 550                             .getDefaultConfiguration();
 551         if (screenLog.isLoggable(PlatformLogger.Level.FINE)) {
 552             if (winGraphicsConfig == null) {
 553                 screenLog.fine("Assertion (winGraphicsConfig != null) failed");
 554             }
 555         }
 556 
 557         // if on a different display, take off old GD and put on new GD
 558         if (oldDev != newDev) {
 559             oldDev.removeDisplayChangedListener(this);
 560             newDev.addDisplayChangedListener(this);
 561         }
 562 
 563         AWTAccessor.getComponentAccessor().
 564             setGraphicsConfiguration((Component)target, winGraphicsConfig);
 565 
 566         checkDPIChange(oldDev, newDev);
 567     }
 568 
 569     private void checkDPIChange(Win32GraphicsDevice oldDev,
 570                                 Win32GraphicsDevice newDev) {
 571         float newScaleX = newDev.getDefaultScaleX();
 572         float newScaleY = newDev.getDefaultScaleY();
 573 
 574         if (scaleX != newScaleX || scaleY != newScaleY) {
 575             windowDPIChange(oldDev.getScreen(), scaleX, scaleY,
 576                             newDev.getScreen(), newScaleX, newScaleY);
 577             scaleX = newScaleX;
 578             scaleY = newScaleY;
 579         }
 580     }
 581 
 582     /**
 583      * From the DisplayChangedListener interface.
 584      *
 585      * This method handles a display change - either when the display settings
 586      * are changed, or when the window has been dragged onto a different
 587      * display.
 588      * Called after a change in the display mode.  This event
 589      * triggers replacing the surfaceData object (since that object
 590      * reflects the current display depth information, which has
 591      * just changed).
 592      */
 593     @Override
 594     public void displayChanged() {
 595         SunToolkit.executeOnEventHandlerThread(target, this::updateGC);
 596     }
 597 
 598     /**
 599      * Part of the DisplayChangedListener interface: components
 600      * do not need to react to this event
 601      */
 602     @Override
 603     public void paletteChanged() {
 604     }
 605 
 606     private native int getScreenImOn();
 607 
 608     // Used in Win32GraphicsDevice.
 609     public final native void setFullScreenExclusiveModeState(boolean state);
 610 
 611 /*
 612  * ----END DISPLAY CHANGE SUPPORT----
 613  */
 614 
 615      public void grab() {
 616          nativeGrab();
 617      }
 618 
 619      public void ungrab() {
 620          nativeUngrab();
 621      }
 622      private native void nativeGrab();
 623      private native void nativeUngrab();
 624 
 625      private boolean hasWarningWindow() {
 626          return ((Window)target).getWarningString() != null;
 627      }
 628 
 629      boolean isTargetUndecorated() {
 630          return true;
 631      }
 632 
 633      // These are the peer bounds. They get updated at:
 634      //    1. the WWindowPeer.setBounds() method.
 635      //    2. the native code (on WM_SIZE/WM_MOVE)
 636      private volatile int sysX = 0;
 637      private volatile int sysY = 0;
 638      private volatile int sysW = 0;
 639      private volatile int sysH = 0;
 640 
 641      @Override
 642      public native void repositionSecurityWarning();
 643 
 644      @Override
 645      public void setBounds(int x, int y, int width, int height, int op) {
 646          sysX = x;
 647          sysY = y;
 648          sysW = width;
 649          sysH = height;
 650 
 651          int cx = x + width / 2;
 652          int cy = y + height / 2;
 653          GraphicsConfiguration current = getGraphicsConfiguration();
 654          GraphicsConfiguration other = SunGraphicsEnvironment
 655                  .getGraphicsConfigurationAtPoint(current, cx, cy);
 656          if (!current.equals(other)) {
 657              AffineTransform tx = other.getDefaultTransform();
 658              double otherScaleX = tx.getScaleX();
 659              double otherScaleY = tx.getScaleY();
 660              initScales();
 661              if (scaleX != otherScaleX || scaleY != otherScaleY) {
 662                  x = (int) Math.floor(x * otherScaleX / scaleX);
 663                  y = (int) Math.floor(y * otherScaleY / scaleY);
 664              }
 665          }
 666 
 667          super.setBounds(x, y, width, height, op);
 668      }
 669 
 670     private void initScales() {
 671 
 672         if (scaleX >= 1 && scaleY >= 1) {
 673             return;
 674         }
 675 
 676         GraphicsConfiguration gc = getGraphicsConfiguration();
 677         if (gc instanceof Win32GraphicsConfig) {
 678             Win32GraphicsDevice gd = ((Win32GraphicsConfig) gc).getDevice();
 679             scaleX = gd.getDefaultScaleX();
 680             scaleY = gd.getDefaultScaleY();
 681         } else {
 682             AffineTransform tx = gc.getDefaultTransform();
 683             scaleX = (float) tx.getScaleX();
 684             scaleY = (float) tx.getScaleY();
 685         }
 686     }
 687 
 688     final int scaleUpX(int x) {
 689         return Region.clipRound(x * scaleX);
 690     }
 691 
 692     final int scaleUpY(int y) {
 693         return Region.clipRound(y * scaleY);
 694     }
 695 
 696     final int scaleDownX(int x) {
 697         return Region.clipRound(x / scaleX);
 698     }
 699 
 700     final int scaleDownY(int y) {
 701         return Region.clipRound(y / scaleY);
 702     }
 703 
 704     @Override
 705     public void print(Graphics g) {
 706         // We assume we print the whole frame,
 707         // so we expect no clip was set previously
 708         Shape shape = ((Window)target).getShape();
 709         if (shape != null) {
 710             g.setClip(shape);
 711         }
 712         super.print(g);
 713     }
 714 
 715     private void replaceSurfaceDataRecursively(Component c) {
 716         if (c instanceof Container) {
 717             for (Component child : ((Container)c).getComponents()) {
 718                 replaceSurfaceDataRecursively(child);
 719             }
 720         }
 721         final Object cp = AWTAccessor.getComponentAccessor().getPeer(c);
 722         if (cp instanceof WComponentPeer) {
 723             ((WComponentPeer)cp).replaceSurfaceDataLater();
 724         }
 725     }
 726 
 727     public final Graphics getTranslucentGraphics() {
 728         synchronized (getStateLock()) {
 729             return isOpaque ? null : painter.getGraphics(false);
 730         }
 731     }
 732 
 733     @Override
 734     public void setBackground(Color c) {
 735         super.setBackground(c);
 736         synchronized (getStateLock()) {
 737             if (!isOpaque && ((Window)target).isVisible()) {
 738                 updateWindow(true);
 739             }
 740         }
 741     }
 742 
 743     private native void setOpacity(int iOpacity);
 744     private float opacity = 1.0f;
 745 
 746     @Override
 747     public void setOpacity(float opacity) {
 748         if (!((SunToolkit)((Window)target).getToolkit()).
 749             isWindowOpacitySupported())
 750         {
 751             return;
 752         }
 753 
 754         if (opacity < 0.0f || opacity > 1.0f) {
 755             throw new IllegalArgumentException(
 756                 "The value of opacity should be in the range [0.0f .. 1.0f].");
 757         }
 758 
 759         if (((this.opacity == 1.0f && opacity <  1.0f) ||
 760              (this.opacity <  1.0f && opacity == 1.0f)) &&
 761             !Win32GraphicsEnvironment.isVistaOS())
 762         {
 763             // non-Vista OS: only replace the surface data if opacity status
 764             // changed (see WComponentPeer.isAccelCapable() for more)
 765             replaceSurfaceDataRecursively((Component)getTarget());
 766         }
 767 
 768         this.opacity = opacity;
 769 
 770         final int maxOpacity = 0xff;
 771         int iOpacity = (int)(opacity * maxOpacity);
 772         if (iOpacity < 0) {
 773             iOpacity = 0;
 774         }
 775         if (iOpacity > maxOpacity) {
 776             iOpacity = maxOpacity;
 777         }
 778 
 779         setOpacity(iOpacity);
 780 
 781         synchronized (getStateLock()) {
 782             if (!isOpaque && ((Window)target).isVisible()) {
 783                 updateWindow(true);
 784             }
 785         }
 786     }
 787 
 788     private native void setOpaqueImpl(boolean isOpaque);
 789 
 790     @Override
 791     public void setOpaque(boolean isOpaque) {
 792         synchronized (getStateLock()) {
 793             if (this.isOpaque == isOpaque) {
 794                 return;
 795             }
 796         }
 797 
 798         Window target = (Window)getTarget();
 799 
 800         if (!isOpaque) {
 801             SunToolkit sunToolkit = (SunToolkit)target.getToolkit();
 802             if (!sunToolkit.isWindowTranslucencySupported() ||
 803                 !sunToolkit.isTranslucencyCapable(target.getGraphicsConfiguration()))
 804             {
 805                 return;
 806             }
 807         }
 808 
 809         boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS();
 810 
 811         if (this.isOpaque != isOpaque && !isVistaOS) {
 812             // non-Vista OS: only replace the surface data if the opacity
 813             // status changed (see WComponentPeer.isAccelCapable() for more)
 814             replaceSurfaceDataRecursively(target);
 815         }
 816 
 817         synchronized (getStateLock()) {
 818             this.isOpaque = isOpaque;
 819             setOpaqueImpl(isOpaque);
 820             if (isOpaque) {
 821                 TranslucentWindowPainter currentPainter = painter;
 822                 if (currentPainter != null) {
 823                     currentPainter.flush();
 824                     painter = null;
 825                 }
 826             } else {
 827                 painter = TranslucentWindowPainter.createInstance(this);
 828             }
 829         }
 830 
 831         if (isVistaOS) {
 832             // On Vista: setting the window non-opaque makes the window look
 833             // rectangular, though still catching the mouse clicks within
 834             // its shape only. To restore the correct visual appearance
 835             // of the window (i.e. w/ the correct shape) we have to reset
 836             // the shape.
 837             Shape shape = target.getShape();
 838             if (shape != null) {
 839                 target.setShape(shape);
 840             }
 841         }
 842 
 843         if (target.isVisible()) {
 844             updateWindow(true);
 845         }
 846     }
 847 
 848     native void updateWindowImpl(int[] data, int width, int height);
 849 
 850     @Override
 851     public void updateWindow() {
 852         updateWindow(false);
 853     }
 854 
 855     private void updateWindow(boolean repaint) {
 856         Window w = (Window)target;
 857         synchronized (getStateLock()) {
 858             if (isOpaque || !w.isVisible() ||
 859                 (w.getWidth() <= 0) || (w.getHeight() <= 0))
 860             {
 861                 return;
 862             }
 863             TranslucentWindowPainter currentPainter = painter;
 864             if (currentPainter != null) {
 865                 currentPainter.updateWindow(repaint);
 866             } else if (log.isLoggable(PlatformLogger.Level.FINER)) {
 867                 log.finer("Translucent window painter is null in updateWindow");
 868             }
 869         }
 870     }
 871 
 872     native void windowDPIChange(int prevScreen, float prevScaleX, float prevScaleY,
 873                                 int newScreen, float newScaleX, float newScaleY);
 874 
 875     /*
 876      * The method maps the list of the active windows to the window's AppContext,
 877      * then the method registers ActiveWindowListener, GuiDisposedListener listeners;
 878      * it executes the initilialization only once per AppContext.
 879      */
 880     @SuppressWarnings("unchecked")
 881     private static void initActiveWindowsTracking(Window w) {
 882         AppContext appContext = AppContext.getAppContext();
 883         synchronized (appContext) {
 884             List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY);
 885             if (l == null) {
 886                 l = new LinkedList<WWindowPeer>();
 887                 appContext.put(ACTIVE_WINDOWS_KEY, l);
 888                 appContext.addPropertyChangeListener(AppContext.GUI_DISPOSED, guiDisposedListener);
 889 
 890                 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
 891                 kfm.addPropertyChangeListener("activeWindow", activeWindowListener);
 892             }
 893         }
 894     }
 895 
 896     /*
 897      * The GuiDisposedListener class listens for the AppContext.GUI_DISPOSED property,
 898      * it removes the list of the active windows from the disposed AppContext and
 899      * unregisters ActiveWindowListener listener.
 900      */
 901     private static class GuiDisposedListener implements PropertyChangeListener {
 902         @Override
 903         public void propertyChange(PropertyChangeEvent e) {
 904             boolean isDisposed = (Boolean)e.getNewValue();
 905             if (isDisposed != true) {
 906                 if (log.isLoggable(PlatformLogger.Level.FINE)) {
 907                     log.fine(" Assertion (newValue != true) failed for AppContext.GUI_DISPOSED ");
 908                 }
 909             }
 910             AppContext appContext = AppContext.getAppContext();
 911             synchronized (appContext) {
 912                 appContext.remove(ACTIVE_WINDOWS_KEY);
 913                 appContext.removePropertyChangeListener(AppContext.GUI_DISPOSED, this);
 914 
 915                 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
 916                 kfm.removePropertyChangeListener("activeWindow", activeWindowListener);
 917             }
 918         }
 919     }
 920 
 921     /*
 922      * Static inner class, listens for 'activeWindow' KFM property changes and
 923      * updates the list of active windows per AppContext, so the latest active
 924      * window is always at the end of the list. The list is stored in AppContext.
 925      */
 926     @SuppressWarnings("unchecked")
 927     private static class ActiveWindowListener implements PropertyChangeListener {
 928         @Override
 929         public void propertyChange(PropertyChangeEvent e) {
 930             Window w = (Window)e.getNewValue();
 931             if (w == null) {
 932                 return;
 933             }
 934             AppContext appContext = SunToolkit.targetToAppContext(w);
 935             synchronized (appContext) {
 936                 WWindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w);
 937                 // add/move wp to the end of the list
 938                 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY);
 939                 if (l != null) {
 940                     l.remove(wp);
 941                     l.add(wp);
 942                 }
 943             }
 944         }
 945     }
 946 }
--- EOF ---