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