1 /*
   2  * Copyright (c) 2008, 2015, 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 com.sun.javafx.tk.quantum;
  27 
  28 import java.nio.ByteBuffer;
  29 import java.security.AccessController;
  30 import java.security.AllPermission;
  31 import java.security.Permission;
  32 import java.security.PrivilegedAction;
  33 import java.security.AccessControlContext;
  34 import java.util.HashMap;
  35 import java.util.LinkedList;
  36 import java.util.List;
  37 import java.util.Map;
  38 
  39 import javafx.scene.input.KeyCombination;
  40 import javafx.stage.Modality;
  41 import javafx.stage.Stage;
  42 import javafx.stage.StageStyle;
  43 
  44 import com.sun.glass.events.WindowEvent;
  45 import com.sun.glass.ui.*;
  46 import com.sun.glass.ui.Window.Level;
  47 import com.sun.javafx.PlatformUtil;
  48 import com.sun.javafx.iio.common.PushbroomScaler;
  49 import com.sun.javafx.iio.common.ScalerFactory;
  50 import com.sun.javafx.tk.FocusCause;
  51 import com.sun.javafx.tk.TKScene;
  52 import com.sun.javafx.tk.TKStage;
  53 import com.sun.prism.Image;
  54 import com.sun.prism.PixelFormat;
  55 import java.util.Locale;
  56 import java.util.ResourceBundle;
  57 
  58 class WindowStage extends GlassStage {
  59 
  60     protected Window platformWindow;
  61 
  62     protected javafx.stage.Stage fxStage;
  63 
  64     private StageStyle style;
  65     private GlassStage owner = null;
  66     private Modality modality = Modality.NONE;
  67     private final boolean securityDialog;
  68 
  69     private OverlayWarning warning = null;
  70     private boolean rtl = false;
  71     private boolean transparent = false;
  72     private boolean isPrimaryStage = false;
  73     private boolean isAppletStage = false; // true if this is an embedded applet window
  74     private boolean isPopupStage = false;
  75     private boolean isInFullScreen = false;
  76 
  77     // A flag to indicate whether a call was generated from
  78     // an input event handler.
  79     private boolean inEventHandler = false;
  80 
  81     // An active window is visible && enabled && focusable.
  82     // The list is maintained in the z-order, so that the last element
  83     // represents the topmost window (or more accurately, the last
  84     // focused window, which we assume is very close to the last topmost one).
  85     private static List<WindowStage> activeWindows = new LinkedList<>();
  86 
  87     private static Map<Window, WindowStage> platformWindows = new HashMap<>();
  88 
  89     private static GlassAppletWindow appletWindow = null;
  90     static void setAppletWindow(GlassAppletWindow aw) {
  91         appletWindow = aw;
  92     }
  93     static GlassAppletWindow getAppletWindow() {
  94         return appletWindow;
  95     }
  96 
  97     private static final Locale LOCALE = Locale.getDefault();
  98 
  99     private static final ResourceBundle RESOURCES =
 100         ResourceBundle.getBundle(WindowStage.class.getPackage().getName() +
 101                                  ".QuantumMessagesBundle", LOCALE);
 102 
 103 
 104     public WindowStage(javafx.stage.Window peerWindow, boolean securityDialog, final StageStyle stageStyle, Modality modality, TKStage owner) {
 105         this.style = stageStyle;
 106         this.owner = (GlassStage)owner;
 107         this.modality = modality;
 108         this.securityDialog = securityDialog;
 109 
 110         if (peerWindow instanceof javafx.stage.Stage) {
 111             fxStage = (Stage)peerWindow;
 112         } else {
 113             fxStage = null;
 114         }
 115 
 116         transparent = stageStyle == StageStyle.TRANSPARENT;
 117         if (owner == null) {
 118             if (this.modality == Modality.WINDOW_MODAL) {
 119                 this.modality = Modality.NONE;
 120             }
 121         }
 122     }
 123 
 124     final void setIsPrimary() {
 125         isPrimaryStage = true;
 126         if (appletWindow != null) {
 127             // this is an embedded applet stage
 128             isAppletStage = true;
 129         }
 130     }
 131 
 132     final void setIsPopup() {
 133         isPopupStage = true;
 134     }
 135 
 136     final boolean isSecurityDialog() {
 137         return securityDialog;
 138     }
 139 
 140     // Called by QuantumToolkit, so we can override initPlatformWindow in subclasses
 141     public final WindowStage init(GlassSystemMenu sysmenu) {
 142         initPlatformWindow();
 143         platformWindow.setEventHandler(new GlassWindowEventHandler(this));
 144         if (sysmenu.isSupported()) {
 145             sysmenu.createMenuBar();
 146             platformWindow.setMenuBar(sysmenu.getMenuBar());
 147         }
 148         return this;
 149     }
 150 
 151     private void initPlatformWindow() {
 152         if (platformWindow == null) {
 153             Application app = Application.GetApplication();
 154             if (isPrimaryStage && (null != appletWindow)) {
 155                 platformWindow = app.createWindow(appletWindow.getGlassWindow().getNativeWindow());
 156             } else {
 157                 Window ownerWindow = null;
 158                 if (owner instanceof WindowStage) {
 159                     ownerWindow = ((WindowStage)owner).platformWindow;
 160                 }
 161                 boolean resizable = false;
 162                 boolean focusable = true;
 163                 int windowMask = rtl ? Window.RIGHT_TO_LEFT : 0;
 164                 if (isPopupStage) { // TODO: make it a stage style?
 165                     windowMask |= Window.POPUP;
 166                     if (style == StageStyle.TRANSPARENT) {
 167                         windowMask |= Window.TRANSPARENT;
 168                     }
 169                     focusable = false;
 170                 } else {
 171                     switch (style) {
 172                         case UNIFIED:
 173                             if (app.supportsUnifiedWindows()) {
 174                                 windowMask |= Window.UNIFIED;
 175                             }
 176                             // fall through
 177                         case DECORATED:
 178                             windowMask |=
 179                                 Window.TITLED | Window.CLOSABLE |
 180                                 Window.MINIMIZABLE | Window.MAXIMIZABLE;
 181                             if (ownerWindow != null || modality != Modality.NONE) {
 182                                 windowMask &=
 183                                     ~(Window.MINIMIZABLE | Window.MAXIMIZABLE);
 184                             }
 185                             resizable = true;
 186                             break;
 187                         case UTILITY:
 188                             windowMask |=  Window.TITLED | Window.UTILITY | Window.CLOSABLE;
 189                             break;
 190                         default:
 191                             windowMask |=
 192                                     (transparent ? Window.TRANSPARENT : Window.UNTITLED) | Window.CLOSABLE;
 193                             break;
 194                     }
 195                 }
 196                 platformWindow =
 197                         app.createWindow(ownerWindow, Screen.getMainScreen(), windowMask);
 198                 platformWindow.setResizable(resizable);
 199                 platformWindow.setFocusable(focusable);
 200                 if (securityDialog) {
 201                     platformWindow.setLevel(Window.Level.FLOATING);
 202                 }
 203             }
 204         }
 205         platformWindows.put(platformWindow, this);
 206     }
 207 
 208     final Window getPlatformWindow() {
 209         return platformWindow;
 210     }
 211 
 212     static WindowStage findWindowStage(Window platformWindow) {
 213         return platformWindows.get(platformWindow);
 214     }
 215 
 216     protected GlassStage getOwner() {
 217         return owner;
 218     }
 219 
 220     protected ViewScene getViewScene() {
 221         return (ViewScene)getScene();
 222     }
 223 
 224     StageStyle getStyle() {
 225         return style;
 226     }
 227 
 228     @Override public TKScene createTKScene(boolean depthBuffer, boolean msaa, AccessControlContext acc) {
 229         ViewScene scene = new ViewScene(depthBuffer, msaa);
 230         scene.setSecurityContext(acc);
 231         return scene;
 232     }
 233 
 234     /**
 235      * Set the scene to be displayed in this stage
 236      *
 237      * @param scene The peer of the scene to be displayed
 238      */
 239     @Override public void setScene(TKScene scene) {
 240         GlassScene oldScene = getScene();
 241         if (oldScene == scene) {
 242             // Nothing to do
 243             return;
 244         }
 245         // RT-21465, RT-28490
 246         // We don't support scene changes in full-screen mode.
 247         exitFullScreen();
 248         super.setScene(scene);
 249         if (scene != null) {
 250             GlassScene newScene = getViewScene();
 251             View view = newScene.getPlatformView();
 252             QuantumToolkit.runWithRenderLock(() -> {
 253                 platformWindow.setView(view);
 254                 if (oldScene != null) oldScene.updateSceneState();
 255                 newScene.updateSceneState();
 256                 return null;
 257             });
 258             requestFocus();
 259         } else {
 260             QuantumToolkit.runWithRenderLock(() -> {
 261                 // platformWindow can be null here, if this window is owned,
 262                 // and its owner is being closed.
 263                 if (platformWindow != null) {
 264                     platformWindow.setView(null);
 265                 }
 266                 if (oldScene != null) {
 267                     oldScene.updateSceneState();
 268                 }
 269                 return null;
 270             });
 271         }
 272         if (oldScene != null) {
 273             ViewPainter painter = ((ViewScene)oldScene).getPainter();
 274             QuantumRenderer.getInstance().disposePresentable(painter.presentable);   // latched on RT
 275         }
 276     }
 277 
 278     @Override public void setBounds(float x, float y, boolean xSet, boolean ySet,
 279                                     float w, float h, float cw, float ch,
 280                                     float xGravity, float yGravity)
 281     {
 282         if (isAppletStage) {
 283             xSet = ySet = false;
 284         }
 285         float pScale = platformWindow.getPlatformScale();
 286         int px, py;
 287         if (xSet || ySet) {
 288             Screen screen = platformWindow.getScreen();
 289             List<Screen> screens = Screen.getScreens();
 290             if (screens.size() > 1) {
 291                 float wx = xSet ? x : platformWindow.getX();
 292                 float wy = ySet ? y : platformWindow.getY();
 293                 float relx = screen.getX() + screen.getWidth() / 2.0f - wx;
 294                 float rely = screen.getY() + screen.getHeight()/ 2.0f - wy;
 295                 float distsq = relx * relx + rely * rely;
 296                 for (Screen s : Screen.getScreens()) {
 297                     relx = s.getX() + s.getWidth() / 2.0f - wx;
 298                     rely = s.getY() + s.getHeight()/ 2.0f - wy;
 299                     float distsq2 = relx * relx + rely * rely;
 300                     if (distsq2 < distsq) {
 301                         screen = s;
 302                         distsq = distsq2;
 303                     }
 304                 }
 305             }
 306             float sx = screen == null ? 0 : screen.getX();
 307             float sy = screen == null ? 0 : screen.getY();
 308             px = xSet ? Math.round(sx + (x - sx) * pScale) : 0;
 309             py = ySet ? Math.round(sy + (y - sy) * pScale) : 0;
 310         } else {
 311             px = py = 0;
 312         }
 313         int pw = (int) (w > 0 ? Math.ceil(w * pScale) : w);
 314         int ph = (int) (h > 0 ? Math.ceil(h * pScale) : h);
 315         int pcw = (int) (cw > 0 ? Math.ceil(cw * pScale) : cw);
 316         int pch = (int) (ch > 0 ? Math.ceil(ch * pScale) : ch);
 317         platformWindow.setBounds(px, py, xSet, ySet,
 318                                  pw, ph, pcw, pch,
 319                                  xGravity, yGravity);
 320     }
 321 
 322     @Override
 323     public float getUIScale() {
 324         return platformWindow.getPlatformScale();
 325     }
 326 
 327     @Override
 328     public float getRenderScale() {
 329         return platformWindow.getRenderScale();
 330     }
 331 
 332     @Override public void setMinimumSize(int minWidth, int minHeight) {
 333         float pScale = platformWindow.getPlatformScale();
 334         minWidth  = (int) Math.ceil(minWidth  * pScale);
 335         minHeight = (int) Math.ceil(minHeight * pScale);
 336         platformWindow.setMinimumSize(minWidth, minHeight);
 337     }
 338 
 339     @Override public void setMaximumSize(int maxWidth, int maxHeight) {
 340         float pScale = platformWindow.getPlatformScale();
 341         maxWidth  = (int) Math.ceil(maxWidth  * pScale);
 342         maxHeight = (int) Math.ceil(maxHeight * pScale);
 343         platformWindow.setMaximumSize(maxWidth, maxHeight);
 344     }
 345 
 346     static Image findBestImage(java.util.List icons, int width, int height) {
 347         Image image = null;
 348         double bestSimilarity = 3; //Impossibly high value
 349         for (Object icon : icons) {
 350             //Iterate imageList looking for best matching image.
 351             //'Similarity' measure is defined as good scale factor and small insets.
 352             //best possible similarity is 0 (no scale, no insets).
 353             //It's found by experimentation that good-looking results are achieved
 354             //with scale factors x1, x3/4, x2/3, xN, x1/N.
 355             //Check to make sure the image/image format is correct.
 356             Image im = (Image)icon;
 357             if (im == null || !(im.getPixelFormat() == PixelFormat.BYTE_RGB ||
 358                 im.getPixelFormat() == PixelFormat.BYTE_BGRA_PRE ||
 359                 im.getPixelFormat() == PixelFormat.BYTE_GRAY))
 360             {
 361                 continue;
 362             }
 363 
 364             int iw = im.getWidth();
 365             int ih = im.getHeight();
 366 
 367             if (iw > 0 && ih > 0) {
 368                 //Calc scale factor
 369                 double scaleFactor = Math.min((double)width / (double)iw,
 370                                               (double)height / (double)ih);
 371                 //Calculate scaled image dimensions
 372                 //adjusting scale factor to nearest "good" value
 373                 int adjw;
 374                 int adjh;
 375                 double scaleMeasure = 1; //0 - best (no) scale, 1 - impossibly bad
 376                 if (scaleFactor >= 2) {
 377                     //Need to enlarge image more than twice
 378                     //Round down scale factor to multiply by integer value
 379                     scaleFactor = Math.floor(scaleFactor);
 380                     adjw = iw * (int)scaleFactor;
 381                     adjh = ih * (int)scaleFactor;
 382                     scaleMeasure = 1.0 - 0.5 / scaleFactor;
 383                 } else if (scaleFactor >= 1) {
 384                     //Don't scale
 385                     scaleFactor = 1.0;
 386                     adjw = iw;
 387                     adjh = ih;
 388                     scaleMeasure = 0;
 389                 } else if (scaleFactor >= 0.75) {
 390                     //Multiply by 3/4
 391                     scaleFactor = 0.75;
 392                     adjw = iw * 3 / 4;
 393                     adjh = ih * 3 / 4;
 394                     scaleMeasure = 0.3;
 395                 } else if (scaleFactor >= 0.6666) {
 396                     //Multiply by 2/3
 397                     scaleFactor = 0.6666;
 398                     adjw = iw * 2 / 3;
 399                     adjh = ih * 2 / 3;
 400                     scaleMeasure = 0.33;
 401                 } else {
 402                     //Multiply size by 1/scaleDivider
 403                     //where scaleDivider is minimum possible integer
 404                     //larger than 1/scaleFactor
 405                     double scaleDivider = Math.ceil(1.0 / scaleFactor);
 406                     scaleFactor = 1.0 / scaleDivider;
 407                     adjw = (int)Math.round((double)iw / scaleDivider);
 408                     adjh = (int)Math.round((double)ih / scaleDivider);
 409                     scaleMeasure = 1.0 - 1.0 / scaleDivider;
 410                 }
 411                 double similarity = ((double)width - (double)adjw) / (double)width +
 412                     ((double)height - (double)adjh) / (double)height + //Large padding is bad
 413                     scaleMeasure; //Large rescale is bad
 414                 if (similarity < bestSimilarity) {
 415                     bestSimilarity = similarity;
 416                     image = im;
 417                 }
 418                 if (similarity == 0) break;
 419             }
 420         }
 421         return image;
 422     }
 423 
 424     @Override public void setIcons(java.util.List icons) {
 425 
 426         int SMALL_ICON_HEIGHT = 32;
 427         int SMALL_ICON_WIDTH = 32;
 428         if (PlatformUtil.isMac()) { //Mac Sized Icons
 429             SMALL_ICON_HEIGHT = 128;
 430             SMALL_ICON_WIDTH = 128;
 431         } else if (PlatformUtil.isWindows()) { //Windows Sized Icons
 432             SMALL_ICON_HEIGHT = 32;
 433             SMALL_ICON_WIDTH = 32;
 434         } else if (PlatformUtil.isLinux()) { //Linux icons
 435             SMALL_ICON_HEIGHT = 128;
 436             SMALL_ICON_WIDTH = 128;
 437         }
 438 
 439         if (icons == null || icons.size() < 1) { //no icons passed in
 440             platformWindow.setIcon(null);
 441             return;
 442         }
 443 
 444         Image image = findBestImage(icons, SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT);
 445         if (image == null) {
 446             //No images were found, possibly all are broken
 447             return;
 448         }
 449 
 450         PushbroomScaler scaler = ScalerFactory.createScaler(image.getWidth(), image.getHeight(),
 451                                                             image.getBytesPerPixelUnit(),
 452                                                             SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT, true);
 453 
 454         //shrink the image and convert the format to INT_ARGB_PRE
 455         ByteBuffer buf = (ByteBuffer) image.getPixelBuffer();
 456         byte bytes[] = new byte[buf.limit()];
 457 
 458         int iheight = image.getHeight();
 459 
 460         //Iterate through each scanline of the image
 461         //and pass it one at a time to the scaling object
 462         for (int z = 0; z < iheight; z++) {
 463             buf.position(z*image.getScanlineStride());
 464             buf.get(bytes, 0, image.getScanlineStride());
 465             scaler.putSourceScanline(bytes, 0);
 466         }
 467 
 468         buf.rewind();
 469 
 470         final Image img = image.iconify(scaler.getDestination(), SMALL_ICON_WIDTH, SMALL_ICON_HEIGHT);
 471         platformWindow.setIcon(PixelUtils.imageToPixels(img));
 472     }
 473 
 474     @Override public void setTitle(String title) {
 475         platformWindow.setTitle(title);
 476     }
 477 
 478     @Override public void setVisible(final boolean visible) {
 479         // Before setting visible to false on the native window, we unblock
 480         // other windows.
 481         if (!visible) {
 482             removeActiveWindow(this);
 483             if (modality == Modality.WINDOW_MODAL) {
 484                 if (owner != null && owner instanceof WindowStage) {
 485                     ((WindowStage) owner).setEnabled(true);
 486                 }
 487             } else if (modality == Modality.APPLICATION_MODAL) {
 488                 windowsSetEnabled(true);
 489             } else {
 490                 // Note: This method is required to workaround a glass issue
 491                 // mentioned in RT-12607
 492                 // If the hiding stage is unfocusable (i.e. it's a PopupStage),
 493                 // then we don't do this to avoid stealing the focus.
 494                 if (!isPopupStage && owner != null && owner instanceof WindowStage) {
 495                     WindowStage ownerStage = (WindowStage)owner;
 496                     ownerStage.requestToFront();
 497                 }
 498             }
 499         }
 500         QuantumToolkit.runWithRenderLock(() -> {
 501             // platformWindow can be null here, if this window is owned,
 502             // and its owner is being closed.
 503             if (platformWindow != null) {
 504                 platformWindow.setVisible(visible);
 505             }
 506             super.setVisible(visible);
 507             return null;
 508         });
 509         // After setting visible to true on the native window, we block
 510         // other windows.
 511         if (visible) {
 512             if (modality == Modality.WINDOW_MODAL) {
 513                 if (owner != null && owner instanceof WindowStage) {
 514                     ((WindowStage) owner).setEnabled(false);
 515                 }
 516             } else if (modality == Modality.APPLICATION_MODAL) {
 517                 windowsSetEnabled(false);
 518             }
 519             if (isAppletStage && null != appletWindow) {
 520                 appletWindow.assertStageOrder();
 521             }
 522         }
 523 
 524         applyFullScreen();
 525     }
 526 
 527     @Override boolean isVisible() {
 528         return platformWindow.isVisible();
 529     }
 530 
 531     @Override public void setOpacity(float opacity) {
 532         platformWindow.setAlpha(opacity);
 533         GlassScene gs = getScene();
 534         if (gs != null) {
 535             gs.entireSceneNeedsRepaint();
 536         }
 537     }
 538 
 539     public boolean needsUpdateWindow() {
 540         return transparent && (Application.GetApplication().shouldUpdateWindow());
 541     }
 542 
 543     @Override public void setIconified(boolean iconified) {
 544         if (platformWindow.isMinimized() == iconified) {
 545             return;
 546         }
 547         platformWindow.minimize(iconified);
 548     }
 549 
 550     @Override public void setMaximized(boolean maximized) {
 551         if (platformWindow.isMaximized() == maximized) {
 552             return;
 553         }
 554         platformWindow.maximize(maximized);
 555     }
 556 
 557     @Override
 558     public void setAlwaysOnTop(boolean alwaysOnTop) {
 559         // The securityDialog flag takes precedence over alwaysOnTop
 560         if (securityDialog) return;
 561 
 562         if (alwaysOnTop) {
 563             if (hasPermission(alwaysOnTopPermission)) {
 564                 platformWindow.setLevel(Level.FLOATING);
 565             }
 566         } else {
 567             platformWindow.setLevel(Level.NORMAL);
 568         }
 569 
 570     }
 571 
 572     @Override public void setResizable(boolean resizable) {
 573         platformWindow.setResizable(resizable);
 574         // note: for child windows this is ignored and we fail silently
 575     }
 576 
 577     // Return true if this stage is trusted for full screen - doesn't have a
 578     // security manager, or a permission check doesn't result in a security
 579     // exeception.
 580     boolean isTrustedFullScreen() {
 581         return hasPermission(fullScreenPermission);
 582     }
 583 
 584     // Safely exit full screen
 585     void exitFullScreen() {
 586         setFullScreen(false);
 587     }
 588 
 589     boolean isApplet() {
 590         return isPrimaryStage && null != appletWindow;
 591     }
 592 
 593     private boolean hasPermission(Permission perm) {
 594         try {
 595             final SecurityManager sm = System.getSecurityManager();
 596             if (sm != null) {
 597                 sm.checkPermission(perm, getAccessControlContext());
 598             }
 599             return true;
 600         } catch (SecurityException se) {
 601             return false;
 602         }
 603     }
 604 
 605     // We may need finer-grained permissions in the future, but for
 606     // now AllPermission is good enough to do the job we need, such
 607     // as fullscreen support for signed/unsigned application.
 608     private static final Permission fullScreenPermission = new AllPermission();
 609     private static final Permission alwaysOnTopPermission = new AllPermission();
 610 
 611     private boolean fullScreenFromUserEvent = false;
 612 
 613     private KeyCombination savedFullScreenExitKey = null;
 614 
 615     public final KeyCombination getSavedFullScreenExitKey() {
 616         return savedFullScreenExitKey;
 617     }
 618 
 619     private void applyFullScreen() {
 620         if (platformWindow == null) {
 621             // applyFullScreen() can be called from setVisible(false), while the
 622             // platformWindow has already been destroyed.
 623             return;
 624         }
 625         View v = platformWindow.getView();
 626         if (isVisible() && v != null && v.isInFullscreen() != isInFullScreen) {
 627             if (isInFullScreen) {
 628                 // Check whether app is full screen trusted or flag is set
 629                 // indicating that the fullscreen request came from an input
 630                 // event handler.
 631                 // If not notify the stageListener to reset fullscreen to false.
 632                 final boolean isTrusted = isTrustedFullScreen();
 633                 if (!isTrusted && !fullScreenFromUserEvent) {
 634                     exitFullScreen();
 635                     fullscreenChanged(false);
 636                 } else {
 637                     v.enterFullscreen(false, false, false);
 638                     if (warning != null && warning.inWarningTransition()) {
 639                         warning.setView(getViewScene());
 640                     } else {
 641                         boolean showWarning = true;
 642 
 643                         KeyCombination key = null;
 644                         String exitMessage = null;
 645 
 646                         if (isTrusted && (fxStage != null)) {
 647                             // copy the user set definitions for later use.
 648                             key = fxStage.getFullScreenExitKeyCombination();
 649 
 650                             exitMessage = fxStage.getFullScreenExitHint();
 651                         }
 652 
 653                         savedFullScreenExitKey =
 654                                 key == null
 655                                 ? defaultFullScreenExitKeycombo
 656                                 : key;
 657 
 658                         if (
 659                             // the hint is ""
 660                             "".equals(exitMessage) ||
 661                             // if the key is NO_MATCH
 662                             (savedFullScreenExitKey.equals(KeyCombination.NO_MATCH))
 663                                 ) {
 664                             showWarning = false;
 665                         }
 666 
 667                         // the hint is not set, use the key for the message
 668                         if (showWarning && exitMessage == null) {
 669                             if (key == null) {
 670                                 exitMessage = RESOURCES.getString("OverlayWarningESC");
 671                             } else {
 672                                 String f = RESOURCES.getString("OverlayWarningKey");
 673                                 exitMessage = f.format(f, savedFullScreenExitKey.toString());
 674                             }
 675                         }
 676 
 677                         if (showWarning && warning == null) {
 678                             setWarning(new OverlayWarning(getViewScene()));
 679                         }
 680 
 681                         if (showWarning && warning != null) {
 682                             warning.warn(exitMessage);
 683                         }
 684                     }
 685                 }
 686             } else {
 687                 if (warning != null) {
 688                     warning.cancel();
 689                     setWarning(null);
 690                 }
 691                 v.exitFullscreen(false);
 692             }
 693             // Reset flag once we are done process fullscreen
 694             fullScreenFromUserEvent = false;
 695         } else if (!isVisible() && warning != null) {
 696             // if the window is closed - re-open with fresh warning
 697             warning.cancel();
 698             setWarning(null);
 699         }
 700     }
 701 
 702     void setWarning(OverlayWarning newWarning) {
 703         this.warning = newWarning;
 704         getViewScene().synchroniseOverlayWarning();
 705     }
 706 
 707     OverlayWarning getWarning() {
 708         return warning;
 709     }
 710 
 711     @Override public void setFullScreen(boolean fullScreen) {
 712         if (isInFullScreen == fullScreen) {
 713             return;
 714         }
 715 
 716        // Set a flag indicating whether this method was called from
 717         // an input event handler.
 718         if (isInEventHandler()) {
 719             fullScreenFromUserEvent = true;
 720         }
 721 
 722         GlassStage fsWindow = activeFSWindow.get();
 723         if (fullScreen && (fsWindow != null)) {
 724             fsWindow.setFullScreen(false);
 725         }
 726         isInFullScreen = fullScreen;
 727         applyFullScreen();
 728         if (fullScreen) {
 729             activeFSWindow.set(this);
 730         }
 731     }
 732 
 733     void fullscreenChanged(final boolean fs) {
 734         if (!fs) {
 735             if (activeFSWindow.compareAndSet(this, null)) {
 736                 isInFullScreen = false;
 737             }
 738         } else {
 739             isInFullScreen = true;
 740             activeFSWindow.set(this);
 741         }
 742         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
 743             if (stageListener != null) {
 744                 stageListener.changedFullscreen(fs);
 745             }
 746             return null;
 747         }, getAccessControlContext());
 748     }
 749 
 750     @Override public void toBack() {
 751         platformWindow.toBack();
 752         if (isAppletStage && null != appletWindow) {
 753             appletWindow.assertStageOrder();
 754         }
 755     }
 756 
 757     @Override public void toFront() {
 758         platformWindow.requestFocus(); // RT-17836
 759         platformWindow.toFront();
 760         if (isAppletStage && null != appletWindow) {
 761             appletWindow.assertStageOrder();
 762         }
 763     }
 764 
 765     @Override public void close() {
 766         super.close();
 767         QuantumToolkit.runWithRenderLock(() -> {
 768             // prevents closing a closed platform window
 769             if (platformWindow != null) {
 770                 platformWindows.remove(platformWindow);
 771                 platformWindow.close();
 772                 platformWindow = null;
 773             }
 774             GlassScene oldScene = getViewScene();
 775             if (oldScene != null) {
 776                 oldScene.updateSceneState();
 777             }
 778             return null;
 779         });
 780     }
 781 
 782     // setPlatformWindowClosed is only set upon receiving platform window has
 783     // closed notification. This state is necessary to prevent the platform
 784     // window from being closed more than once.
 785     void setPlatformWindowClosed() {
 786         platformWindow = null;
 787     }
 788 
 789     static void addActiveWindow(WindowStage window) {
 790         activeWindows.remove(window);
 791         activeWindows.add(window);
 792     }
 793 
 794     static void removeActiveWindow(WindowStage window) {
 795         activeWindows.remove(window);
 796     }
 797 
 798     final void handleFocusDisabled() {
 799         if (activeWindows.isEmpty()) {
 800             return;
 801         }
 802         WindowStage window = activeWindows.get(activeWindows.size() - 1);
 803         window.setIconified(false);
 804         window.requestToFront();
 805         window.requestFocus();
 806     }
 807 
 808     @Override public boolean grabFocus() {
 809         return platformWindow.grabFocus();
 810     }
 811 
 812     @Override public void ungrabFocus() {
 813         platformWindow.ungrabFocus();
 814     }
 815 
 816     @Override public void requestFocus() {
 817         platformWindow.requestFocus();
 818     }
 819 
 820     @Override public void requestFocus(FocusCause cause) {
 821         switch (cause) {
 822             case TRAVERSED_FORWARD:
 823                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_FORWARD);
 824                 break;
 825             case TRAVERSED_BACKWARD:
 826                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED_BACKWARD);
 827                 break;
 828             case ACTIVATED:
 829                 platformWindow.requestFocus(WindowEvent.FOCUS_GAINED);
 830                 break;
 831             case DEACTIVATED:
 832                 platformWindow.requestFocus(WindowEvent.FOCUS_LOST);
 833                 break;
 834         }
 835     }
 836 
 837     @Override
 838     protected void setPlatformEnabled(boolean enabled) {
 839         super.setPlatformEnabled(enabled);
 840         platformWindow.setEnabled(enabled);
 841         if (enabled) {
 842             // Check if window is really enabled - to handle nested case
 843             if (platformWindow.isEnabled()) {
 844                 requestToFront();
 845             }
 846         } else {
 847             removeActiveWindow(this);
 848         }
 849     }
 850 
 851     void setEnabled(boolean enabled) {
 852         if ((owner != null) && (owner instanceof WindowStage)) {
 853             ((WindowStage) owner).setEnabled(enabled);
 854         }
 855         /*
 856          * RT-17588 - exit if stage is closed from under us as
 857          *            any further access to the Glass layer
 858          *            will throw an exception
 859          */
 860         if (enabled && (platformWindow == null || platformWindow.isClosed())) {
 861             return;
 862         }
 863         setPlatformEnabled(enabled);
 864         if (enabled) {
 865             if (isAppletStage && null != appletWindow) {
 866                 appletWindow.assertStageOrder();
 867             }
 868         }
 869     }
 870 
 871     // Note: This method is required to workaround a glass issue mentioned in RT-12607
 872     protected void requestToFront() {
 873         if (platformWindow != null) {
 874             platformWindow.toFront();
 875             platformWindow.requestFocus();
 876         }
 877     }
 878 
 879     public void setInEventHandler(boolean inEventHandler) {
 880         this.inEventHandler = inEventHandler;
 881     }
 882 
 883     public boolean isInEventHandler() {
 884         return inEventHandler;
 885     }
 886 
 887     @Override
 888     public void requestInput(String text, int type, double width, double height,
 889                         double Mxx, double Mxy, double Mxz, double Mxt,
 890                         double Myx, double Myy, double Myz, double Myt,
 891                         double Mzx, double Mzy, double Mzz, double Mzt) {
 892         platformWindow.requestInput(text, type, width, height,
 893                                     Mxx, Mxy, Mxz, Mxt,
 894                                     Myx, Myy, Myz, Myt,
 895                                     Mzx, Mzy, Mzz, Mzt);
 896     }
 897 
 898     @Override
 899     public void releaseInput() {
 900         platformWindow.releaseInput();
 901     }
 902 
 903     @Override public void setRTL(boolean b) {
 904         rtl = b;
 905     }
 906 
 907 }